Degree Blogg
7Feb/110

Sharepoint 2010 – Write to Sharepoint Trace Log file

Posted by Andreas

Application trace logging using log4net has been covered previously several times in this blog, but I thought I’d just add another one that has been very helpful for me. Even though the Sharepoint log files (which are written to through the Unified Logging Service ULS, for those who care about details) are very crowded and hard to read, it can be a very useful tool for developers. When deploying even the simplest custom web parts and features to a production environment, some of your logic might not work as well as it did when running on the development box. And without the ability to debug, you will find yourself sitting there in the dark guessing which code line that causes the stuff-up.

So here’s a 2 minute implementation of a method that uses the ULS and writes to the standard Sharepoint 2010 log files (usually located under “Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\LOGS”):

1. Create a simple method in your class

private void Log(string source, TraceSeverity traceSeverity, EventSeverity eventSeverity, string logMessage)
{
    try
    {
        SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory(source, traceSeverity, eventSeverity), traceSeverity, logMessage, null);
    }
    catch (Exception)
    {
        // maybe write to Event Log?
    }
}

2. Call your method with the desired log levels

Log("MyVeryOwnEventReceiver", TraceSeverity.High, EventSeverity.Error, "God damn it, something went totally wrong!?!");

3. Find your log entry in the log file

image

Just a couple of notes:

- Beware of the log level! Your solution might have the minium log level set to higher than “Information” and your entry will not be written if your log level is set too low

- The ULS method SPDiagnosticsService.Local.WriteTrace accepts a object[] as the final parameter. I am just sending null, but this is perfect for for instance the stack trace from an exception!

5Feb/111

Sharepoint 2010 – SPWebTemplate overview

Posted by Andreas

When programmatically creating new sites you define which template to use by referring to its name, ID or with an instance of the actual template class. To list the templates available in your solution, use the following code:

SPWebTemplateCollection coll = site.GetWebTemplates(uint.Parse(System.Threading.Thread.CurrentThread.CurrentUICulture.LCID.ToString()));
foreach (SPWebTemplate tmpl in coll)
{
    ... // knock yourself out

This will loop the template collection (including your own custom ones). To save you a bit of hassle I will list all the standard templates shipped with Sharepoint 2010. When creating a new site, just refer to the template name (for instance “CMSPUBLISHING#0” for a publishing site) like this:

SPWeb myNewSubSite = web.Webs.Add(uniqueUrl, subSiteTitle, "", uint.Parse(System.Threading.Thread.CurrentThread.CurrentUICulture.LCID.ToString()), "CMSPUBLISHING#0", false, false);

Here are the 50 standard templates, with index, name and description. Please note that you’re not guaranteed that your index matches mine…

[0]        "GLOBAL#0 : This template is used for initializing a new site."    string
[1]        "STS#0 : A site for teams to quickly organize, author, and share information. It provides a document library, and lists for managing announcements, calendar items, tasks, and discussions."    string
[2]        "STS#1 : A blank site for you to customize based on your requirements."    string
[3]        "STS#2 : A site for colleagues to work together on a document. It provides a document library for storing the primary document and supporting files, a tasks list for assigning to-do items, and a links list for resources related to the document."    string
[4]        "MPS#0 : A site to plan, organize, and capture the results of a meeting. It provides lists for managing the agenda, meeting attendees, and documents."    string
[5]        "MPS#1 : A blank meeting site for you to customize based on your requirements."    string
[6]        "MPS#2 : A site for meetings that track status or make decisions. It provides lists for creating tasks, storing documents, and recording decisions."    string
[7]        "MPS#3 : A site to plan social occasions. It provides lists for tracking attendees, providing directions, and storing pictures of the event."    string
[8]        "MPS#4 : A site to plan, organize, and capture the results of a meeting. It provides lists for managing the agenda and meeting attendees in addition to two blank pages for you to customize based on your requirements."    string
[9]        "CENTRALADMIN#0 : A site for central administration. It provides Web pages and links for application and operations management."    string
[10]    "WIKI#0 : A site for a community to brainstorm and share ideas. It provides Web pages that can be quickly edited to record information and then linked together through keywords"    string
[11]    "BLOG#0 : A site for a person or team to post ideas, observations, and expertise that site visitors can comment on."    string
[12]    "SGS#0 : This template provides a groupware solution that enables teams to create, organize, and share information quickly and easily. It includes Group Calendar, Circulation, Phone-Call Memo, the Document Library and the other basic lists."    string
[13]    "TENANTADMIN#0 : A site for tenant administration. It provides Web pages and links for self-serve administration."    string
[14]    "ACCSRV#0 : Microsoft Access Server"    string
[15]    "ACCSRV#1 : Create an assets database to keep track of assets, including asset details and owners."    string
[16]    "ACCSRV#3 : Create a database to track information about fundraising campaigns including donations made by contributors, campaign related events, and pending tasks."    string
[17]    "ACCSRV#4 : Create a contacts database to manage information about people that your team works with, such as customers and partners."    string
[18]    "ACCSRV#6 : Create an issues database to manage a set of issues or problems. You can assign, prioritize, and follow the progress of issues from start to finish."    string
[19]    "ACCSRV#5 : Create a project tracking database to track multiple projects, and assign tasks to different people."    string
[20]    "BDR#0 : A site to centrally manage documents in your enterprise."    string
[21]    "OFFILE#0 : (obsolete) This template creates a site designed for records management. Records managers can configure the routing table to direct incoming files to specific locations. The site also lets you manage whether records can be deleted or modified after they are added to the repository."    string
[22]    "OFFILE#1 : This template creates a site designed for records management. Records managers can configure the routing table to direct incoming files to specific locations. The site also lets you manage whether records can be deleted or modified after they are added to the repository."    string
[23]    "OSRV#0 : This template creates a site for administering shared services"    string
[24]    "PPSMASite#0 : A site for presenting PerformancePoint dashboards and scorecards. The site also includes links to PerformancePoint Dashboard Designer and storage for dashboard content such as analytic charts, reports, KPIs, and strategy maps."    string
[25]    "BICenterSite#0 : A site for presenting Business Intelligence Center."    string
[26]    "SPS#0 : This template is obsolete."    string
[27]    "SPSPERS#0 : This web template defines a Personal Space for an individual participating on a SharePoint Portal."    string
[28]    "SPSMSITE#0 : A site for delivering personalized views, data, and navigation from this site collection into My Site. It includes personalization specific Web Parts and navigation that is optimized for My Site sites."    string
[29]    "SPSTOC#0 : This template is obsolete."    string
[30]    "SPSTOPIC#0 : This template is obsolete."    string
[31]    "SPSNEWS#0 : This template is obsolete."    string
[32]    "CMSPUBLISHING#0 : A blank site for expanding your Web site and quickly publishing Web pages. Contributors can work on draft versions of pages and publish them to make them visible to readers. The site includes  document and image libraries for storing Web publishing assets."    string
[33]    "BLANKINTERNET#0 : This template creates a site for publishing Web pages on a schedule, with workflow features enabled.  By default, only Publishing subsites can be created under this site. A Document and Picture Library are included for storing Web publishing assets."    string
[34]    "BLANKINTERNET#1 : This template creates the Press Releases subsite for an Internet-facing corporate presence website."    string
[35]    "BLANKINTERNET#2 : A site for publishing Web pages on a schedule by using approval workflows. It includes document and image libraries for storing Web publishing assets. By default, only sites with this template can be created under this site."    string
[36]    "SPSNHOME#0 : A site for publishing news articles and links to news articles. It includes a sample news page and an archive for storing older news items."    string
[37]    "SPSSITES#0 : A site for listing and categorizing important sites in your organization. It includes different views for categorized sites, top sites, and a site map."    string
[38]    "SPSCOMMU#0 : This template is obsolete."    string
[39]    "SPSREPORTCENTER#0 : A site for creating, managing, and delivering Web pages, dashboards, and key performance indicators that communicate metrics, goals, and business intelligence information."    string
[40]    "SPSPORTAL#0 : A starter site hierarchy for an intranet divisional portal. It includes a home page, a News site, a Site Directory, a Document Center, and a Search Center with Tabs. Typically, this site has nearly as many contributors as  readers and is used to host team sites."    string
[41]    "SRCHCEN#0 : A site for delivering the search experience. The welcome page includes a search box with two tabs: one for general searches, and another for searches for information about people. You can add and customize tabs to focus on other search scopes or result types."    string
[42]    "PROFILES#0 : This template creates a profile site that includes page layout with zones"    string
[43]    "BLANKINTERNETCONTAINER#0 : A starter site hierarchy for an Internet-facing site or a large intranet portal. This site can be customized easily with distinctive branding. It includes a home page, a sample press releases subsite, a Search Center, and a login page. Typically, this site has many more readers than contributors, and it is used to publish Web pages with approval workflows."    string
[44]    "SPSMSITEHOST#0 : A site used for hosting personal sites (My Sites) and the public People Profile page. This template needs to be provisioned only once per User Profile Service Application, please consult the documentation for details."    string
[45]    "ENTERWIKI#0 : A site for publishing knowledge that you capture and want to share across the enterprise. It provides an easy content editing experience in a single location for co-authoring content, discussions, and project management."    string
[46]    "SRCHCENTERLITE#0 : A site for delivering the search experience. The site includes pages for search results and advanced searches."    string
[47]    "SRCHCENTERLITE#1 : The Search Center template creates pages dedicated to search. The main welcome page features a simple search box in the center of the page. The template includes a search results and an advanced search page. This Search Center will not appear in navigation."    string
[48]    "SRCHCENTERFAST#0 : A site for delivering the FAST search experience. The welcome page includes a search box with two tabs: one for general searches, and another for searches for information about people. You can add and customize tabs to focus on other search scopes or result types."    string
[49]    "visprus#0 : A site for teams to quickly view, share, and store Visio process diagrams. It provides a versioned document library for storing process diagrams, and lists for managing announcements, tasks, and review discussions."    string

 

 

21Jan/110

Sharepoint 2010 – Term Store Management missing

Posted by Andreas

Can’t find the Term Store Management item under Site Settings?

image

The feature isn’t enabled, just use Powershell and run this command:
Enable-SPFeature -id “73EF14B1-13A9-416b-A9B5-ECECA2B0604C” -Url <Site-URL>

.. or use STSAdm and run this command:
stsadm -o activatefeature -id 73EF14B1-13A9-416b-A9B5-ECECA2B0604C -url http://[your_site_url] –force

11Jan/110

Sharepoint 2010 – programmatically import and use web parts from site gallery

Posted by Andreas

I recently discovered that when I export webparts with custom settings (like a CQWP with styling, filter values etc) and stored them in the site collection gallery, the configuration values were lost when programmatically importing them in a Visual Studio 2010 solution. The .webpart file created upon export contains all the custom settings, but this was ignored when using the standard add method ( SPLimitedWebPartManager.AddWebPart(…) ). All I got was a standard clean out-of-the box instance of the CQWP, which meant manual configuration was required.

To overcome this limitation there is another available method in the SPLimitedWebPartManager class: SPLimitedWebPartManager.ImportWebPart(…). This method takes an XmlReader parameter, which should get its data feed from the .webpart file stored in the gallery. The trick is to find the path to this file, but when you’ve got that it is simple to import it to your current page and then add it a web part zone. Here’s a snippet to help you get started:

    using (SPSite site = new SPSite(SPContext.Current.Site.Url))
    {
        SPList list = site.RootWeb.Lists["Web Part Gallery"];

        for (int i = 0; i < list.ItemCount; i++)
        {
            if (list.Items[i].Title.Contains("The title of my custom webpart"))
            {

... // you can figure out the rest

The following code is in the context of a newly created sub site, and I access the SPLimitedWebPartManager for a publishing page like this:

using (SPSite site = new SPSite(SPContext.Current.Site.Url))
    {
        // get reference to newly created sub site
        using (SPWeb web = site.AllWebs[_NewSiteGuid])
        {

            PublishingWeb pWeb = PublishingWeb.GetPublishingWeb(web);

            PublishingPage page = pWeb.GetPublishingPage(_NewSiteUri);
            // check out page before making changes
            page.CheckOut();

            // access webparts
            SPLimitedWebPartManager wpMgr = web.GetLimitedWebPartManager(page.Uri.ToString(), PersonalizationScope.Shared);

...

A bit further down the actual import and adding occurs:

...

    string error = ""; // holds the potential error message
    // bit of ugly coding here, Robert C. Martin will shoot you if you use this!
    string exportedWebPartXml = new StringReader(site.RootWeb.GetFileAsString(site.RootWeb.Url + "/_catalogs/wp/SomeCustomFancyComponent.webpart")).ReadToEnd();
    // alter contents
    exportedWebPartXml = exportedWebPartXml.Replace("SomeValueThatShouldBeGeneric", "ThisOtherValueThatIsPageDependant");

    // create a reader
    XmlTextReader reader = new XmlTextReader(new StringReader(exportedWebPartXml));

    // wpMgr is an instance of SPLimitedWebPartManager
    System.Web.UI.WebControls.WebParts.WebPart importedWp = wpMgr.ImportWebPart(reader, out error);

    // make some other alterations if applicable
    importedWp.Title = "New title";

    // add the web part to the publishing page
    wpMgr.AddWebPart(importedWp, "TopBarLeftZone", 0);

...

Note: I have removed validation for increased readability, but before someone gets on my back for possible null pointer exception traps I just wanted to say it was deliberate!

If you expect to get anything out of this post, I assume you already know enough basic coding to see what’s going on. Basically, using the path pointing to the .webpart file the contents are read into the XmlTextReader and passed on to the ImportWebPart() method. The return parameter is an instance of your webpart (if you got the path correct…) which can then be added to a web part zone on your page.

Your custom settings are now preserved, and as you see in this code I also do some runtime alterations of the .webpart file contents which practically removes any boundries to what you can do.

9Dec/100

Sharepoint trick – when a webpart causes a Server Error

Posted by Andreas

image

This is a useful trick when one of your custom webparts causes the page to fail and gives you the Yellow Screen of Death. By just adding a query string parameter you’ll get access to the “maintenance page”, where you can delete or reset the faulty webpart.

Add “?contents=1” behind the url (giving you something similar to this: http://server/sites/DemoNet/SitePages/WebParts.aspx?contents=1 )

image

Now go back to Visual Studio, quietly admit to yourself that your error handling sucks and make sure you fix it!

20Oct/100

Sharepoint 2010 Development – SPWeb disposal error

Posted by Andreas

A common error for any Sharepoint 2010 developer is the following:

“Trying to use an SPWeb object that has been closed or disposed and is no longer valid”

image

You’ve obviously disposed of an SPWeb instance somewhere in your code which in turn eliminates further execution on this object. But why does this happen?

When retrieving the SPWeb object (which is a site under a site collection) there are multiple possibilities:

SPSite siteColl = new SPSite(“http://yourserver“);
SPWeb site = siteColl.OpenWeb();

or, more likely if you see this error:

SPWeb site = SPContext.Current.Web;

The difference between these two is that the latter actually doesn’t open a site, you’re getting it from the current context which is used by both the page and possibly other controls. Not a problem – until you dispose of it. Assuming you are following other “best practices” you’re running your code within a using(SPWeb site = SPContext.Current.Web) block, which automatically disposes of the object afterwards. This is where your error message appears.

So, in other words: create a new site collection instance by using the new SPSite(“http://yourserver”) rather than just fetching it from the current context.

Update: or if you insist on not knowing your own url: new SPSite(SPContext.Current.Site.Url)

image

31Aug/100

Sharepoint 2010 Custom MasterPage og CSS

Posted by Peder

I forbindelse med et prosjekt fikk jeg i oppdrag å sette opp en Sharepoint site til å bruke egen masterpage og css. Her er en gjennomgang av hvordan jeg gikk frem for å løse dette.

Begynn med å gå inn å Sharepoint Designer og klikk på Master Pages. Kopier v4.master og lim inn i samme område. Gi den nye kopien av v4.master et passende navn.

Gå deretter til All files => Style Library. Høyre klikk og velg New=>css og gi css filen et passende navn.

For å få muligheten for å velge hvilken master page siten skal peke mot må man aktivere noen features. Gå inn i site settings og Site collection features under Site Collection Administration. Aktiver Sharepoint Server Publishing Infrastructure. Deretter går man inn på Manage site features under Site Actions og aktiverer Sharepoint Server Publishing.

Nå har noen nye valg dukket opp i site settings. Klikk på Master page under Look and Feel. Endre Site Master Page og System Master Page til å peke mot den master pagen som ble opprettet i Sharepoint Designer og klikk OK. Siten bruker nå den nye masterpagen.

Gå tilbake til Sharepoint Designer og gå inn på Master Pages. Klikk på den nye masterpagen og velg deretter edit file. Skriv inn følgende kodelinje inn i header tagen og pass på å legge den under PlaceHolderAdditionalPageHead => Sharepoint:DelegateControll tagen.

Hvis følgende filemelding dukker i Sharepoint Designer under edit file prosessen:
soap:Server was unable to process request. ---> Value does not fall within the expected range, er det mulig at feilen ligger i configuring alternate access mapping. Gå til Sharepoint Central Administration => Application Management => Configure alternate access mappings. Her er det viktig at sharepoint siten peker til riktig path. Istedenfor f.eks localhost så skriv in path til siten.

PS: Husk å check inn og checke ut MasterPagen og CSS filen som brukes.

26Aug/100

Log4Net i Sharepoint 2010 web parts

Posted by Andreas

All kode (av noenlunde kvalitet...) som kjører i et produksjonsmiljø logger. Eller burde gjøre det. Om det er til fil, database, eventlogg eller remote over eteren har ikke noe å si - et sted bør det holdes varig track på hva som skjer i en applikasjon. Da jeg hev meg på utvikling i Sharepoint var en av de første "utfordringene" hvordan jeg skulle få skrevet til egne loggfiler i stedet for å bruke de interne Sharepointloggene. Hvorfor? Vel.. det skjer mye Sharepoint. Mye som du sannsynligvis ikke er interessert i, og som ikke nødvendigvis er relevant for den feilen du leter etter grunnen til.

Log4Net er en .NET-portering av Log4J. Den gjør det enkelt for en applikasjon å logge til fil. Det er omtrent det jeg tar med av bakgrunn, siden den som leser dette innlegget sannsynligvis har brukt det før og bare vil se hvordan det brukes i Sharepoint. Jeg tar heller ikke med mulighetene for å bruke Log4Net til å sende mail via SMTP, men den er verdt å få med seg at eksisterer.

Sharepoint er bygd på ASP.NET, og du kan derfor bruke samme fremgangsmåte som du hadde gjort i en hvilken som helst annen ASP.NET-applikasjon. Det som utgjør forskjellen er at en web part ikke nødvendigvis har en web.config-fil. I dette eksemplet bruker jeg den som ligger i roten på selve Sharepoint-instansen (På site collection-nivå). Det er ikke noe i veien for å bruke en mer lokal web.config, men det utgjør ikke noe vesentlig forskjell i demosammenheng.

1. Legg til en referanse til log4net.dll
Denne må enten registreres i GAC manuelt, eller så kan du legge den til Package-elementet i web part-prosjektet. Sjekk dette innlegget for en gjennomgang av det.

2. Legg inn den vanlige ConfigSection og konfigurasjon i web.config
I denne demoen bruker jeg web.config for en hel site collection (ligger gjerne her C:\inetpub\wwwroot\wss\VirtualDirectories\80\web.config).

Øverst i web.config legger jeg log4net-noden som første element under "configSections":




....

Deretter legger jeg appender-config som et element rett før "system.web":






















.......

3. Legg til en entry i AssemblyInfo.cs
Her settes ConfiguratorAttribute til å peke på web.config som konfigurasjonsfil:


[assembly: log4net.Config.XmlConfiguratorAttribute(ConfigFile = "web.config", Watch = true)]

4. Legg til en statisk instans av loggeren på klassenivå slik som før (denne henter generisk ut klassens navn i stedet for å selv måtte sette dette for hver klasse):


private static readonly log4net.ILog _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

5. Logging burde nå (med Guds velvilje og litt triksing med skriverettighetene i målmappen) fungere:


_log.Info("Degree Blogg sparte meg akkurat for masse jobb!");
_log.Warn(" (and I'm a poet and didn't know it) ");

26Aug/100

Deployment av 3-parts assemblies i Sharepoint 2010

Posted by Andreas

Om du har merket at de assembliene du legger inn referanser til i et Sharepoint-prosjekt i Visual Studio 2010 ikke blir med under deployment får du fremgangsmåten her.

Sharepoint baserer seg på at disse ligger i GAC (Global Assembly Cache), men de sendes ikke automatisk dit selv om du legger dem til i prosjektet (for eksempel for en webpart). Dette må du konfigurere i Package-elementet i prosjektet.

1. Lokaliser Package-mappen i prosjektet som deployes til Sharepoint. Åpne den og dobbelklikk på Package.package. Du får da opp en oversikt over hvilke deler packen som deployes inneholder (Features, project items etc).

2. Nede til venstre ser du tre elementer: Design, Advanced og Manifest. Trykk Advanced, og du får opp en liste hvor du kan legge til "Additional assemblies". Her er det bare å trykke Add og navigere til de .dll-filene du vil at Visual Studio skal registrere i GAC ved Deploy.

Merk at det noen ganger feiler når du kjører Build / Deploy, og at dette skyldes en av assembliene som skal registreres i GAC. Det oppstår når Visual Studio skal fjerne den eksisterende .dll-filen fra GAC og den brukes av en annen prosess og dermed er låst. Løsningen er stort sett bare å kjøre Build / Deploy igjen..