in

SharePoint Blogs

The Best Place for SharePoint-related Blogs

Team Eli's blog

  • Record Center / Information Management Policy Jobs Not Running

    Interesting enough, we noticed after installing “something” (it could have been Project Server, Service Pack 1 or even the Infrastructure Update) that none of the time jobs managed by the PolicyConfig service were running.   If fact, they were all list as have never run, which I knew is a lie since they used to work at some point in time.  These jobs include:

    Expiration Policy
    Hold Processing and Reporting
    Information Management Policy
    Records Center Processing

    In trying to troubleshoot this issue, I was messing around with one of the stsadm commands that can be used to set the when these four polices run.  However, when I tried to run the following command:

    Stsadm –o setpolicyschedule –schedule “daily at 23:50:00”

     I would get an error saying “The Information Management Policy feature is not active.”  After a little bit more research, I found out that the “feature” that it is complaining about is the RecordsManagment feature.  Granted, this feature is scope at the Farm level and thereby is automatically activate, I took the liberty and ran

     stsadm –o activatefeature –name RecordsManagement –force. 

    After running the re-activate command, I was then able to run the setpolicyschedule command (which I didn’t need to do to resolve the issue) and volia my Information Management Policy’s timer jobs are running again!

     

  • Use Powershell to get a new GUID

    In SharePoint development, you are always in need of generating a unique id (aka GUID) for your features and solutions.  While most developers have a GUID generator setup in Visual Studio, here is a quick alternative if you don't want to open Visual Studio but have Powershell open.

    [System.Guid]::NewGuid().ToString()

     And volia...you have a new GUID that you can use in your application.

    Eli

     

  • TeamEli's first CodePlex Project -- GUI to Add Solutions to SharePoint Solution Store

    Well, I finally took the time to sit down and put together a CodePlex project for some of the application pages (ok...only one so far...) that I am building for SharePoint admins.  So far, I have an application page that allows SharePoint admins to upload SharePoint solutions into the solution store.  This gets rid of the need to have to log into the server to run that one pesty stsadm command (addsolution).  I am not sure if anybody will find this useful but I figured it would be a nice start for my presence on CodePlex.

    TeamEli SharePoint (2007) Tools

     As far as the application goes, it's fairly straight forward.  A custom application page that resides in the ADMIN folder with a custom action on the operations page right underneath the Solutions Management link.  Turns out it is pretty easy to add new solution files to the store.  First, I have to save a copy of the file locally so that the API can access the file then I check to see if the solution already exists.  If it does, it upgrades the solution.  Otherwise, it just adds the solution to the store.

    // Saves file to local system so that sharepoint can upload it
    string path = "C:\\Packages\\" + upload_Solution.FileName;
    upload_Solution.SaveAs(path);

    // Gets current solution (if present)
    SPSolution solution = SPFarm.Local.Solutions[upload_Solution.FileName];

    if (solution == null)
    {
         
    // Adds new solution to farm
         
    solution = SPFarm.Local.Solutions.Add(path);
    }
    else
    {
        
    if (solution.Deployed)
         {
              
    // Upgrades the solution now.
              
    solution.Upgrade(path, DateTime.Now);
         }
        
    else
        
    {
             
    // Removes solution and re-adds it
             
    solution.Delete();
              solution =
    SPFarm.Local.Solutions.Add(path);
         }
    }

    Please feel free to give it awhirl and let me know what your thoughts are and if you have any problems.

    Eli

  • Use SharePoint API to Create Wiki Page

    I had the task of auto generating hundreds of Wiki pages (basically a wiki of commonly used terms for my client's line of business).  The data was read from a Tab delimited file and in order to create Wiki pages, we have to do a few things differently.  Below is the .NET / SharePoint code that can be used to create a new Wiki page.

    string
    fileName = "TestWiki";
    string body = "This is the body of the Wiki. Can contain <b>HTML</b>";

    SPSite site = new SPSite("http://localhost");
    SPWeb web = site.OpenWeb();
    SPList list = web.Lists["Wiki"];

    string serverRelativeUrl = web.ServerRelativeUrl + list.Title + "/" +fileName+ ".aspx";

    SPListItem item = list.RootFolder.Files.Add(serverRelativeUrl, SPTemplateFileType.WikiPage).Item;
    item[
    "WikiField"] = body;
    item.UpdateOverwriteVersion();

    web.Dispose();
    site.Dispose();

    Hope this helps to save somebody some time.

    Eli

  • Powershell Script -- Start Custom Timer Job Out of Schedule

    I find it sometimes that I need to start one of my custom timer jobs ahead of schedule (normally for testing purpose) so I decided to put together a Powershell that calls the execute method of the SPJobDefintion.

    OneTimeJob.ps1 

    param
    (
     [string] $jobName
    )

    [void][reflection.assembly]::LoadWithPartialName("Microsoft.SharePoint")

    $service = [Microsoft.SharePoint.Administration.SPWebService]::AdministrationService
    $webApps = $service.WebApplications

    foreach ($webApp in $service.WebApplications)
    {
         $job = $webApp.JobDefinitions | where { $_.Name -eq $jobName } 

         if ($job -ne $NULL)
         {
               $job.Execute([system.guid]::Empty)
               Write "Job Executed"
         }
         else
         {
               Write "Job Not Found"
         }
    }

    # END SCRIPT

    To run, pass in the name of the time job class.

     \OneTimeJob.ps1 "TeamEli.SharePoint.OneTimer"

     Hope this saves somebody some time.

    Eli

  • Restricting Available Templates in MOSS07

    Some companies have a list of corporate approved site templates that they only want users to be able to select when creating new sites.  One way that I found to restrict this is to create a Feature that uses the WSS OM to change the list of available template.  You could then “staple” this feature to templates that you want a restricted list of site templates on.

           public override void FeatureActivated(SPFeatureReceiverProperties properties)
            {
                SPWeb site = (SPWeb)properties.Feature.Parent;

                site.AllowUnsafeUpdates = true;

                // Standard Publishing Feature must be activated
                PublishingWeb publishWeb = PublishingWeb.GetPublishingWeb(site);
                publishWeb.Web.AllowUnsafeUpdates = true;

                // Create a collection to store selected SPWebTemplates
                Collection<SPWebTemplate> temp = new Collection<SPWebTemplate>();

                //Get all available templates for a given lanugage
                Microsoft.SharePoint.SPWebTemplateCollection templates = site.GetAvailableWebTemplates(site.Language);

                // Add specific templates to our collection
                temp.Add(templates["STS#0"]);
                temp.Add(templates["MPS#1"]);
                temp.Add(templates["MPS#2"]);

                // Update the templates that are available
                publishWeb.SetAvailableCrossLanguageWebTemplates(temp, false);
                publishWeb.Update();

                site.AllowUnsafeUpdates = false;
            }

  • Filtering Active Directory Accounts from People Picker

    Overview
    There are two locations in SharePoint where users can “find” other users in the system; either by performing People search using the search services or using the People Picker control. Results can be different from both locations because they are pulling from different data sources. The People Search pulls from the SharePoint Profile database, which is a list of all the users that were pulled in from Active Directory that matched a certain filter criteria. The people picker on the other hand pulls directly from Active Directory and because of this, the people picker typically return more results. This is due to the fact that it is grabbing users that are disabled, service accounts and users that don’t have email address (duplicate accounts in our AD structure due to multiple domains).

    Problem
    People picker is returning users that are disabled and service accounts. Also, due to our infrastructure, a number of users have multiple accounts across the domains but only one “main” account. The main account is always the account that has an associated email address; all other accounts don’t have email address.

    Resolution
    Using the stsadm tools, it is possible to set a custom Active Directory filter on the people picker control. This can be done with the following command.

    Stsadm –o setproperty
                –pn peoplepicker-searchadcustomfilter
                –pv -<LDAP filter>
                -url  <WebApplication url>

    Example Syntax:

    Stsadm –o setproperty
                 –pn peoplepicker-searchadcustomfilter
                 -pv “(|(&(mail=*)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))(objectcategory=group))”
                 -url http://myportal

    This example filters out accounts that don’t have email address or are disabled. Because security groups don’t always have email addresses, we did an OR statement to make sure they are still included. This command must be run against each web application that should have the custom filter applied to the People Picker.

    On a side note, it appears that the People Picker doesn’t just pull from Active Directory but also pulls information from the Site Collection list of users who have “hit” the site. This in my opinion is a bit annoying because accounts that have been disabled (people leaving the company) stick around. Figures.

    I thought I'd share this with everyone as this seems to be undocumented stsadm command.  I noticed it in the list of available properties to set using setproperty but couldn’t find a SINGLE thing on the web so we asked a Microsoft representative if he had any guidance.

    Note: The line breaks are for readability and should not be in the batch file or command line.

  • Modifying the Workflow Association Data in SharePoint 2007 Pre-Initiation

    One of our customers wanted to use the out-of-the-box Approval workflow but wanted to pre-fill the Approvers field with users from the items metadata.  In another words, each document in a document library can potentially have a difference approver so rather than have the Approvers field pre-filled from the initial Association data, they want it populated from the Approvers field on the initiated item.

    Initially, I started out modifying the out-of-the-box Approval InfoPath form (Review-RoutingInit.xsn) and was able to successfully populate the Approvers field with hardcoded data but I then realized that I didn’t know what Item the workflow had been started on, which meant I couldn’t figure out who the Approver was for the specific item that the workflow had been started.  So, I scrapped that idea and decided to create a new InfoPath Workflow initiation page (this is the page that loads the InfoPath Forms on initiation).

    After using Lutz Roeder’s . NET Reflector to analyze the IniWrkflIPPage.aspx page, I was able to determine that if I wanted to change the AssociationData before it was populated into the InfoPath Form Control, I needed to modify the property m_formData before the page finished its databinding.

    protected override void OnLoad(EventArgs ea)
    {  
        base.OnLoad(ea);
      
       
    /* Some code commented out */  
       
    this.m_assocTemplate = SPWorkflowAssociationCollection.GetAssociationForListItemById(this.m_listItem, associationId);
      
       
    this.m_formData = this.m_assocTemplate.AssociationData;  
    --> this.m_formData = “My modified form data”;   
       
    /* More code commented out */
      
       
    this.DataBind(true);
    }


    I then went on to modify the form data using metadata from the item that initiated the workflow.  Because I wanted to inherit my custom InfoPath initiation form from IniWrkflIPPage.aspx, I realized that I couldn’t change the code in the OnLoad event, but instead needed to change it on the DataBind event. 

    protected override void DataBind(bool raiseOnDataBinding)
    {   
       if (m_listItem["Approver"] != null)
       
      
    {
            
          
    // Load form data into an XmlDocument
            
          
    XmlDocument xmlDoc = new XmlDocument();
             
           
    xmlDoc.LoadXml(this.m_formData);
             
           // Defines the namespace manager to handle using the 'my' namespace
            
          
    XmlNamespaceManager manager = new XmlNamespaceManager(xmlDoc.NameTable);
            
          
    manager.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD");
             
          
    // Gets a collection of all the Users specificed in the Approver field (Requires Approver field on document library)
            
          
    SPFieldUserValueCollection users = new SPFieldUserValueCollection(m_listItem.Web, m_listItem["Approver"].ToString());
            
          
    string personXml = string.Empty;
             
          
    // Loops through each user and builds out a new Person element
             
          
    foreach (SPFieldUserValue user in users)
            
          
    {
                 
             
    personXml += "<my:Person><my:DisplayName>" + user.User.Name + "</my:DisplayName><my:AccountId>" + user.User.LoginName + "</my:AccountId><my:AccountType>User</my:AccountType></my:Person>";
             
           
    }
          // Selects the Reviewers (aka Approver) in the form data and sets the innerXml = to the collection of users we built        
         
    xmlDoc.SelectSingleNode("/my:myFields/my:Reviewers", manager).InnerXml = personXml;
              // overwrites the default association data with our newly modified data
             this.m_formData = xmlDoc.InnerXml;  
    }
    }

    As it turned out, apparently some of the global variables in the IniWrkflIPPage.aspx don’t “port” very well over to my custom page, so I ended up faking it a bit.  There is probably a better way to do this but at this point I didn’t care J

    using System;
    using System.Collections.Generic;
    using System.Security.Permissions;
    using Microsoft.SharePoint;
    using Microsoft.Office.InfoPath.Server.Controls; 

    namespace TeamEli.Sharepoint.Workflow.ApprovalWorkflow
    {
      [PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust"), PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
      public class ApprovalIniWrkflIP : Microsoft.Office.Workflow.IniWrkflIPPage
      {
            //// Fields
            protected new string m_listItemName;
            protected new string m_listItemUrl;
            protected new string m_workflowName;
            protected new XmlFormView XmlFormControl;
             //// Methods
            protected override void OnInit(EventArgs e)
            {
                base.XmlFormControl = this.XmlFormControl;
                base.m_workflowName = this.m_workflowName;
                base.m_listItemName = this.m_listItemName;
                base.m_listItemUrl = this.m_listItemUrl;
                base.OnInit(e);
            }
             protected override void OnLoad(EventArgs ea)
            {
                base.OnLoad(ea);
                this.XmlFormControl = base.XmlFormControl;
                this.m_listItemName = base.m_listItemName;
                this.m_workflowName = base.m_workflowName;
            }

             protected override void DataBind(bool raiseOnDataBinding)        {            //// See above…        }
      }
    }

    With the code above precompiled into a DLL, all I needed to was modify two lines of my copied IniWrkflIPPage.aspx and that was to add my assembly and to change the page inherits to our new codebehind page.

    <%@ Assembly Name="TeamEli.Sharepoint.Workflow.ApprovalWorkflow, Version=1.0.0.0, Culture=neutral, PublicKeyToken=<<PUBLICKEYTOKEN>>"%>
    <%@ Page Language="C#" Inherits="TeamEli.Sharepoint.Workflow.ApprovalWorkflow.ApprovalIniWrkflIP" MasterPageFile="~/_layouts/application.master"   EnableSessionState="true" AutoEventWireup="false"  %>

    All that was left was to create a new Feature that calls our new initiation page.  And volia!

    Hopefully some of this made sense.  If not, let me know.

    Eli

  • Adding a Sites Listing (& Setting Owner) from WSS Object Model

    In one of my current projects, in which I am programmatically adding a new SPListItem to the Sites list (created from the Site Directory Template), I couldn't figure out how to get the Owners name to save.

    /* Doesn't save the Owner information to the Sites list */
    SPListItem
    item = siteDirectoryList.Items.Add();

    item[item.Fields.GetFieldByInternalName("Owner").Id] = string.Format("{0};#{1}", user.ID.ToString(), user.Name);

    After a number of fruitless effects to try and set the Owner field properties, I decided to take a look at all the available fields that are part of the Sites List. The list below is all the fields that were returned for my instance of Sites. Your may contain more or less depending on how much you customize your Sites List.

    ContentTypeId        Title            _ModerationStatus        _ModerationComments        File_x0020_Type
    SiteTitle        SiteURL        Description            Owner                    Division
    Region            DivisionMulti        RegionMulti            BestBet                URL
    TopSite            TasksAndTools        OwnerNew            URLStatus                Manager
    ID            ContentType        Modified            Created                Author
    Editor            _HasCopyDestinations    _CopySource            owshiddenversion            WorkflowVersion
    _UIVersion        _UIVersionString    Attachments            Edit                    LinkTitleNoMenu
    LinkTitle        SelectTitle        InstanceID            Order                    GUID
    WorkflowInstanceID    FileRef            FileDirRef            Last_x0020_Modified            Created_x0020_Date
    FSObjType        PermMask        FileLeafRef            UniqueId                ProgId
    ScopeId        HTML_x0020_File_x0020_Type                _EditMenuTableStart            _EditMenuTableEnd
    LinkFilenameNoMenu    LinkFilename        DocIcon            ServerUrl                EncodedAbsUrl
    BaseName        MetaInfo        _Level                _IsCurrentVersion            TitlewURL
    TitlewMenu

    As you can see, there are many hidden fields that are used to keep the Sites List working. Needless to say, there are two fields relating to the Owner field; Owner & OwnerNew. So, I tried setting the OwnerNew field of my Sites list and it worked!! See sample code below…

    public static void AddSiteItem(string siteDirectoryURL, string title, string url, string desc)
    {

    /* Open up a SPWeb to the Site Directory */
    SPSite portalSite = new SPSite(siteDirectoryURL);
    SPWeb siteDirectory = portalSite.OpenWeb();

    /* Get the Sites List */
    SPList siteDirectoryList = siteDirectory.Lists["Sites"];
    portalSite.AllowUnsafeUpdates = true;

    /* Create a new item in memory */
    SPListItem item = siteDirectoryList.Items.Add();

    /* Grab current user to be used as the owner */
    SPUser user = SPContext.Current.Web.CurrentUser;

    /* Set default properties */
    item[item.Fields.GetFieldByInternalName("Title").Id] = title;
    item[item.Fields.GetFieldByInternalName("URL").Id] = url;
    item[item.Fields.GetFieldByInternalName("Description").Id] = desc;

    /* Set owner field using field OwnerNew vs. Owner */
    item[item.Fields.GetFieldByInternalName("OwnerNew").Id] = string.Format("{0};#{1}", user.ID.ToString(), user.Name);

    /* Update / Save item */
    item.Update();

    portalSite.AllowUnsafeUpdates = false;

    }

    Who would have thought? Anyways, hopefully if anyone else has to do this, this will help.

    As a side note, I am using item.Fields.GetFieldByInternalName because I have multiple languages that use the same pages and strangely enough only the Title field seemed to have a common GUID across each of the languages and you can't use item["Title"] because they don't always match across languages (meaning Title is spelled differently in French or Spanish) only InternalName seemed to be constant across the languages. I could have used the index value but that is prone to change and is just bad practice.

    Eli

     

  • Welcome - Repost

    Well, now that SharepointBlogs is back (Way to go Dustin!), I thought I'd repost some of my post.  (Not there was many to begin with :)) 

    Well, I guess it is time to stick my head out from behind the monitors and start blogging about some of the experiences I have encountered with SharePoint 2007. A number of SharePoint bloggers have helped me through some tough spots and I would like to return the favor to the SharePoint community. First of all, my name is Eli Van Eenwyk. I am located in the sunny city of Buffalo (NY) where 2 feet of snow in the middle of October isn't unheard of (Come on, admit it…you are jealous…) J

    As a side note, I want to explain why I named my blog TeamEli. It all started while working onsite at one of our clients and some of their employees (whom I was working with) coined my code the ".Eli Framework" (similar to the .NET framework). This framework is alleged to be able to do anything you tell it to do. If you want it to convert WebSphere to SharePoint with the click of a button, it was DONE. As it turns out, this framework was too much for one person to manage, so we had to establish a team of developers and managers so that we could keep this framework living up to expectations. Hence, the name TeamEli. I didn't want you to think that I thought so highly of myself that I had to name a blog after myself, although if that is what you want to believe, I can't stop you. Heck, this name is better that my old high school nickname…but we will leave that story for another time.

    P.S. If this blog post doesn't turn out correctly or even show up on the right site, blame it on Word 2007. We geeks always have to try out the new things (yah yah…old news).

    If anybody has comments on any of my blogs to come, please feel free to comment.

    Eli


Need SharePoint Training? Attend a SharePoint Bootcamp!

Posts (c) their respective authors. Everything else (c) 2007 SharePoint Experts