Oxite: Blog Description & Profile Management
Following my Oxite blog post series, I add some extra functionality to the administration panel of the blog engine as well as some little changes to the blog engine title, shown on the front-end. Also I'll use this oportunity to give a little insight about Oxite's core and internal structure.
Let's vent first...
If you search and read reviews you may notice that Oxite it's not considered to be good at all. Actually is not referred as a Microsoft ASP .NET MVC Framework guidance project, not even a starter kit. Adding to that I'm absolutely sure when I say it's not even close to a "Best Practices & Patterns" repository for ASP .NET MVC Framework enthusiasts.
Instead of wasting time reinventing the wheel, by reviewing the code (again!) or the project as a whole. I'll just summarize what I think of it with a single sentence. Ready? Here it goes. Oxite is an unorthodox, undocumented and untested application made with good intentions. MVC is supposed to enforce the developer to come up with a good application design, but ironically this just makes Oxite a good anti-pattern example providing a high coupled/low cohesive design and incredible complex project structure due to unnecessary abstraction layers. Perhaps it's just because it's not a finished product and it was created in the early stages of a maturing technology. Who knows?!
I'll admit I like the challenge and that's precisely why I chose this as my starting point for my blog engine. I know the guys who created this have a disclaimer, telling me other "mature" choices, BUT I didn't expect to be abbandoned in this creepy box of moving sand! (Look for Orchard in codeplex).
And now... The features
So, if you follow other bloggers you may have noticed that they all have a funky/catchy phrase below the blog title. And if you watch closely, you may notice that the funky/catchy phrase is missing here. You may be thinking, so what? it's no biggie. And you might be right. But in the end, advertisement, which should be considered a science, proves you wrong! These phrases have a name! They are called slogans, and they have claimed to be the most effective means of drawing attention (according to wikipedia). But the most interesting thing is that a slogan implies distinction between products, and that's it! that's what we must always pursue when having a web page among millions and millions... DISTINCTION!
Jumping right into business, achieving this is fairly simple. The Site entity has a property called Description, which is accessible from the Model in the view. This is convenient since we are able to update the description already from the settings tab in the Admin section.
Then locate the header.ascx on your skin folder. Change the title div as follows:
<div id="title">
<h1><a href="<%=Url.Posts() %>"><%=Model.Site.DisplayName %></a></h1>
<%=Model.Site.Description %>
</div>
So that's it, nice and simple! Now we have a fancy catch phrase space. Which reminds me, now I need a slogan!
The next thing is something that really annoyed me. While browsing through the issues on oxite's codeplex page, I learned that in order to change the logged user's display name (in this case the admin, since oxite does not support user management yet ;) you had to go to the database and change it manually. I can't stand the greeting message when I log in, every time I read "Welcome, Oxite Administrator!" I get cranky and bored. So let's fix that and put our name there!
First we need to locate the routes dictionary class. It's a class file called OxiteRoutes.cs located on the Oxite project. This is how our new route looks.
AddRoute(
"UserChangeProfile",
"Admin/ChangeProfile",
new { controller = "User", action = "ChangeProfile", validateAntiForgeryToken = true },
null,
new { Namespaces = controllerNamespaces });
Cool, so let's take a look to what we did here. We defined a route name: "UserChangeProfile" and mapped an url to it: "Admin/ChangeProfile", according to our routing engine this url must be mapped to an action, which we defined on the object defaults attribute. We said that the action will be: "ChangeProfile" and that it's going to be located on the UserController.cs. The other parameters are tokens and other optional properties.
Oxite also has a Helper class for url generations from the views. It's called UrlHelperExtensions.cs and it's located on the Oxite.Mvc project inside of the Extensions folder. We should add this snippet there.
public static string UserChangeProfile(this UrlHelper urlHelper)
{
return urlHelper.RouteUrl("UserChangeProfile");
}
So now that we are done with the routing, we need to change the EditUser method on the service that is going to be used by the controller. I just modified the signature of the EditUser method interface to look like this:
void EditUser(User user, bool passwordUpdate, out ValidationStateDictionary validationState);
I added a small modification to the method implementation, so we don't modify the user password when change the user full name. The implementation looks like this:
public void EditUser(User user, bool passwordUpdate, out ValidationStateDictionary validationState)
{
validationState = new ValidationStateDictionary();
validationState.Add(typeof(User), validator.Validate(user));
if (!validationState.IsValid)
{
return;
}
if (!string.IsNullOrEmpty(user.Password) && passwordUpdate)
{
user.PasswordSalt = Guid.NewGuid().ToString("N");
user.Password = saltAndHash(user.Password, user.PasswordSalt);
}
user.HashedEmail = user.Email.ComputeHash();
repository.Save(user);
}
Ok, so we only need to do two more things and we are done. First we need to add the controller action definition that is going to be called by the routing engine, and second we need to create the view that is going to be rendered by this action.
First let's edit the UserController. Let's add our actions for HTTP verbs Get/Post.
[ActionName("ChangeProfile"), AcceptVerbs(HttpVerbs.Post)]
public virtual object ChangeProfileSave(User currentUser, FormCollection form)
{
string name = form["displayName"];
string email = form["email"];
ValidationStateDictionary validationState;
if (string.IsNullOrEmpty(name))
{
ModelState.AddModelError("displayName", "New Name is not set.");
}
if (string.IsNullOrEmpty(email))
{
ModelState.AddModelError("email", "New Email is not set.");
}
if (ModelState.IsValid)
{
currentUser.DisplayName = name;
currentUser.Email = email;
userService.EditUser(currentUser, false, out validationState);
if (!validationState.IsValid)
{
ModelState.AddModelErrors(validationState);
}
}
if (!ModelState.IsValid)
{
ModelState.SetModelValue("displayName", new ValueProviderResult(name, name, CultureInfo.CurrentCulture));
ModelState.SetModelValue("email", new ValueProviderResult(email, email, CultureInfo.CurrentCulture));
return ChangeProfile(currentUser);
}
return Redirect(Url.AppPath(Url.ManageUsers()));
}
Finally, let's add our view code.
<%@ Page Language="C#" AutoEventWireup="true" MasterPageFile="~/Views/Shared/Admin.master" Inherits="System.Web.Mvc.ViewPage>" %> <%@ Import Namespace="Oxite.Mvc.Extensions" %> <asp:Content ContentPlaceHolderID="MainContent" runat="server"> <h2 class="title"><%=Model.Localize("ChangeProfile", "Change Profile") %></h2> <%=Html.ValidationSummary() %> <form action="" method="post"> <div> <%=Html.TextBox("displayName", m => m.User != null ? m.User.DisplayName : "", Model.Localize("DisplayName", "Display Name"), new { size = 42, @class = "text" })%> </div> <div> <%=Html.TextBox("email", m => m.User != null ? m.User.Email : "", Model.Localize("Email", "Email"), new { size = 42, @class = "text" })%> </div> <div> <input type="submit" name="submit" class="submit button" value="<%=Model.Localize("ChangeProfile", "Change Profile") %>" /> <%=Html.OxiteAntiForgeryToken(m => m.AntiForgeryToken) %> </div> </form> </asp:Content> <asp:Content ContentPlaceHolderID="Scripts" runat="server"> <% Html.RenderScriptTag("site.js"); %> </asp:Content>
That's it ladies and gentleman! Now we are able to modify our user's email and display name.
UPDATE!
After making these changes, I noticed that something else had to be done to get a complete solution to the DisplayName fix. After applying the updates above we successfully update the DisplayName to whatever we want, but Admin (which is an username) is still being shown on the comments made by the me, instead of MY NAME. So in order to achieve this we only have to make a mall update to the Comments.ascx file as follows.
Replace the line of the name paragraph inside of the comment div with this one:
Mr. <strong><%=Html.LinkOrDefault((Model.PartialModel.Child.Creator.Name.ToLower() == "admin") ?
Model.PartialModel.Child.Creator.DisplayName : Model.PartialModel.Child.Creator.Name.CleanText(),
Model.PartialModel.Child.Creator.Url.CleanHref())%></strong>
0 Comments