Tudor Hofnar
Tudor Hofnar

Reputation: 267

Changing title of documents being uploaded - Event Receiver

Ok so I have a request from a user and I want to achieve it using and Event Receiver.

Basically what she wants is to have a Document Library where users can upload documents. Each document will have a file name like this "UserA - Customization.docx". Now you can imagine that a user can upload many documents that will have an identical name so what we want to do is number the files automatically. So if if UserA uploads the first document, SharePoint will add a number after the name so the file will be called "UserA - Customization - 1.docx" and then he uploads a second document and it will be called "UserA - Customization - 2.docx".

However, now if UserB wants to upload his first document it must be numbered "UserB - Customization - 1.docx" so basically the counter needs to restart if its a new documet and continue from the highest number if the document name already exists.

So basically SharePoint needs to check if the name of the current document exists in the list and if it does add a number next to it, but the number must be 1 greater than the highest document so it will just increase.

Is there any way to do this? Any ideas?

This is what I have come up with so far for simply changing the name of the file adding "-xx" to the filename but this is not working.

public override void ItemAdded(SPItemEventProperties properties) 
{ 
    SPFile spf = properties.ListItem.File; 
    string url = properties.AfterUrl; 

    int positionOfSlash = url.LastIndexOf("/"); 
    string pathBeforeFileName = url.Substring(0, positionOfSlash); 
    string newFileName = createNewFileName(url.Substring(positionOfSlash)); 

    string myNewUrl = pathBeforeFileName + newFileName; 

    DisableEventFiring(); 
    spf.MoveTo(myNewUrl); 

    spf.Update(); 
    EnableEventFiring(); 
} 
static string createNewFileName(string oldFileName) 
{ 
    int positionOfPeriod = oldFileName.LastIndexOf("."); 

    string fileName = oldFileName.Substring(0, positionOfPeriod); 
    string fileExtension = oldFileName.Substring(positionOfPeriod); 

    string newFileName = fileName + "-xx" + fileExtension; 

    return newFileName; 
}

Where am I going wrong with this code? Thanks for any help!

EDIT: this is the code I am using in the console app in Visual Studio to attach the EventReceiver to the Document Library.

using (SPSite site = new SPSite("http://servername:port/subsite1/subsite2/"))
        {

            using (SPWeb web = site.OpenWeb())
            {
                SPList list = web.Lists["My Doc Library"];

                SPEventReceiverDefinition def = list.EventReceivers.Add();
                def.Assembly = "DocumentLibrary_ClassLib, Version=1.0.0.0, Culture=Neutral, PublicKeyToken=611205b34d18f14d";
                def.Class = "DocumentLibrary_ClassLib.EventReceiver";
                def.Type = SPEventReceiverType.ItemAdded;
                def.Update();
            }
        }

EDIT #2: Ok how about something like this?

//this will get just the name of the file without the extension and I will send that to the 
//query builder which will count how many files there are with that name and return
int positionOfPeriod = oldFileName.LastIndexOf(".");
string tempFileName = oldFileName.Substring(0, positionOfPeriod);

SPQuery query = BuildArbitraryQuery(properties.List, "Name", tempFileName, true);

But now I don't really understand the query in the BuildArbitraryQuery, how can I change this to give me the desired behavior? (sorry if its a total noob question but I've never dealt with C# and EventReceivers before)
- Well after looking at BuildArbitraryQuery for a while I think I made some sense of it, basically I don't need to change anything? Since it receives the name of the file and the name of the column as a parameter it should be fine right?

Also since items within the list will be something like ClientA Request - 3.docx and I am sending the filename to the BuildArbitraryQuery will be able to find part matches rather than the full match. So for example if the filename BuildArbitraryQuery receives is ClientA Request.docx will it be able to find all other requests from that ClientA? So would ClientA Request - 1.docx, ClientA Request - 2.docx all be included in the calculation?

Upvotes: 1

Views: 10052

Answers (1)

Robert Christ
Robert Christ

Reputation: 2190

Here you go, tested it and it works.

/// <summary>
   /// An item was added.
   /// </summary>
   public override void ItemAdded(SPItemEventProperties properties)
   {
       base.ItemAdded(properties);
       SPListItem item = properties.ListItem;

       if (item["Name"] == null)
           return; //or better yet, log

       string oldFileName = item["Name"].ToString();

       SPQuery query = BuildArbitraryQuery(properties.List, "Created By", properties.UserDisplayName, true);

       int count = properties.List.GetItems(query).Count;

       int positionOfPeriod = oldFileName.LastIndexOf(".");
       if (positionOfPeriod == -1)
       {
            fileName = oldFileName;
            fileExtension = "";
        }
       else
       {
             fileName = oldFileName.Substring(0, positionOfPeriod);
             fileExtension = oldFileName.Substring(positionOfPeriod);
        }

       string newFileName = fileName + "-xx" + count.ToString() + fileExtension;

       item["Name"] = newFileName;

       try
       {
           properties.Web.AllowUnsafeUpdates = true;
           EventFiringEnabled = false;

           item.Update();
       }
       finally
       {
           properties.Web.AllowUnsafeUpdates = false;
           EventFiringEnabled = true;
       }
   }

   /// <summary>
   /// Builds an arbitrary SPQuery which filters by a single column value.
   /// </summary>
   /// <param name="list">The list you will run the query against.</param>
   /// <param name="columnDisplayName">The Display Name of the column you want to filter.</param>
   /// <param name="value">The value to filter against.</param>
   /// <returns>A new SPQuery object ready to run against the list.</returns>
   public static SPQuery BuildArbitraryQuery(SPList list, string columnDisplayName, string value, bool deepSearch)
   {
       if (list == null)
           throw new ArgumentNullException("You cannot pass a null list to Helper.BuildArbitraryQuery.");

       if (!list.Fields.ContainsField(columnDisplayName))
           throw new ArgumentException("The SharePoint List \"" + list.Title + "\" does not contain the Field \"" + columnDisplayName + "\".");

       string internalName = list.Fields[columnDisplayName].InternalName;
       SPQuery query = new SPQuery();
       query.Query = "<Where><Eq><FieldRef Name=\"" + internalName + "\"/><Value Type=\"Text\">" + value + "</Value></Eq></Where>";

       if (deepSearch)
           query.ViewAttributes += "Scope='RecursiveAll'";

       return query;
   }

Upvotes: 2

Related Questions