views:

3447

answers:

5

All -

I am using WSS 3.0. Currently, HR will upload an employee's internal company resume to a document library on our site, but for privacy reasons, we must then restrict access to that document library, which forces users to then go through HR each time they want to update their resume.

My thought is to create a list with attachments enabled that allows users to only view/edit their own items, and then give HR permission to manage all entries. This works with the exception that HR will need to create the initial list item and attach the resume, which means the list item will be "created by {hr}" and not visible/editable by the end user whose resume is attached.

Any ideas on how I can either allow HR to modify the "created by" field on upload so end users will see and can edit their resume, or go about this in a different way?

Thanks!

+3  A: 

Why not just use a document library for the resumes? (instead of a list w/ attachments.) You can give HR full read/write to all documents within, and the owner of the resume will have contribute permissions to only their own resume.

Jason Watts
Jason, sure would work as well, but the issue is still how do I tell SP who owns the resume if HR is always the one to upload it first? (We can't count on users to add their own initial resumes, but we want them to be able to maintain them going forward.) In this scenario, SP would assume HR is the owner of all resumes, instead of individual users. Hence, I believe I still need a way to either modify the "created by" field or some other creative workaround...
Mark
You can hide the "created by" field, and add your own "People and Groups" column called something like "Resume Owner" or something similar. HR would just have to be dilligent about tagging the resumes with the names of the owners.
Jason Watts
Jason - Maybe I'm missing something...? I realize I can hide the Created By field and create my own custom field, but as far as I can tell, I can't assign permissions based on a custom column (e.g., create a new "People and Groups" column, and then an associated permission that says employees can only view/edit items where 'logged in name = custom column name'). Assuming I cannot assign these permissions, then I'm still stuck... I have no way of allowing users to *only* view/edit their resume. Please expound if I've missed something obvious!
Mark
You're getting closer. Hiding documents from a view doesn't actually restrict access to it. Access restriction has nothing to do with a field that contains their particular name. You'll need to use an "HR" group and give them full access to all content within the library. You'll then need a "Users" or "Resume Owners" group that is prohibited from access to the library. At that point, you can go item by item, and break permissions on each individual document, explicitly giving the resume owner access to their owned resume.
Jason Watts
Understood. Thank you for your patience. I was hoping there was a quick point/click way to just update the Created By and not have to set a custom permission for every single resume in our company, and then have to maintain it for every new one uploaded. (And since permissions info isn't always obvious, it just seems it creates for all kinds of potential issues of HR forgetting to create a permission for a user, etc.) Oh well. I'll look at the code below as well and see if that might suit our purposes. Bummer this use case isn't more easily handled.... thanks again for your help, though!
Mark
I agree completely, the security/permissioning model isn't well suited for allowing access to content at the item level. You just gave me a great idea for a feature however!
Jason Watts
A: 

With a custom upload screen you could change the context of the current user before doing the upload. It requires looking up the user token using something like the following (these are snippets of working code with error handling and other stuff removed). Note that EnsureUser will require that the current user basically be an admin/owner.

using (SPSite site = GetImpersonatedSite(runAsUser))
{
    using (SPWeb web = site.OpenWeb())
    {
        // Do stuff here
    }
}

private SPSite GetImpersonatedSite(string username)
{
    user = SPContext.Current.Web.EnsureUser(username);
    SPSite site = new SPSite(SPContext.Current.Web.Url, user.UserToken);
    return site;
}
Kirk Liemohn
The current user is always going to be HR, since they'll be uploading the resumes
Jason Watts
A: 

I had a similar situation (migration into sharepoint) where I had to add a file with the admin user to a doc library and then "change" the users. I did it like this, might help you somewhat:

using (var root = site.RootWeb)
 {
   var users = root.SiteUsers;
   var user = users["domain\username"];
   file.Item[SPBuiltInFieldId.Created_x0020_By] = user.ID;
   file.Item[SPBuiltInFieldId.Modified_x0020_By] = user.ID;
   file.Item.UpdateOverwriteVersion();
Johan Leino
The usage of SPBuiltInFieldId.Modified_x0020_By is wrong here. The field contains the users LoginName value (string) and not the ID.
MrFox
Better use `file.Item[SPBuiltInFieldId.Editor] = user`. I had issues with `SPBuiltInFieldId.Modified_x0020_By`.
MrFox
+3  A: 

Create a document library to hold the resume's. Then give the HR department (SharePoint User group) "read / write all" permissions on the library, the give everyone else read / write your own" rights. Create a Content Type called "Resume" based on the out-of-the-box Document content type. Then add a field that contains the Employee (SPUser field) whom the resume concerns to the content type (and any other fields required, i.e. name, address etc.). Have HR fill this in correctly when creating the listitem (make the fields required).

Then, write a itemeventreceiver bound to the content type you just created and override the ItemUpdated event.

The code would be something like the following:

public override void ItemUpdated(SPItemEventProperties properties)
{
  SPSecurity.RunWithElevatedPrivileges(delegate
  {
    using (SPWeb web = properties.OpenWeb())
    {
       web.AllowUnsafeUpdates = true; 
       var item = web.Lists[properties.ListId].GetItemById(properties.ListItemId);
       if (item != null)
       {
         if (item.Fields.ContainsField("Employee"))
         {
           item["Author"] = item["Employee"]; 
           // Author is the internal name of the Created by field, 
           // always use Internal Names!
           DisableEventFiring();
           item.SystemUpdate();
           EnableEventFiring();
         }
       }
     }
   });
}

you can bind the ItemEventReceiver using a FeatureReceiver to the content type like so:

SPContentType docCt = web.ContentTypes[new SPContentTypeId("CONTENTYPE ID GOES HERE")];
docCt.EventReceivers.Add(SPEventReceiverType.ItemUpdated, "ASSEMBLYNAME, Version=1.0.0.0, Culture=neutral, PublicKeyToken=TOKEN", "FULLY QUALIFIED CLASSNAME");
docCt.Update();
Colin

related questions