Correct Post Times and Delete in BlogEngine.NET using Windows Live Writer
When posting future posts using Windows Live Writer (WLW) the time was being changed from what I was expecting. My expected behavior is that specifying a date and time should be the same in both the BlogEngine.NET web interface and WLW. In other words, if I create a post to be published tomorrow at 1:15 PM in both interfaces, both posts should have the same posted date/time. This was not the case.
During testing, I found that I couldn't delete items using WLW. I would get an exception saying I'm "not authorized to delete the object." As both updates are in the same class, I decided to write them both in this post.
Correcting Updated Post Times for WLW
A brief discussion about time in BE.NET as it can be confusing. The Time Offset value setting, allows the server to change the display to your time (or any time zone you want). For instance, I am in GMT -7, my server is in GMT -5 and that makes my Time Offset -2. When I create a post in the BE.NET editor, the default time is my local time, even though the server is two hours ahead of me.
WLW converts my local time value to UTC and sends that to the server. New posts work properly, the problem occurs when updating a post using WLW. Here is the EditPost method in the BlogEngine.Core.API.MetaWeblog.MetaWeblogHandler class.
1: /// <summary>
2: /// metaWeblog.editPost
3: /// </summary>
4: /// <param name="postID">post guid in string format</param>
5: /// <param name="userName">login username</param>
6: /// <param name="password">login password</param>
7: /// <param name="sentPost">struct with post details</param>
8: /// <param name="publish">mark as published?</param>
9: /// <returns>1 if successful</returns>
10: internal bool EditPost(string postID, string userName, string password, MWAPost sentPost, bool publish)
11: {
12: ValidateRequest(userName, password);
13:
14: Post post = Post.GetPost(new Guid(postID));
15:
16: if (String.IsNullOrEmpty(sentPost.author))
17: post.Author = userName;
18: else
19: post.Author = sentPost.author;
20: post.Title = sentPost.title;
21: post.Content = sentPost.description;
22: post.IsPublished = publish;
23: post.Slug = sentPost.slug;
24: post.Description = sentPost.excerpt;
25:
26: if (sentPost.commentPolicy != "")
27: {
28: if (sentPost.commentPolicy == "1")
29: post.IsCommentsEnabled = true;
30: else
31: post.IsCommentsEnabled = false;
32: }
33: post.Categories.Clear();
34: foreach (string item in sentPost.categories)
35: {
36: Category cat;
37: if (LookupCategoryGuidByName(item, out cat))
38: post.Categories.Add(cat);
39: else
40: {
41: // Allowing new categories to be added. (This breaks spec, but is supported via WLW)
42: Category newcat = new Category(item, "");
43: newcat.Save();
44: post.Categories.Add(newcat);
45: }
46: }
47: post.Tags.Clear();
48: foreach (string item in sentPost.tags)
49: {
50: if (item != null && item.Trim() != string.Empty)
51: post.Tags.Add(item);
52: }
53:
54: if (sentPost.postDate != new DateTime())
55: post.DateCreated = sentPost.postDate.AddHours(-BlogSettings.Instance.Timezone);
56:
57: post.Save();
58:
59: return true;
60: }
There is no need to subtract the Time Offset, as the time is in UTC. Line 55 was modified to this next code block.
1: post.DateCreated = sentPost.postDate;
Deleting from Windows Live Writer
As I mentioned, while testing I found that BE.NET would throw an exception when deleting a post using WLW. Turns out that deleting an item checks to see if the IsAuthenticated is true, but post creation just checks to see if your username and password are valid. Here is the ValidateRequest method found in the BlogEngine.Core.API.MetaWeblog.MetaWeblogHandler class.
1: /// <summary>
2: /// Checks username and password. Throws error if validation fails.
3: /// </summary>
4: /// <param name="userName"></param>
5: /// <param name="password"></param>
6: private void ValidateRequest(string userName, string password)
7: {
8: if (!Membership.ValidateUser(userName, password))
9: {
10: throw new MetaWeblogException("11", "User authentication failed");
11: }
12: }
IsAuthenticated uses Thread.CurrentPrincipal.IsAuthenticated, so that is what we need to populate. This code adds a principal for the current user and their roles to the Thread. Insert the following code at line 12.
1: else
2: Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(userName), Roles.GetRolesForUser(userName));
3: }
License and Code
The code in this post is released under the Microsoft Reciprocal License, which is the license for BlogEngine.NET.