Dedicated to development, configuration, and tips and tricks for both WSS 3.0 and MOSS

Posted by Brian LaSitis on Wednesday, 4 Mar 2009 02:16

In my last posting in this series, we reviewed how to create a custom content type as a SharePoint feature.  While this is a great way to modularize the set of content types that are used within an organization, having them defined in a feature does not implicitly make them a part of all future sites that are created, as a SharePoint environment grows & evolves over time.   Let us briefly review a couple of approaches to get around this:

  • Define Procedures:  One approach to this is simply to define procedures, such that when new sites are created, the feature(s) that define the content types are activated as part of the process.  Unfortunately, while this method can be effective, it has room for error – if someone forgets to activate the features after a site is created, then that site is inconsistent, and over time you may end up with a lot of inconsistent sites within your organization’s SharePoint environment. 
  • Staple The Feature(s) to a Built-in Site Definition:  A second method would be to simply staple the custom features to one of the built-in site definitions that come with SharePoint, such as the Team Site or Blank Site.  While this would certainly ensure that the features were activated consistently, it might just be a little too consistent.  After all, if you have a feature that defines some content types intended for Client Project sites, and another feature that defines content types for Knowledge Base sites, which built-in site definition do you staple them to?  Do you draw straws to see which one will get attached to the Team Site and which one goes to Blank?  Do you staple all of our custom features to the Team Site definition & make them all available everywhere?  While these options work, and you might be able to tack on this work in a small 1-2 hour time window, they really don’t effectively leverage the SharePoint product capabilities.  You end up having all of these un-related features stapled on to the built-in site definitions, and everything is just one big hodgepodge.
  • Create a Custom Site Definition:  A third method is to bite the bullet and just create a copy of the built-in Team or Blank site definition, and include your custom feature in it directly, or leverage feature stapling to attach it.  This method, despite requiring one to face the challenge of a custom site definition, offers the best solution, as you could create multiple copies of the built-in Team site definition and tie different custom features to each, as needed, to ensure all.  Without question, this is the preferred approach of the three I’ve defined.

Creating a custom site definition based upon the built-in Blank site template is a relatively trivial task, so I will not belabor the process, but instead focus on how we can take our “copied” version, and modify it to include instantiation of our custom feature.

To get us all on the same page, I have updated my Visual Studio solution to include the additional necessary “12” hive folders and files for our new site definition, named ACME.  Below is what the updated structure looks like:

image

Also, for everyone’s reference, here is the contents of the custom WEBTEMP file that was created:

<?xml version="1.0" encoding="utf-8"?>
<!-- _lcid="1033" _version="12.0.4518" _dal="1" -->
<!-- _LocalBinding -->
<Templates xmlns:ows="Microsoft SharePoint">
    <Template Name="ACME" ID="11000">
        <Configuration ID="0" 
                       Title="Acme Blank Site" 
                       Hidden="FALSE" 
                       ImageUrl="/_layouts/images/blankprev.png" 
                       Description="An ACME blank site for you to customize based on your requirements." 
                       DisplayCategory="ACME" 
                       AllowGlobalFeatureAssociations="False" >    
        </Configuration>
    </Template>
</Templates>

Within the ONET.XML file, the only notable section is Configurations, shown below:

<Configurations>
    <Configuration ID="0" Name="AcmeBlank">
        <Lists />
        <Modules>
            <Module Name="DefaultBlank" />
        </Modules>
        <SiteFeatures>
            <!-- BasicWebParts Feature -->
            <Feature ID="00BFEA71-1C5E-4A24-B310-BA51C3EB7A57" />
            <!-- Three-state Workflow Feature -->
            <Feature ID="FDE5D850-671E-4143-950A-87B473922DC7" />
            <!-- Acme Content Types -->
            <Feature ID="A5DA673C-FF79-427c-BBB4-422937F82FCB" />
        </SiteFeatures>
        <WebFeatures>
            <Feature ID="00BFEA71-4EA5-48D4-A4AD-7EA5C011ABE5" />
            <!-- TeamCollab Feature -->
            <Feature ID="F41CC668-37E5-4743-B4A8-74D1DB3FD8A4" />
            <!-- MobilityRedirect -->
        </WebFeatures>
    </Configuration>
</Configurations>

As you can see, the Acme Content Types feature (A5DA673C-FF79-427c-BBB4-422937F82FCB) is included in the SiteFeatures section, meaning that this feature will be automatically activated when instances of this site definition are created.

Another approach that could be taken is instead of explicitly linking out to our custom feature from within the ONET.XML file, leverage feature stapling to instead attach our feature back to the site definition, using a TemplateName value of “ACME#0”, denoting configuration ID #0 of the ACME site definition.  This blog posting offers a nice explanation of what is involved in leveraging the feature stapling capability of MOSS 2007.  In all honesty, in this scenario, I’m not sure one method offers a significant advantage over the other.  I personally would probably take the route of just embedding the Feature instantiation within the ONET.XML, as my snippet from above illustrates, but as they say “de gustibus non est disputandum.”

At any rate, the whole process of creating my copy of the Blank Site Definition took maybe 30 minutes, and for the flexibility it offers to any sites created from it going forward, as well as the capabilities it opens up for being able to activate custom features that apply to the nature of the sites being created from it, this exercise is well worth it.  If nothing else, it can illustrate to a client that our skill level with the product is a couple of notches above the competition, and it positions them well for the future.

Posted by Brian LaSitis on Monday, 2 Mar 2009 07:23

In my last posting on this topic, I illustrated the use of a SharePoint feature to define a set of site columns to add to a given site.  In this posting, we will extend that same feature to include a second element manifest that will define a content type that uses these site columns.

If you recall, each of the site columns we defined included a unique ID value – these ID values will become important when we create a content type using these site columns.  Below is a table that references each site column we’ve created and the associated ID for each:

Site Column ID Name
{9A77BCEB-5230-48f2-93ED-D87DAAEC6998} ArticleHeadline
{CB2E757E-3876-40c8-A720-C98063DBCC3D} ArticleAbstract
{13480703-B260-4354-83DC-AA7AF22A629E} Classification
{B022435C-D299-40fd-B071-5C9DF95F6F4F} DisclosureLevels
{DBBD4D5F-A390-414b-9BB2-1160C1B2E35E} Lifetime
{7B6905FB-6619-4276-AB3D-9A04A04D8806} ExpectedCost
{6AAD913B-D930-49b5-8BCD-99FAB8857257} ExpiresOn
{D38E97DA-CD82-4a77-9086-3B97A6D61A92} Activated

 

In order to create a content type within SharePoint, you must inherit from an existing one.  In most cases, this inheritance will start with one of the built-in content types, such as Item, Document, or Folder.  For this exercise, we will create a content type that inherits from Document and includes the Article Headline, Article Abstract, Classification, Disclosure Levels, and Expires On custom site columns.  Consider the following XML that makes up our element manifest, named AcmeContentTypes.xml

<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
    <ContentType ID="0x0101007B9487E9C4334930A393D9E96E152D36" 
                 Name="Acme Document" 
                 Description="" 
                 Group="Acme Content Types" 
                 Sealed="FALSE" 
                 Version="0">
        <FieldRefs>
            <FieldRef ID="{9A77BCEB-5230-48f2-93ED-D87DAAEC6998}" Name="ArticleHeadline" />
            <FieldRef ID="{CB2E757E-3876-40c8-A720-C98063DBCC3D}" Name="ArticleAbstract" />
            <FieldRef ID="{13480703-B260-4354-83DC-AA7AF22A629E}" Name="Classification" Required="TRUE" />
            <FieldRef ID="{B022435C-D299-40fd-B071-5C9DF95F6F4F}" Name="DisclosureLevels" Required="TRUE" />
            <FieldRef ID="{6AAD913B-D930-49b5-8BCD-99FAB8857257}" Name="ExpiresOn" />
        </FieldRefs>
    </ContentType>
</Elements>

Let us step through what we have here, starting with the outer ContentType element.  This element is what defines the actual content type we are creating, and includes its ID, Name, Description, and Group.  Now you are probably looking at that ID value and going, huh?  Let us step back for a moment and discuss how an ID for a custom content type is created.

Content Type IDs

The ID value that is assigned to a custom content type defines two things -- (1) it is a unique identifier for the content type, and (2) it defines the inheritance tree, or lineage, for the content type.  The 1st point I’ve made makes complete sense, but the second probably sounds confusing – how could that ugly thing I’m calling an ID actually tell us *anything* about the inheritance tree for the content type.  Well, as it turns out, there is some madness to this.  Let us being with a definition for how a content type ID is constructed  -- there are two possible formats:

  • Parent Content Type ID + 2 Hexadecimal Values (cannot be “00”)
  • Parent Content Type ID + “00” + Unique Identifier

Let us also examine the ID values and hierarchy for the core set of content types included in SharePoint:

image

If you study that graphic, you can see that the most fundamental content type within SharePoint, named System uses the ID value of 0x.  The next level in defines the base Item content type, with an ID value of 0x01.  Let us look back at bullet point #1 in the rules for constructing a content type ID, and we can quickly see that the ID value of the Item content type makes sense, as it includes the ID value of its parent (System), and 2 hexadecimal digits.  As you go further into the hierarchy, you will see that this pattern continues nicely.  Now, let us look again at the ID value I included in our custom Acme Document content type, defined above, only this time, I will use some color coding to help us pick apart its components:

0x0101007B9487E9C4334930A393D9E96E152D36

  • 0x – System
  • 0x01 – Item
  • 0x0101 – Document
  • 0x0101007B9487E9C4334930A393D9E96E152D36 – Acme Document (note “00” separator)

Based upon the color coding, you can easily see that the ID of our custom content type does make sense, but you may be asking, why did I switch to the second method of creating a content type name, by using a unique identifier, instead of just two more hexadecimal digits?  The reason for doing so is a Microsoft recommendation, which states that when constructing a content type ID for a content type that is a direct child of a content type that was created by someone else, the method of using a unique identifier should be used.  This is to ensure that your custom content type will not conflict with any others that may have been created.  For any additional custom content types that are children of this one, the first method of just appending 2 hexadecimal digits can be used.  As an example, if we were to create a content type below our custom Acme Document content type, its identifier could be set to: 0x0101007B9487E9C4334930A393D9E96E152D3601 (note the addition of just “01” to our original ID value).

Hopefully I have done justice in explaining this concept, but if I did not, or you still have questions, please take a look at the following pages in MSDN, which I used as a reference in coming up with my explanation:

FieldRef Elements

Fortunately, the ID values for creating custom content types is probably the most challenging aspect of creating a content type, so once you are able to get your head around this logic, you should be in good shape.  Let us now proceed to what is included within our ContentType XML element, namely the set of site columns that our content type will define to augment the set of site columns that are “brought along” from the parent content type ours inherits from.  Each site column to be included is defined using a FieldRef XML element, which includes only two required attributes – ID and Name.  Fortunately these two are easy, as the ID value corresponds back to the ID value we defined for our custom site column, and the Name value also directly maps back to the site column definition.  Another attribute you may want to include in your FieldRef element is named Required, and accepts a Boolean value indicating whether the given site column will require a value when list item instances are created that use this content type. In my example, I have defined the Classification and Disclosure Levels fields as being required.

The only other remaining piece to allow us to complete our custom content type is to add a reference to our new element manifest to our feature.xml file.  Below is what our feature.xml file should look like with the additional reference added:

<?xml version="1.0" encoding="utf-8" ?>
<Feature  Id="A5DA673C-FF79-427c-BBB4-422937F82FCB"
          Title="Acme Content Types"
          Description=""
          Version="12.0.0.0"
          Scope="Site"
          Hidden="FALSE"
          xmlns="http://schemas.microsoft.com/sharepoint/">
    <ElementManifests>
        <ElementManifest Location="AcmeSiteColumns.xml" />
        <ElementManifest Location="AcmeContentTypes.xml" />
    </ElementManifests>
</Feature>

If we proceed to activate this feature on a site collection, we will see the following additional content type added, as shown below:

image

If we drill into the Content Type page for this entry, we can see the following details, identically reflecting what we defined in our custom feature:

image 

See just the same results you would get if you created this within the site itself using the Site Settings pages directly, only this method involves a LOT less pointing & clicking.

In the next posting in this series, we will see how we can take the custom feature we’ve developed here to create a content type, and tie it into a custom site template, so that as future site collections are created, they can each be consistent and include this content type by default.

Posted by Brian LaSitis on Thursday, 26 Feb 2009 02:07

Nearly every SharePoint project that involves some type of content management, whether it be using document libraries, custom lists, or published web content, we generally find ourselves needing to create one or more content types to deliver the designed solution to a client.  Up until this point, most often we have taken the most basic approach to creating content types in the past – performing the task through the built-in SharePoint UI.  While this methodology works, it can almost be thought of as the brute-force approach.  I make this comparison because while the built-in UI offers a working method for creating content types, and the underlying site columns that they are made up of, it lacks in providing any means to take those site columns and content types and move them elsewhere, or apply them to multiple site collections.

Another approach to building content types and site columns to leverage the built-in Features framework within SharePoint 2007.  While this method isn’t as glitzy, as it involves creating XML files instead of just pointing & clicking, it offers a much more portable, extensible deliverable.  Instead of going to a client and just manually building out the same site columns and content types in each & every site collection they require, we can create a single SharePoint feature that contains these definitions, and then activate it on each site collection after it is created, or take it one step further, and create a custom site definition based upon the site template the client wants to use for their sites, and configure the feature within it to be activated automatically upon site creation, or leverage the feature stapling framework as another approach to automatically activating this feature for new sites – what could be more streamlined, and error-proof than that?

My intent is for this posting to cover simply the process of creating a feature for defining site columns, since these are the fundamental building blocks of content types, then in forthcoming posts, discuss the process of bringing these site columns together into one or more content types, and finally how to take the built-in Team site definition, create a duplicate of it, and add our custom feature to it, to illustrate automatic feature activation. 

A SharePoint feature, is a module of functionality that can be enabled at specific scopes within a SharePoint farm, namely the farm level, web application level, site collection level, and site level.  SharePoint itself uses features for nearly everything it provides out of the box – the standard list definitions, the built-in site columns & content types, built-in web parts, and they are even used to define what is displayed on the Site Settings page and the Site Actions menu.  Each of the features, both built-in and custom ones, live within the “12” hive at 12\TEMPLATE\FEATURES, and each is contained within its own unique folder, whose name reflects the feature’s purpose.  Within this folder there is one and only one required file, whose name must be feature.xml.  This file defines the basic characteristics of the feature including its ID, name, description, activation scope, and visibility.  Additionally this XML file can define additional information that is specific to the feature itself, such as a receiving assembly, and potentially one or more element manifests.  Each element manifest that is defined is represented by an additional XML file that is also contained within the feature’s folder, and within these files is where the uniqueness of the feature comes out, as they can be used to define the set of files that the feature is going to inject into a given site, or the custom actions the feature will add to the Site Actions menu, or the site columns and/or content types that the feature will add to its targeted site, just to name a few.  Finally, in addition to the element manifest XML files, the feature’s folder can contain any number of other files and folders that are needed for the feature itself, based upon its intended purposes.

For our example feature, we will start with just two XML files in our feature, namely the required feature.xml file, and a single element manifest named AcmeSiteColumns.xml.  Let us begin by reviewing the contents of our feature.xml file:

<?xml version="1.0" encoding="utf-8" ?>
<Feature  Id="A5DA673C-FF79-427c-BBB4-422937F82FCB"
          Title="Acme Content Types"
          Description=""
          Version="12.0.0.0"
          Scope="Site"
          Hidden="FALSE"
          xmlns="http://schemas.microsoft.com/sharepoint/">
    <ElementManifests>
        <ElementManifest Location="AcmeSiteColumns.xml" />
    </ElementManifests>
</Feature>

A couple of the more important points about this to highlight are:

  • Id – this must be a unique GUID for your feature.  New GUIDs  can be generated within Visual Studio using the Tools, Create GUID menu option.
  • Title – this is the display name of the feature, as it will show up within SharePoint.
  • Description – this is the descriptive text that will show up within SharePoint for the feature, on the listing page.
  • Scope – this can be set to 1 of 4 possible values – Farm, WebApplication, Site (site collection), Web (site).
  • We have one element manifest defined, using a filename of AcmeSiteColumns.xml

And the accompanying AcmeSiteColumns.xml file contains the following:

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
    <!-- Acme Site Columns -->
    <Field ID="{9A77BCEB-5230-48f2-93ED-D87DAAEC6998}" 
           Name="ArticleHeadline" 
           StaticName="ArticleHeadline" 
           SourceID="http://schemas.microsoft.com/sharepoint/v3" 
           Group="Acme Site Columns" 
           DisplayName="Article Headline" 
           Type="Text" 
           Required="FALSE" 
           Sealed="FALSE">        
    </Field>

    <Field ID="{CB2E757E-3876-40c8-A720-C98063DBCC3D}"
           Name="ArticleAbstract"
           StaticName="ArticleAbstract"
           SourceID="http://schemas.microsoft.com/sharepoint/v3"
           Group="Acme Site Columns"
           DisplayName="Article Abstract"
           Type="Note"
           NumLines="6"
           Required="FALSE"
           Sealed="FALSE">
    </Field>

    <Field ID="{13480703-B260-4354-83DC-AA7AF22A629E}"
           Name="Classification"
           StaticName="Classification"
           SourceID="http://schemas.microsoft.com/sharepoint/v3"
           Group="Acme Site Columns"
           DisplayName="Classification"
           Type="Choice"
           Required="FALSE"
           Sealed="FALSE">
            <CHOICES>
                <CHOICE></CHOICE>
                <CHOICE>Public</CHOICE>
                <CHOICE>Internal</CHOICE>
                <CHOICE>Restricted</CHOICE>
                <CHOICE>Confidential</CHOICE>
                <CHOICE>Secret</CHOICE>
            </CHOICES>
            <Default></Default>
    </Field>

    <Field ID="{B022435C-D299-40fd-B071-5C9DF95F6F4F}"
           Name="DisclosureLevels"
           StaticName="DisclosureLevels"
           SourceID="http://schemas.microsoft.com/sharepoint/v3"
           Group="Acme Site Columns"
           DisplayName="Disclosure Levels"
           Type="MultiChoice"
           Required="FALSE"
           Sealed="FALSE">
            <CHOICES>
                <CHOICE>A1</CHOICE>
                <CHOICE>A2</CHOICE>
                <CHOICE>A3</CHOICE>
                <CHOICE>A4</CHOICE>
                <CHOICE>A5</CHOICE>
            </CHOICES>
    </Field>

    <Field ID="{DBBD4D5F-A390-414b-9BB2-1160C1B2E35E}"
           Name="Lifetime"
           StaticName="Lifetime"
           SourceID="http://schemas.microsoft.com/sharepoint/v3"
           Group="Acme Site Columns"
           DisplayName="Lifetime (yrs.)"
           Type="Number"
           Required="FALSE"           
           Min="0"
           Max="100"
           Decimals="0"
           Sealed="FALSE">
    </Field>

    <Field ID="{7B6905FB-6619-4276-AB3D-9A04A04D8806}"
            Name="ExpectedCost"
            StaticName="ExpectedCost"
            SourceID="http://schemas.microsoft.com/sharepoint/v3"
            Group="Acme Site Columns"
            DisplayName="Expected Cost"
            Type="Currency"
            Required="FALSE"
            Sealed="FALSE">
    </Field>

    <Field ID="{6AAD913B-D930-49b5-8BCD-99FAB8857257}"
            Name="ExpiresOn"
            StaticName="ExpiresOn"
            SourceID="http://schemas.microsoft.com/sharepoint/v3"
            Group="Acme Site Columns"
            DisplayName="Expires On"
            Type="DateTime"
            Required="FALSE"
            Sealed="FALSE">
    </Field>

    <Field ID="{D38E97DA-CD82-4a77-9086-3B97A6D61A92}"
           Name="Activated"
           StaticName="Activated"
           SourceID="http://schemas.microsoft.com/sharepoint/v3"
           Group="Acme Site Columns"        
           DisplayName="Activated"
           Type="Boolean"        
           Required="FALSE"
           Sealed="FALSE">
        <Default>0</Default>
    </Field>
</Elements>

As should be evident, we have defined eight (8) site columns, each of which is represented by a Field XML element in the file shown above.  In this set of site columns, I’ve attempted to illustrate the following basic built-in field types: Single line of text, Multiple lines of text, Choice (single value), Choice (multiple value), Number, Currency, Yes/No, and Date/Time.  Obviously I was not able to exhaust each potential option that is available within these field types, but I’ve attempted to provide a sampling of what is possible, to wet your appetite a bit.  Furthermore notice that each site column defines its own ID value which must be a unique GUID for each column you define, as well as a consistent, meaningful grouping name. 

Also, note the use of the Required and Sealed attributes.  The Required attribute is used to indicate if a given site column, when it is used, must require a value to be specified – each of my examples set this attribute to FALSE.  The Sealed attribute is a bit more interesting – this defines if the given site column is able to be modified within the SharePoint UI after it is created, such as to allow its choice values or other customizable options to be changed.  For some columns, restricting this might make sense, but in this example I’ve again set each one to a value of FALSE for this attribute.

To see the fruits of our work, we can browse to a SharePoint site where this custom feature has been activated within, and we should see the following on the Site Columns page:

image

As stated earlier, both of these files will live within a new folder that defines the feature.  I find it most beneficial to create an Empty Visual Studio project when creating features and other custom SharePoint artifacts, and within this project, create a folder hierarchy that mimics the “12”-hive structure, for the portions of it that are applicable to your solution.  In the case of this custom feature, this means creating folders for 12, TEMPLATE, and FEATURES, nesting them accordingly, to result in the following project structure:

image

In addition to the folder structure, you will also see a BAT file script named wsp.bat. This particular BAT file script contains the necessary commands to invoke WSPBuilder to bundle this feature into a solution package, and then activate the feature within a SharePoint site.  Based upon  your deployment needs, you may find it beneficial to modify the logic within this script to meet your own needs.  The contents of my deployment BAT file is as follows:

@ECHO OFF
@SET WSPPBUILDER="C:\Tools\WspBuilder\WspBuilder.exe"
@SET STSADM="C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\STSADM.EXE"
@SET SOLUTIONNAME=ContentTypeTest.wsp
@SET URL=http://sp2007dev
@SET FEATURE=AcmeContentTypes
  
ECHO Building Solution Package
  %WSPPBUILDER% -Excludepaths bin
  
ECHO Deactivate feature
 %STSADM% -o deactivatefeature -name %FEATURE% -url %URL%/sites/blank
  
ECHO Removing Existing Solution
 %STSADM% -o retractsolution -name %SOLUTIONNAME% -immediate -url %URL%
 %STSADM% -o execadmsvcjobs
 %STSADM% -o deletesolution -name %SOLUTIONNAME%

ECHO Installing Solution
 %STSADM% -o addsolution -filename %SOLUTIONNAME%
 %STSADM% -o deploysolution -name %SOLUTIONNAME% -immediate -url %URL% -allowGacDeployment -allowCasPolicies -force
 %STSADM% -o execadmsvcjobs

ECHO Recycling App Pool
 iisapp /a "SharePoint - 80" /r

ECHO Activate feature
 %STSADM% -o activatefeature -name %FEATURE% -url %URL%/sites/blank

I find it easiest to simply set up this script to be executed as a post-build event, this way once you have made changes to your XML files within the Visual Studio project, you can just build the project, and that will kick off this process, from which you can proceed to your site, and review the results.  Below identifies how my post-build event is configured:

image

Using this approach I’ve outlined for packaging up SharePoint artifacts is well worth it, as it provides a nice end-to-end means for taking a Visual Studio project and turning it into something that can be deployed to any number of SharePoint farms without any additional work.  My forthcoming posts on creating additional features and site definitions will employ a similar pattern, though I will refrain from explaining the details of it in those posts.

So with that said, you can see what I mean by saying that leveraging SharePoint Features to deploy custom site columns adds tremendous value to a project.  Not only does it allow development efforts for creating site columns to produce something that is re-usable, but it also formalizes the process, and makes it agnostic of the SharePoint farm that it is being deployed to.  There is absolutely no reason why a set of custom features that define an *entire* SharePoint build-out couldn’t be created, pulled together into a WSP solution package, and then at the client site just install the solution package, deploy it, and proceed to activate its contents as necessary.  In the next posting I’m planning on this topic, we will take the set of site columns we’ve defined here and create a content types that links them all together, again leveraging the feature framework for the entire process.

Posted by Aaron Varga on Tuesday, 3 Feb 2009 01:19

Document libraries are a great way to manage and organize documents, and one of the many benefits of leverage SharePoint for document managements is to have a central repository for documents.  File shares are prone to duplicate documents and multiple versions of the same documents, and obviously using SharePoint will mitigate many of these concerns.  Occasionally though, it makes sense to keep a document in multiple locations – what if for whatever reason, a document belongs in Library 1 and also Library 2?  Should you just upload it to both places?  Nope!  Utilizing a built-in content type will allow you to create shortcuts that point to the original location in your document library.

To illustrate this, I’ve created an Expense Reports document library with a few entries. Granted, expense reports probably aren’t a practical example, but I couldn’t think of anything more creative :).

image


As you can see, the actual documents are stored within this library.  Next, I created a My Shortcuts document library that will be used to store documents or shortcuts to other documents.  After creating the My Shortcuts library, you must allow the management of content types by navigating to Site Settings > Advanced Settings:
 image


Next, add the Link to a Document content type to the document library:

image

 
Once you add this content type, you will be able to create a link from the New toolbar menu:

image 


Simply enter a name for the shortcut and the URL, and click OK:

image


Viola!  A link to the document!

image

 

This is a great way to keep your documents in a single place, but still be able to provide a link to it from within a completely separate library.