One of the enhancements that the HR team has been asking for since I first launched this is the ability to attach some of the files that applicants send to the applicants once they are entered into the system. This way, when looking through applicants anyone can easily view their resume, cover letter, or project (yes, sometimes there are projects!) as well as add a rating value to the project. Seeing as we have a RadUpload control for Silverlight, I figured this would be a great opportunity to get that running in the application, especially since we have the new RadUploadDropPanel that makes it even easier to get files uploaded in your applications.
Since it always provides for the most *interesting* approach to things, we're first going to implement this in the MVVM version of the application and then we'll move over to the regular code-behind/event driven version for comparison sakes (that comes in the next post).
<!-- Note - I know from a previous comment on the last post someone asked for the full source code, but that isn't quite available yet. I'm doing a significant amount of refactoring and tweaking to make this app more streamlined and additionally to improve the user experience for my friends in HR, so putting up the code now would be like you walking into the repair shop when they have your engine in pieces 15 feet from your car. Trust me, it'll be worth it. :) -->
The first thing I realized in this quest is that I'd need to create folders to store these files, and to avoid any overwriting of similarly named files (you have no idea how many people send in CoverLetter.docx), we want folders unique to each applicant. The first try at this was to simply use the generated ApplicantID when creating an applicant, but something about that didn't sit quite right so instead we modified the model a tiny bit and added ApplicantGUID and a few other fields to cover things we like to look at:
With that set, we need to ensure that we are actually adding and using the new GUID for our applicants that our WCF RIA Service can use for creating user folders. Therefore our refactored AddApplicant method on ApplicantViewModel.cs looks like this:
public
void
AddCompleteEventHandler(Applicant anApplicant)
{
AddApplicant(anApplicant);
this
.MakeMeActive(
this
.regionManager,
"MainRegion"
,
"ApplicantsView"
);
}
public
void
AddCompleteNoCloseEventHandler(Applicant anApplicant)
{
AddApplicant(anApplicant);
}
public
void
AddApplicant(Applicant anApplicant)
{
if
(anApplicant ==
null
)
{
// do nothing, new applicant add cancelled
}
else
{
// This is necessary for applicant file uploads, since we need a unique directory per applicant
anApplicant.ApplicantGUID = Guid.NewGuid();
this
.context.Applicants.Add(anApplicant);
this
.context.SubmitChanges((s) =>
{
// First we notify the user that the applicant was added successfully
eventAggregator.GetEvent<NotifyUserEvent>().Publish(
"Applicant added successfully"
);
ActionHistory myAction =
new
ActionHistory();
myAction.ApplicantID = anApplicant.ApplicantID;
myAction.PostingID = anApplicant.PostingID;
myAction.Description = String.Format(
"New applicant {0} {1} has been added for position {2} by {3}"
, anApplicant.FirstName, anApplicant.LastName, anApplicant.PostingID.ToString(),
"default user"
);
myAction.TimeStamp = DateTime.Now;
eventAggregator.GetEvent<AddActionEvent>().Publish(myAction);
}
,
null
);
}
}
The astute reader will also notice that previously there was only AddCompleteEventHandler - to stick with the logic I already have, which I kind of like, I'm keeping the logic but moving it to an AddApplicant method and letting the event handlers (which are using the EventManager from Prism for some loose-coupling action) call this and additionally change the active view if necessary.
Getting back on track, having that GUID allows me to then create a unique folder for every new applicant to store their files. We take care of this in the WCF RIA Service itself when we perform the auto-magically created Insert operation. After the generated code handles the actual operation, we add this little bit to create a directory based on the application directory and ApplicantGUID as well as set a FileSystemAccessRule so that these files can be accessed. For the demo I am using Everyone, but this is where you would give people on your ActiveDirectory, etc., access:
// Create our strings, makes it easier to use them down the line
string
baseDirectory =
"C:/Projects/VS2010/Projects/TelerikRecruiterSilverlight/Recruiter/Recruit/Recruit.Web/"
;
string
subDirectory =
"HR/applicants/"
+ applicant.ApplicantGUID.ToString();
// Create a DirectoryInfo object for our base directory, allowing us to create a subdirectory
DirectoryInfo di =
new
DirectoryInfo(baseDirectory);
di.CreateSubdirectory(subDirectory);
// Create a new DirectoryInfo based on our subdirectory, then 'Allow' 'Everyone' access to 'Read' files there
DirectoryInfo subDI =
new
DirectoryInfo(baseDirectory + subDirectory);
DirectorySecurity subDISecurity = subDI.GetAccessControl();
subDISecurity.AddAccessRule(
new
FileSystemAccessRule(
"Everyone"
, FileSystemRights.Read, AccessControlType.Allow));
// Set this security access to our subdirectory, so we can then read files (aka, click a link to download/view)
subDI.SetAccessControl(subDISecurity);
So we've got an updated model, updated methods for adding an applicant, and the ability to create a directory based on all this goodness that we can utilize with RadUpload to attach files to applicants. Stay tuned for tomorrow, when we actually implement RadUpload in the MVVM application through a clever use of attached behaviors.
Evan Hutnick works as a Developer Evangelist for Telerik specializing in Silverlight and WPF in addition to being a Microsoft MVP for Silverlight. After years as a development enthusiast in .Net technologies, he has been able to excel in XAML development helping to provide samples and expertise in these cutting edge technologies. You can find him on Twitter @EvanHutnick.