Category Archives: Features

Use a CustomAction to Fight Blog Spam

I’ve been really busy lately, and a minor thorn in my side has been some especially obnoxious blog spammers. To keep my readers from seeing them spam and to deny them the ability to benefit from their evil ways, I turned on approvals in my content list. One of the spammers decided to fight back by sending the same content over every few minutes. One of the downsides of using approval is that it also makes getting rid of items in bulk more difficult, so this really, really irritated me and I had to write a little feature to restore my own sanity.

The feature consists of a custom action and an application page. You can download the VS2005 solution and web solution package here.

Here’s how you use it.

If Has Got Spam

(sorry, I’ve been playing with LOLCODE again)

 

Activate the Feature

(If you don’t know how to install a web solution package, the Visual Studio project will do it for you when you build)

Refresh the Comments List and Use the new Menu

Click OK to Delete the Evil Spam

Be careful to approve the comments that are not evil spam before you do this. I know I deleted a couple good comments accidentally.

How it Works

 

The custom action itself is simple. It targets only comment lists and shows up only if you have permission to approve items.

<CustomAction
Id="DeletePendingAction"


RegistrationType="List"


RegistrationId="302"


GroupId="ActionsMenu"


Location="Microsoft.SharePoint.StandardMenu"


Sequence="100"


Title="Delete Pending Comments"


Description="Combat spam by deleting all the unapproved comments."


Rights="ApproveItems">

<UrlAction
Url="{SiteUrl}/_layouts/ZapPending.aspx?list={ListId}"/>

</CustomAction>

When you use it, it sends you to the new application page. You can see it’s full code in the download, but the main function is shown below.

protected
void BtnDeletePendingComments_Click(object sender, EventArgs e)

{


SPList list = SPContext.Current.List;


for (int i = list.Items.Count – 1; i >= 0; i–)

{


SPListItem comment = list.Items[i];


if (comment.ModerationInformation.Status == SPModerationStatusType.Pending)

{


Console.WriteLine(comment["Title"]);

comment.Delete();

}

}

Response.Redirect(list.DefaultViewUrl);

}

Enjoy! I’ll put this up on CodePlex next week.

–Doug Ware

Author: Doug Ware

Know Your 12 Hive – fields and ctypes

The 12 hive contains a wealth of information for the intrepid SharePoint developer. It is located on the disk at Program FilesCommon FilesMicrosoft Sharedweb server extensions12. All of the features installed on a given SharePoint server are located within the 12 Hive in the TEMPLATEFEATURES folder.

This example comes from a WSS install a MOSS server has many more features.

The two highlighted folders in the picture above are the features that implement the global site columns and field types available on every SharePoint site. The reason they are always available is that they are activated by the GLOBAL site definition and the GLOBAL site definition is always applied to every new site before the specific site definition’s instructions.

Here is a snippet of the piece of GLOBAL ONET.xml file that activates these two features. You can find it in the 12 hive too! It’s at TEMPLATEGLOBALXML.


 

<SiteFeatures>

<!– Fields –>            

<Feature ID="CA7BD552-10B1-4563-85B9-5ED1D39C962A" />

<!—ctypes –>

<Feature ID="695B6570-A48B-4A8E-8EA5-26EA7FC1D162" />

</SiteFeatures>

This means that you can safely assume that the columns and content types defined by these features are always available for your own use.

These files make great references when you begin creating your own field (site column) features and content types. This is especially true of content types because you can get the ID’s of any of the common fields directly from the field feature’s element manifest, fieldswss.xml.

For example, say you need to know the ID of the Comments site column. It’s right there in fieldswss.xml!

Want to create your own content type? Navigate in the 12 hive to TEMPLATEFEATURESctypes and open ctypeswss.xml.

Here is a bit of this file that shows the definition of the XMLDocument content type:

<ContentType ID="0x010101"

Name="$Resources:XMLDocument"

Group="$Resources:Document_Content_Types"

Description="$Resources:XMLDocumentCTDesc"

V2ListTemplateName="xmlform"

Version="0">

<FieldRefs>

<RemoveFieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" />

<FieldRef ID="{5d36727b-bcb2-47d2-a231-1f0bc63b7439}" Name="RepairDocument" />

<FieldRef ID="{11851948-b05e-41be-9d9f-bc3bf55d1de3}" Name="ShowRepairView" />

<FieldRef ID="{4b1bf6c6-4f39-45ac-acd5-16fe7a214e5e}" Name="TemplateUrl" />

<FieldRef ID="{cd1ecb9f-dd4e-4f29-ab9e-e9ff40048d64}" Name="xd_ProgID" />

</FieldRefs>

</ContentType>

You can tell from the ContentType ID that this inherits from System (0x), Item (0x01), and Document (0x0101). You can also see that this type removes the Title field it inherited from Item using the RemoveFieldRef element.

  

Author: Doug Ware

Feature Element Scope Matrix


 

Edit: ContentTypeBinding can scope to Web. I updated the matrix accordingly.

Edit: ListTemplate can scope to Site. I updated the matrix accordingly.

In the past, I was often frustrated by feature scope incompatibilities in my solutions when using ActivationDependencies. These days I almost always use a feature receiver instead (not just to handle dependencies though). Either way, remembering the compatible scopes for each element is hard on my poor brain, so I made this matrix a few months ago that I use as a reference.

Element Type

Farm 

WebApplication 

Site 

Web 

Content Type 

   

   

x 

   

Content Type Binding 

   

   

x 

x

Control 

x 

x 

x 

x 

Custom Action 

x 

x 

x 

x 

Custom Action Group 

x 

x 

x 

x 

Document Converter 

   

x 

   

   

Feature/Site Template Association

x 

x 

x 

   

Field 

   

   

x 

   

Hide Custom Action 

x 

x 

x 

x 

List Instance 

   

   

x 

x 

List Template 

   

   

x 

x 

Module 

   

   

x 

x 

Receiver 

   

   

   

x 

Workflow 

   

   

x 

   

 
 

Enjoy!

–Doug Ware

Author: Doug Ware

Deploying Assemblies to BIN Versus GAC

I had a good conversation with a systems engineer recently about where feature assemblies should be installed. I think it’s worth sharing my opinion on this here. His opinion was based on common advice that web part assemblies should go in the BIN folder and include a proper CAS policy. My opinion is based on many years of .Net development and my assessment of what an organization should have in place before introducing the configuration management burdens CAS brings to the table.

The only thing that is inherently special about an assembly in the GAC in .Net is that it can be resolved and loaded based on its full name. The full name includes the public key token and version which are embedded in the assembly. The public key is used against a hash of the assembly (created with a private key) to verify the assembly is not altered. This is why the full name of a signed assembly is called a "strong name". And, this is part of the mechanism used to handle multiple versions of the same assembly.

The public key is the most commonly used evidence for code access security, and as a result, if you are creating a CAS policy for an assembly that is going in the BIN, you probably won’t skip code signing which is a requirement for installing an assembly to the GAC.

.Net gives assemblies in the GAC full trust by default.

Assemblies loaded from the GAC get the same amount of process isolation as assemblies loaded from anywhere else. If ten application domains load the same assembly, there are ten instances of the assembly loaded, not one. Assemblies don’t get loaded until a process in the app domain tries to invoke code in the assembly. So, putting an assembly in the GAC, or even referencing the assembly in an assembly manifest, will not cause the assembly to load or code to run. Putting an assembly in BIN on one site does prevent other sites from loading the assembly because their app domains can’t find the assembly to load it. Putting it in the GAC won’t cause them to load it.

Full trust does not mean that the assembly can do anything that it wants. It has permissions up to those of the identity it is executing under unless it uses impersonation to explicitly assume the identity of a more privileged account. This is a big difference between .Net and native code.

Anyway… In my opinion, the best argument to be made for putting assemblies into BIN instead of the GAC in a SharePoint site is that, assuming the CAS policies are tightly controlled by a group other than development, it requires the devs to explicitly state that the assembly needs to do something out of the norm and could potentially prevent a rogue dev from sneaking in code that is mucking with stuff on the disk, database, registry, etc.

But, this requires governance to affect.

Absent governance an assembly in BIN is no more or less dangerous to the overall stability of the server than an assembly in the GAC. And, in my opinion, if the environment is a farm and if the decision to put an assembly in BIN means that the environment now requires custom policies on a per web app basis, the BIN approach has a significant downside. You could mitigate by deploying policies directly based on a custom evidence, like a corporate-wide public key, but that would only really work if the dev group delay-signed the assemblies and another group completed the signing and built the solution.

But, absent strong and continuing governance and review with clear demarcation between dev and production and mature configuration management practices, putting bits into BIN is more likely to cause problems than simply putting them in the GAC.

So, to sum it up, there are times when CAS policies address clear requirements and in those cases, BIN might well be the way to go. But absent the ability to clearly trace the decision to an actual requirement, I contend that it is not by default ‘better’.

Author: Doug Ware

SharePoint 2007 List Membership Provider

I recently completed a very cool project where I had the pleasure of working with Matt Ranlett. A big element of the project is a membership provider that Matt built that uses a SharePoint list as the user store. The client, Preparis, has very graciously allowed Matt to put the result of on CodePlex here.

Project Description
This project contains a custom Membership and Role provider for SharePoint 2007 which allows you to store your users in a SharePoint list on the site. This gives you the advantage of being able to maintain your users via the simple SharePoint list UI without requiring custom SQL access web parts

Custom SharePoint 2007 List-based Membership and Role Provider

This custom SharePoint 2007 forms based authentication provider was created to fit the following customer requirements:

  • on a single server (farm), use forms based authentication to validate external users to prevent the need for Active Directory accounts
  • on the single server (farm), host 20 to 30 or more site collections, each exclusively relying on forms based authentication user validation
  • each SharePoint site collection must maintain a private set of users
  • grant the ability to independently back up and restore each individual site collection, including the user base
  • include easy to use and cheap to build membership list maintenance tools

Here is a link to Matt’s blog post on the topic. Congratulations Matt!