SharePoint and deployment of global.asax code - sharepoint

I want to start logging some custom messages into the ULS from my custom SharePoint code. My code is running inside list item receivers attached to some lists. I'd like to configure this logging mechanism within the application start event handler in global.asax. What's the best-practices way to deploy a SharePoint solution package that modifies global.asax?

I don't know about "best practice", but I would be quite keen on making the edits via code in the feature reciever.
With a line that backs up the file for later restoration.
For logging, we have used Scott hilliers code here to create a trace provider to log with.
Works a treat.
I should clarify that we use a static readonly wrapper for the trace provider
static readonly Log instance = new Log();
that registers itself with the code
SPFarm farm = SPFarm.Local;
Guid traceGuid = farm.TraceSessionGuid;
unit result = NativeMethods.RegisterTraceGuids(ControlCallback, null, ref traceGuid, 0, IntPrt.Zero, null, null, out hTraceReg);
System.Diagnostics.Debug.Assert(result==NativeMethods.ERROR_SUCCESS, "TraceRegister result = " + result.ToString());
Gah!
This then gets instanciated only once and we use the destructor to unregister it.

Related

My custom Windows Service is not writing to my custom Event Log

I have written a custom Windows Service that writes data to a custom Event Log (in the Windows Event Viewer).
For dev'ing the biz logic that the service uses, I created a Windows Form which simulates the Start/Stop methods of the Windows Service.
When executing the biz logic via the Windows Forms, info is successfully written to my custom Event Log. However, when I run the same biz logic from the custom Windows Service, information is failing to be written to the Event Log.
To be clear, I have written a library (.dll) that does all the work that I want my custom service to do - including the create/write to the custom Event Log. My Form application references this library as does my Windows Service.
Thinking the problem is a security issue, I manually set the custom Windows Service to "Log on" as "Administrator", but the service still did not write to the Event Log.
I'm stuck on how to even troubleshoot this problem since I can't debug and step into the code when I run the service (if there is a way to debug a service, please share).
Do you have any ideas as to what could be causing my service to fail to write to the event log?
I use it like this. There can be some typos. Writed it on my phone browser...
public class MyClass
{
private EventLog eventLog = new EventLog();
public void MyClass()
{
if (!System.Diagnostics.EventLog.SourceExists("MyLogSource"))
System.Diagnostics.EventLog.CreateEventSource("MyLogSource", "MyLogSource_Log");
eventLog.Source = "MyLogSource";
eventLog.Log = "MyLogSource_Log";
}
private void MyLogWrite()
{
eventLog.WriteEntry(ex.ToString(), EventLogEntryType.Error);
}
}
To debug a running service you need to attach to the process. See here for the steps.
You could also add parameter checking to the Main entry point and have a combination service and console app which would start based on some flag. See this SO post for a good example but here's a snippet:
using System;
using System.ServiceProcess;
namespace WindowsService1
{
static class Program
{
static void Main(string[] args)
{
if (args == null)
{
Console.WriteLine("Starting service...");
ServiceBase.Run(new ServiceBase[] { new Service1() });
}
else
{
Console.WriteLine("Hi, not from service: " + args[0]);
}
}
}
}
The above starts the app in console mode if there any parameters exist and in service mode if there are no parameters. Of course it can be much fancier but that's the gist of the switch.
I discovered why my service wasn't writing to the Event Log.
The problem had nothing to do with any part of the code/security/etc that was attempting to write to the EL. The problem was that my service wasn't successfully collecting the information that is written to the EL - therefore, the service wasn't even attempting to write the log.
Now that I fixed the code that collects the data, data is successfully writing to the event log.
I'm open to having this question closed since the question was amiss to the real problem.

Execute SPDatasource query in Console App?

Is it possible to execute an SPDataSource object query in a console app for testing?
e.g.:
SPDataSource source = new SPDataSource
{
UseInternalName = true,
DataSourceMode = SPDataSourceMode.List,
SelectCommand = "<View/>"
};
source.SelectParameters.Add("WebId", TypeCode.String, "rootweb");
source.SelectParameters.Add("ListName", TypeCode.String, "Contacts");
var c = source.GetView();
var d = c.Select();
I think the context info is missing but can't figure out how to add it?
I just looked at it in Refelector and it ends up creating a class called SPDataSourceView which depends on SPContext.
I have never been able to creat an SPContext from a console application because of constructors marked as internal.
One option would be to put your class into a Web Service that is deployed to your SharePoint Farm. Then have your console application call this Web Service. However you might be better off using one of the Out of Box SharePoint Web Services.
I´m not sure what you´re after here, I mean
Testing your SPDataSource in console app (nothing to do like said by JD)
Getting data from sharepoint in a datasource manner.
If your´re going for solution 2 you could use a linqdatasource instead of the spdatasource.
See my post on this if that´s what your´re looking for.

sharepoint logging/trace

how can i perform logging in sharepoint. i want to use tracing.
so that it logs in the 12 hive logs.
Microsoft provide an example:
http://msdn.microsoft.com/en-us/library/aa979522.aspx
This sample writes to the ULS log using the native trace methods, so a bit of pInvoke is used in the code.
You can then control the type of logging event in your code such as:
TraceProvider.WriteTrace(0, TraceProvider.TraceSeverity.High, Guid.Empty, "MyExeName", "Product Name", "Category Name", "Sample Message");
The event throttling settings in central admin will still be honored with this approach.
My preferred approach is to write a custom HttpModule to trap and log all errors. After logging the error, you can redirect the user to an error page - this is the approach I've seen most used for custom error handling in SharePoint.
In your HttpModule, you can use an approach such as the one described by Daniel to write the exceptions to the ULS logs.
Here's a simple example of doing this:
Create a class that implements IHttpModule, and wire up the appropriate event in the Http pipeline:
public void Init(HttpApplication context)
{
context.Error += new EventHandler(context_Error);
}
In the context_Error event, go through all the errors and log them ...
void context_Error(object sender, EventArgs e)
{
TraceProvider.RegisterTraceProvider();
foreach (var ex in HttpContext.Current.AllErrors)
{
TraceProvider.WriteTrace(0,
TraceProvider.TraceSeverity.Exception,
Guid.NewGuid(),
Assembly.GetExecutingAssembly().FullName,
"<your application name>",
"<exception category>",
ex.ToString());
}
TraceProvider.UnregisterTraceProvider();
HttpContext.Current.Server.ClearError();
HttpContext.Current.Response.Clear();
HttpContext.Current.Server.Transfer("/_layouts/Error500.aspx");
}
You of course have to wrap this all up into a feature (scoped at the Web Application level) and deploy it to SharePoint.
Note that your entry in the section of web.config for this custom error http module needs to be first in the list. The order in which the http modules are listed in the section matters, and the custom error http module should always execute first.
(SharePoint 2007?) From Central Admin, go to Operations->Diagnostic Logging, "Trace Log" and "Event Throttling" are what you're looking for.
Select a category in "Event Throttling" and select least critical errors for both the event and trace logs. Then, select a path for the trace logs (mine was defaulted to ..12\LOGS) and supply a max number of logs and the number of minutes to use each log file.
I wrote some blog posts that will help you out. What i suggest is using the BCL logging classes (System.Diagnostics) and creating a custom TraceListner that writes to the SharePoint ULS logs.
http://sharepoint.nailhead.net/2008/04/instrumentation-logging-for-sharepoint.html
If you´re using MOSS, you can use this:
Microsoft.Office.Server.Diagnostics.PortalLog.LogString("Message");
Keep in mind that according to Microsoft documentation - LogString is reserved for internal use and is not intended to be used directly from your code.
For more details refer this link -
https://msdn.microsoft.com/en-us/library/office/microsoft.office.server.diagnostics.portallog_members.aspx

How to Create a Managed Path through SharePoint Object Model

This is a question for a WSS/SharePoint guru.
Consider this scenario: I have an ASP.Net web service which links our corporate CRM system and WSS-based intranet together. What I am trying to do is provision a new WSS site collection whenever a new client is added to the CRM system. In order to make this work, I need to programmatically add the managed path to the new site collection. I know that this is possible via the Object Model, but when I try it in my own web service, it fails. Sample code extract below:
Dim _ClientSiteUrl As String = "http://myintranet/clients/sampleclient"
Using _RootWeb As SPSite = New SPSite("http://myintranet")
Dim _ManagedPaths As SPPrefixCollection = _RootWeb.WebApplication.Prefixes
If Not (_ManagedPaths.Contains(_ClientSiteUrl)) Then
_ManagedPaths.Add(_ClientSiteUrl, SPPrefixType.ExplicitInclusion)
End If
End Using
This code fails with a NullReferenceException on SPUtility.ValidateFormDigest(). Research suggested that this may be due to insufficient privileges, I tried running the code within an elevated privileges block using SPSecurity.RunWithElevatedPrivileges(AddressOf AddManagedPath), where AddManagedPath is a Sub procedure containing the above code sample.
This then fails with an InvalidOperationException, "Operation is not valid due to the current state of the object."
Where am I going wrong?
One workaround I have managed to do is to call out to STSADM.EXE via Process.Start(), supplying the requisite parameters, and this works.
Update: whilst developing the web service, I am running it using the built-in Visual Studio 2005 web server - what security context will this be running under? Can I change the security context by putting entries in web.config?
Update: I think the problem is definitely to do with not running the web service within the correct SharePoint security context. I decided to go with the workaround I suggested and shell out to STSADM, although to do this, the application pool identity that the web service runs under must be a member of the SharePoint administrators.
Update
I think you have proved that the issue is not with the code.
SPSecurity.RunWithElevatedPrivileges: Normally the code in the SharePoint web application executes with the privileges of the user taking the action. The RunWithElevatedPrivileges runs the code in the context of the SharePoint web application pools account (i think)
The description on MSDN could go into the details a tiny bit more.
The issue with the call may be that the web service is not actually running the code within a SharePoint process, so explaining why it cannot elevate (wild guess alert).
Have a crack at changing the user of your web services application pool and see if that gives any joy.
It is likely to be a permissions issue.
Maybe try:
Dim clientSiteUrl As String = "http://myintranet/clients/sampleclient"
Using SPSite = new SPSite(clientSiteUrl)
webApp As SPWebApplication = SPWebApplication.Lookup(new Uri(clientSiteUrl));
If Not (webApp.Prefixes.Contains(clientSiteUrl)) Then
webApp.Prefixes.Add(clientSiteUrl, SPPrefixType.ExplicitInclusion)
End If
End Using
This is not exact code.
Since the above code is not the exact code, here is the exact working code for a Web Application scopped feature in the Feature Activated event:
On feature activation at the Mange web application features page, activate feature will create a new Explicit managed path in the specified web application (I want to replace the hard coding, maybe with Properties.Feature.Parent, or something similar.)
using (SPSite site = new SPSite("http://dev-moss07-eric/PathHere")) {
SPWebApplication webApp = SPWebApplication.Lookup(new Uri("http://dev-moss07-eric"));
if (webApp.Prefixes.Contains("PathHere"))
{
//
}
else
{
webApp.Prefixes.Add("PathHere", SPPrefixType.ExplicitInclusion);
}
}
Code can probably be improved, but its my attempt at converting the above code.
If you want to create a managed path (explicit) and a site collection at that path, do the following:
using (SPSite site = new SPSite("http://dev-moss07-eric")) {
SPWebApplication webApp = SPWebApplication.Lookup(new Uri("http://dev-moss07-eric"));
if (webApp.Prefixes.Contains("ManagedPathHere"))
{
//
}
else
{
webApp.Prefixes.Add("ManagedPathHere", SPPrefixType.ExplicitInclusion);
}
using (SPWeb web = site.OpenWeb())
{
SPWebApplication webApplication = web.Site.WebApplication;
try
{
webApplication.Sites.Add("ManagedPathHere","Site Title Here","This site is used for hosting styling assets.", 1033, "STS#1", "6scdev\\eric.schrader", "Eric Schrader", "eric.schrader#6sc.com");
}
catch (Exception ex)
{
//ex.ToString;
}
}
}

Versioning by default in SharePoint

How can I modify a SharePoint site so that versioning is turned on by default in Document Libraries?
Versioning is not done at the site level, but at the list level.
If you want versioning to be turn on on each new library, you'll have to either:
Use your own library template (with versioning turned on)
Use feature + event handler to programmatically activate versioning on each new list
The easiest way is probably to use your own template. To do this, create a new document library, activate versioning, then save this list as template.
When you create a new list, you will then be able to use your template and directly create a list with versioning activated.
You could of course create your own site definition, but that's probably not the best solution. Creating a custom library template will work too, but if you want versioning turned on for the libraries that a particular site definition creates for you, you'll have to come up with something else.
We happen to have done this for our SharePoint implementation. We decided the best way was to create an event handler feature and staple it to all sites so that when the site is created, versioning will get turned on for all existing document libraries. Of course, new document libraries would get whatever versioning options the user who created it set.
The problem we ran into is that there is no "ListCreating" event handler so we couldn't turn the versioning on at that point. So, we tried to put the code inside the FeatureActivated event handler, figuring it would be activated on site creation and then all document libraries could be changed to have versioning turned on. The problem is that this event fired before the libraries were actually created.
So instead, we decided to put the code into the "ItemAdding" event handler and remove it after the first time that it runs. So the first time a user adds a list item or a document, it will turn on versioning for all document libraries in the site. This way, we ensure there is no way for a user to add a document to an existing library without it being versioned. Additionally, any libraries that get created before an item gets added will have versioning turned on by default as well.
It was a bit of a hairy solution, but it has worked very well for us. Here's the code we used:
public class SetVersioning : SPItemEventReceiver
{
public override void ItemAdding(SPItemEventProperties properties)
{
SPWeb CurrentWeb = properties.OpenWeb();
foreach (SPDocumentLibrary doclib in CurrentWeb.GetListsOfType(SPBaseType.DocumentLibrary))
{
doclib.EnableVersioning = true;
doclib.MajorVersionLimit = 8;
//doclib.EnableMinorVersions = true;
doclib.Update();
}
//now get rid of the receiver
SPEventReceiverDefinitionCollection receivers = CurrentWeb.EventReceivers;
foreach (SPEventReceiverDefinition definition in receivers)
{
if (definition.Name.Equals(EVENT_RECEIVER_NAME))
{
definition.Delete();
break;
}
}
base.ItemAdding(properties);
}
}

Resources