| Brad 的个人资料Brad Younge's Blog日志列表 | 帮助 |
|
|
2007/6/8 Localize a Date in SharePoint 2007 or WSS 3.0One great thing about SharePoint 2007 is the ease of localizing information. Each user of a SharePoint site can change settings such as their Locale, Time zone, Calendar, etc. by selecting the "My Settings" choice that drops down when the user clicks on the Welcome link in the upper right corner of a site and then proceeds to their Regional Settings. Like this:
If the user selects a different Locale such as "English (New Zealand)", then they will see dates formatted the way they are use to. For example, May 30th, 2007 is displayed as "30/04/2007". This works great with all of the OOTB SharePoint pages and web parts (as far as I know), but if you develop your own page or web part, you'll need to write a little localization code to have the date display correctly. Here's some example code to convert the date in the example above appropriately for each user of the site:
If you also want to adjust the time to the person's Time Zone, you'll need to write a little bit more code, but I'll blog about that some other day. If Having Problems with Visual Studio Extensions for WSS 3.0I just posted a blog about how I re-organized a bunch of code into multiple projects to get past some deployment issues I was having with Visual Studio Extensions for WSS 3.0, but have since found what *may* have been one of my problems over the past couple of weeks (though I'm sure it wasn't my only problem). Turns out that if you have a comment in the <Lists> section of your onet.xml file, sometimes (not always – though – very wired) Visual Studio will throw an exception stating that an "object reference not set to an instance of an object". For example, if your onet.xml file looks like this, you *may* get the object reference not set to an instance of an object error:
If you remove the highlighted comment, it works! Adding comments to other areas of your onet.xml file may also cause issues, but I haven't tested all the different areas to see where it is problematic. If anyone else comes across any other areas, I'd love to get a comment from you. Tip if Having Problems with Visual Studio Extensions for Windows SharePoint Services 3.0Over the past several weeks, I have been having some serious headaches using the Visual Studio Extensions for WSS 3.0 and it came to a climax today. For a project I'm working on, we have the need to create several content types, several Custom Lists based on these content types, and then a Site Definition that makes use of these content types and lists. I've put a good amount of time into creating a Visual Studio project with several "Project Items" based on the "Visual C# Project Items – SharePoint" templates that get installed with the mentioned extensions, image shown here:
In summary, I performed the following steps to get where I'm at now:
One of the great things about the VS Extensions for WSS is that as I wrote the content types, lists and site definition, I could easily deploy everything to my local SharePoint site collection by right-clicking on the VS Project and selecting deploy. Things went pretty well for a couple of weeks, but then I started getting the following error when I tried to deploy: "object reference not set to an instance of an object". The error was NOT an error with my code (I know - bold statement), but instead was an exception being thrown by Visual Studio. You see, when you choose to deploy to project, VS actually has to do a good amount of work to re-organize all of the files you've created, create a manifest, create the actual .wsp file (SharePoint Solution File) and then a batch script (setup.bat) which is executed after all is done to actually deploy your SharePoint *stuff* to the server. The exception was occurring during the step within Visual Studio that creates the .wsp and setup.bat files. For a few weeks I was able to get past this problem by shutting down and then re-launching Visual Studio and then try deploying again and whola… it would work. Lately, however, the problem started occurring more and more often and has gotten harder and harder to work around. Yesterday, it got so bad that my workaround became:
Then, if I made even the smallest, most insignificant change to any of the files in this project, the Deploy would fail and I'd have to go through the above steps again. Well… today it got even worse. Now, I can't get it to deploy at all!!! Even if I roll back all my changes to a version that was deploying fine this morning, it still doesn't work… Ugh. So, over the past 2 hours I've been trying to narrow down the problem by excluding directories in the project (which I had tried before), and narrowed it down to the Site Definition folder. If it's included in the project, the Deploy fails. If it's NOT included in the project, the Deploy works. ----------------------------------------------------------------------------------------------- OK… I'm back, and I've got a "kind-of-ok" solution, so here it is:
In the code above, there are two things to note:
Now that I have my code re-organized in Visual Studio as stated above, deployment is very smooth. However, it has only been one day since I re-organized as stated above, so only time will tell if this is more stable than my original organization. If this turns out to be troublesome after a few days or weeks, I'll be sure to blog about what I find then. 2007/6/4 Profile Linked Content Query in MOSS 2007Earlier this year, I had the opportunity to create a MOSS 2007 web part for a client which integrates information from the current user's profile with a MOSS Search Query in order to find information most relevant to the current user (similar to the Personalized Content Objects in CMS). For example, the client has offices all over the world, and one of the things they wanted to do was have a "Most recent content additions" page which would show the most recent information added to the portal, filtered by the region that the information was targeted to (e.g. North America, Europe, Asia, Middle East, Africa, etc.). I accomplished this through the implementation of a web part that could be placed on any page and configured by the content manager for the site. The web part turned out pretty well, and the other day a couple guys I've worked with over the years from MCS, Henry Winkler and Matt Fangman, asked if I'd be up for blogging about it, so that brings me here. The ProblemAt first glance, we thought we'd use audiences, which seems to be the obvious choice, to target the content as described above, but when we dove a bit deeper into the customer's requirements, it was apparent that as soon as we started adding additional filter criteria such as current user job role and primary technology of interest, that the number of audiences which would be needed increased exponentially, making correct categorization of content extremely difficult (because when you post content, you need to choose each and every audience that the content should be targeted to). Let me give you an example: Let's say that we wanted to create a page which would be called "Recent Postings in your Region related to your Job". This page would display content with the following criteria:
With just the example choices given above, if we wanted to use audiences to target this information, we would need to create 48 audiences (3*4*4) to cover all of the possible combinations. If we added just one more region, such as Africa, then the number or audiences would increase to 64 (4*4*4). In case this isn't entirely obvious, here are the first few audiences we would need:
Taking this one step further, if we had 12 regions, 8 job roles and 15 technologies, we'd need 1440 audiences, not going to happen. The SolutionSo, we needed to come up with a different solution. Without much discussion, it was obvious that the best thing to do was going to be looking up information from current user's profile and combining this with a MOSS SQL Search query to find the relevant information. Luckily for us, there was a parallel Microsoft Identity Integration Server project (see http://www.microsoft.com/miis) being implemented which would provide all of the profile information we were going to need, so we didn't have to worry about populating this information within the scope of our project. Now that we had a good approach, it was merely a matter of implementing the web part and providing the content manager a way to "match up" content metadata with the user's profile attributes. This was achieved relatively easily with the use of the MOSS Enterprise Search SQL Syntax (see http://msdn2.microsoft.com/en-us/library/ms493660.aspx) query capabilities and a little bit of code to structure the query appropriately before sending it off to the search server. So, for the example above, we can get the region, job role and primary technology out of the user's profile (e.g. Asia, Engineer & MOSS), inject the values into the search query, execute the search and then display the results. The resulting web part turned out very well and has been successfully deployed to a number of sites and site collections. Since initial deployment, the web part has been extended so that the content manager can further filter on things such as content type, search scope or keywords. The ResultIf you plan to read further through this blog, you'll probably want to download the Visual Studio Solution I've made available so you can follow my code examples (there's too much code to blog about everything, so I'm just going to focus on the things pertinent to this post). To download a Zip file of the Visual Studio 2005 Solution, go to my Office Live Site Download Page and click on the QueryForIt.img link (2 IMPORTANT NOTES: 1. I had to rename the file to have a *.img extension in order to get it to work, so after you download the QueryForIt.img file, rename it to QueryForIt.zip before opening. 2. When you build the solution, you must switch your build configuration to RELEASE or the post-build commands will fail – or you can go edit the DDF file). The code is spread across several files, but this is primarily due to the fact that I used some existing custom toolparts plus one just for this web part as well as a couple of helper classes to do some dirty work. Here's a breakdown of the code:
The Custom ToolpartsIn order to make configuration of the web part as easy as possible, I create a few custom toolparts to be used when modifying the web part. These custom toolparts' functionality include:
Here are some screenshots of what the toolparts look like:
The third image above titled "Filter & Customize Output" is where the user can input a keyword, select the columns to display, and finally enter an "Advanced Query". The "Advanced Query" textbox is where the content manager would write the code which will be injected into the SQL Syntax Search Query. The syntax for this "Advanced Query" textbox is very specific and is explained in the comment below the textbox. (I know… this isn't very user friendly, but I wasn't expecting the typical content administrator to be modifying this web part. If you were expecting this, then this custom toolpart could be extended to be much more user friendly). Integrating User Profile Information with the Enterprise SQL SearchOnce the toolparts were created, the next step was to implement the actual web part which would integrate the current user's profile information with a basic SQL Query. I started with a base web part with properties to hold the information provided by the toolparts above and then wrote the code to create and then execute the query. Here are a few code snippets of how the query is created and executed (if you want to make fun of my variable naming techniques, go ahead… habits are hard to break):
The first box of code above contains the code used to actually execute the Enterprise Search using the SQL syntax. The second box of code above contains the code which is used to generate the part of the SQL where clause containing information from the user's profile. And the third box of code shows the few lines it takes to actually retrieve the info from the current user's profile. Formatting of Search ResultsIf you've gotten this far through this post, then you're here for the long haul, so I should point out that the results returned from the SQL Search are not quite ready for being displayed. When result content contains either site columns of type "Option" or "Lookup", the results returned from search are not always textual, but sometimes are integers (representing the ID of the actual choice that was made). Furthermore, if the possible values are Multi-select, then the results returned from the Search Query are delimitated with ';#'. In order to convert these results to more friendly strings, I had to write the FixLookupAndMultiChoiceColumns() method which exists in the Util.cs file (not shown in this post). If you look at this method, you'll notice that I look through all of the results returned from the search and correct the columns that need correcting. Something to note: when I first wrote this method I was a little concerned with performance, but once I got to testing it, it appeared to be just fine. Binding the Search Results to an SPGridViewNow that I have the results, I wanted to display them in a grid which had both sorting and paging functionality. I've written a lot of .NET code over the years and this use to be kind of a pain in the butt task that we all had to do. Luckily, though, with the implementation of the GridView class in .NET 2.0, and the extension of this component by WSS 3.0 to the SPGridView class, I'm able to do this without writing any paging or sorting code. However, this isn't so straight forward when you have to write all of the code within a web part and don't have use of code-in-front, but I'm not going to get into it in detail here, but you can read my posting titled My Memberships Web Part for WSS 3.0 to get a bit more information about how I accomplished this with the use of an the SPGridView and an ObjectDataSource. Deploying the Web Part with a SharePoint SolutionIn addition to writing the code for the web part, I also setup the Visual Studio solution so that a SharePoint solution (.wsp) file would be created for easy deployment to SharePoint whenever you run a build. This was accomplished by doing the following:
Now, every time I run a build, the Statera.Moss.Webparts.QueryForIt.wsp file gets created. To add and deploy this solution to a SharePoint 2007 web, you need to follow these steps:
Bonus: From Date & To Date Connected Web PartsIn addition to the functionality already mentioned, I also added the ability to connect up to two Date Filter web parts. You would do this if you wanted to limit the results to say, postings or modifications over the past 7 days. To do this, you would add two date filter web parts to the page, set their default values accordingly, and then connect them to the "FromDate" and "ToDate" properties on this web part. ConclusionWell… that pretty much covers it. Here's a screenshot of the final web part in use with the results sorted by the created date descending. Note that since I'm using the SPGridView, I didn't have to write any sort of formatting code within my web part, it all gets taken care of so the grid matches other grids within SharePoint (and if the CSS files are customized, those customizations will be applied to this web part as well).
If you download the code and make any cool additions to it, I'd love to get a copy of what you did. Just send me an email at: Check out Wade Wegner's blog for great information on Commerce Server 2007. 2007/6/2 My Memberships Web Part for WSS 3.0 (SharePoint 2007)The other day I wrote a quick web part for our corporate intranet which shows a list of all sites that the current user is a member of. The part is a bit slow, because it traverses the site hierarchy and checks to see if the current user is a member of the "Members" group of each site, but it works (for the most part). I just came across a blog posting titled "Increased performance for MOSS apps using the PortalSiteMapProvider" at http://blogs.msdn.com/ecm/archive/2007/05/23/increased-performance-for-moss-apps-using-the-portalsitemapprovider.aspx that I'll probably try to use to improve the performance of this web part at some point, but don't have time right now. I'm doing this blog post in order to share a few techniques I used to create the web part, and also give a link to a zip file of the web part Visual Studio project I created (yep… I'm finally giving back to the SharePoint community after taking-taking-taking). I've zipped and uploaded the Visual Studio solution to my Office Live Site site download page, and the file is named Statera.Moss.Webparts.MyMemberships.img (NOTE: after you download the file, you need to change the extension to .ZIP and then you will be able to unzip it). There's not very much code to this project at all, but there are a few things of interest above and beyond the purpose of the web part, so I'll walk through a few things here. Overview
Making good use of the SPGridViewThe SPGridView is a great SharePoint class which inherits from the .NET 2.0 GridView class and adds in all of the necessary css classes and other style stuff so that the grid looks like the other grids in SharePoint. I found a good posting about using the SPGridView at http://blogs.msdn.com/powlo/archive/2007/02/25/displaying-custom-data-through-sharepoint-lists-using-spgridview-and-spmenufield.aspx. The great thing about using the SPGridView (or even just the .NET 2.0 GridView class) is that you don't have to write any paging or sorting code if you get it all hooked up correctly. Hooking the SPGridView up correctly within a web part is a bit tricky since you don't have use of the designer in Visual Studio. If you look at the code you'll notice a couple of things. First, the MyMembershipHelper class is extremely small… so small that I'll post its entire contents here: using System; namespace Statera.Moss.Webparts public MyMembershipsHelper() public MyMembershipsHelper(DataTable myData) public DataView GetQueryResults()
All this class does is provide a place to store a DataTable as well as a method that the SPGridView can call when it needs the data (GetQueryResults). Now if you look at the MyMemberships cless, you'll see that during the initialization of the Web Part that an ObjectDataSource object is created and added to the web part's controls: //----------------------------- By doing this, you can then set the DataSourceID of the SPGridView object to this new ObjectDataSource and the SPGridView will know where to get its data: _grdResults = new SPGridView(); The only other thing you need to do is create the _myObjectDataSource_ObjectCreating method so that you can set the object instance of the ObjectDataSource to the instance of the myMembershipsQueryHelper class that you'll later create and populate: public void _myObjectDataSource_ObjectCreating(object sender, ObjectDataSourceEventArgs e) Now that we have the SPGridView and ObjectDataSource objects all linked up, we just need to create an instance of the myMemberships class populate it with the sites that the current user is a member of.
Determining Site MembershipThe only other interesting methods are the code is the GetMyMembershipsHelperClass() and the AddSite() methods. The GetMyMembershipsHelperClass() method creates the DataTable in order to store the membership information and then calls the AddSite() method. The AddSite() method is a recursive method (calls itself) in order to traverse the site hierarchy. Probably the most important line of code in the AddSite() method is: string sIdOfMemberGroup = mySpWeb.Properties["vti_associatemembergroup"]; This line of code gets the ID of the "Members Group" so that we can later determine if the current user is a member of this group. The ID of the "Members Group" is stored in the Properties bag of the SPWeb when the site is created and a Members Group is created. NOTE: you can set the value of this property programmatically if you ever have the need to change it or set it to be a different group than the one that SharePoint sets up for you. Now that we have the Group ID of the "Members Group" for the site, we can determine if the current user is in this group with the following code: SPGroup myMemberGroup = mySpWeb.Groups.GetByID(iGroupId); I had to put this code inside a try/catch block in order to catch the Access Denied exceptions that occur if the current user doesn't have enough permission to enumerate the Members Group. This is one place where performance could be drastically improved.
Deploying the SharePoint SolutionSo, in addition to writing the code for the web part, I also setup the Visual Studio solution so that a SharePoint solution (.wsp) file would be created for easy deployment to SharePoint whenever you run a build. This was accomplished by doing the following:
Now, every time I run a build the Statêra.Moss.Webparts.MyMemberships.wsp file gets created. To add and deploy this solution to a WSS 3.0 or SharePoint 2007 web, you just copy the wsp file to the SharePoint server and then execute the following STSADM commands from the command line: stsadm –o addsolution –filename Statera.Moss.Webparts.MyMemberships.wsp NOTE: for some reason, if you copy and paste the above two lines into a command prompt, it won't work. You need to type them out by hand. Well… that's all I've got to say at this time. Feel free to let me know if you have any problems… Other key words or phrases: Membership Web Part MOSS 2007 WSS 3.0 Windows SharePoint Services 3.0 2006/8/23 Problems with SharePoint 2007 Beta 2 Configuration WizardI just spent a good part of my day trying to get the SharePoint 2007 Beta2 Configuration Wizard to work and finally got past a very frustrating problem. I continued to receive "Exception: System.InvalidOperationException: This access control list is not in canonical form and therefore cannot be modified" even after reading several blog entries and following the suggestions. The most thorough entry I found was on Nerd Notes.net at http://www.nerdnotes.net/blog/PermaLink.aspx?guid=93c5d391-39aa-4905-be2e-c12233a9d929, but even after following the suggestions there, I still continued to get the error.
After looking through the log file that the configuration wizard produces I found that the last registry key that was attmpted to be accessed was at HKLM/SOFTWARE/Microsoft/Office Server. I browsed to this key using RegEdit.exe, right clicked on it and selected Properties. Just like in the Nerd Notes blow, I received a dialog stating that the Key's permissions were corrupt. After clicking OK and then closing out of RegEdit, I re-ran the Configuration Wizard and it completed successfully!
I you are having similar problems but can't find the exact resolution, my recommendation would be to search through the log file that the configuration wizard produces for the last reference to a registry key. Then use RegEdit to pull up the permissions on the key, and if you're lucky, you'll get an error message stating that the permissions are corrupt and you'll be able to click OK to resolve them.
2006/6/17 MOSS 2007: SharePoint Feature ManagerTodd Baginski has developed a very useful tool for management of Features in SharePoint Feature Manager. Read more and download at http://www.sharepointblogs.com/tbaginski/archive/2006/06/16/8423.aspx |
|
|