Transition to Quark Theme

I have been working with the Revera theme for about 4 months now I have discovered that the theme does not work for me as well as I would have liked.  This is mostly due to the main page slider and needing a reasonably sized images, where ideally they are all the same to prevent annoying page movements, and since I’m not running a photo based blog the theme does not make sense for me.

I have been casually looking around for a replacement theme that contained a clean look, is responsive, and customizable.  I came across Quark and like how the theme is built upon HTML5 and CSS3 and according to some of the reviews the code isn’t bloated and the theme is easy to customize, which I am going to venture into over the next week.

I have already started some basic, but key, code level modifications by adding a modification of the default full page template to include one that does not allow for comments as I do not want to allow users to comment on pages.   The one thing this theme doesn’t have that I need is a portfolio section, which leads me into the key elements I need to complete to get this theme working for me:

  • Portfolio template to allow for a section to showcase my applications
  • Full page template without comments (done)
  • Front page and footer widget areas the theme allows you to set
  • Front page display in general
  • Logo for my website
    • Main logo with website name and potentially caption
    • Favicon

I think this theme has the potential to work nicely for me so long as a theme update does not break my code level customizations which at this point appears to be the addition of two new page templates.  I am going to do further reading on the theme to learn more about what Quark has to offer.  The theme does not allow you to bookmark posts based on the short links (with the ?p=#) so I may need to find a way to add that too, but just encase I made the modification now to change the permalink structure for blog posts to /blog/%postname%/, which unfortunately will break any current links that are not short links.

Separation of Concerns: View Model & Database Model

I noticed how when I used scaffolding to generate my MVC5 controller with views using Entity Framework this would only allow me to specify a model representing a table in my database, which is not what I want.  In order to have true separation of concerns I wanted to:

  • Use view models for my views that get validated prior to any database updates
  • Use database models, auto generated, to represent my database and contain the results of queries

I accomplished this by implementing various services which handle the communication with the database.  The controller contains an instance of the service it requires to communicate with the database.  The service queries the database and returns the results to the controller, which then sends the information to the view utilizing the appropriate format.

When querying (performing  select) the database:

  • Resulting database model(s) are converted to the corresponding view model(s), and
  • Returned to the controller, where
  • They are passed through to the view for display

When updating an entry:

  • View Model is passed from the view to the controller
  • Validated using an appropriate validator, I built with Fluent Validation
  • If invalid, error message is returned to the view and displayed
  • If valid, view model is passed to the service to update the corresponding tables in the database
  • In either case if errors occur then the model state is updated and the default view for the action is returned

An example of this is how I manage reading lists.  I have my reading list controller (ReadingListController class) which handles user interactions and manipulates the model.  You can see below that the controller class contains minimal logic and utilizes helper classes to perform the logic. A validator class is used for validating data being passed from the view, when creating/editing a reading list, using Fluent Validation.  This ensures valid data is being entered into the database rather than relying on exceptions being returned on insert/updating of the database due to ill formatted values.

To provide separation of concerns services are used to interact with the database model.  Utilizing a service also means that the controller does not need to know the implementation of the service and how to interact with the database so by extending the particular service interface you can change the implementation, i.e. if I decided to not utilize the Entity Framework anymore and instead use NHibernate.

In the case of reading lists two services are used, the reading list service and the author service. The author service is utilized here so that we do not duplicate code for retrieving authors who have books and specific books for that author based on selection.  I am still working on the view implementation of this.  The reading list service contains the implementation for retrieving:

  • List of reading lists for a particular user (List title, number of novels in the list)
  • A specific reading list (for editing/deleting)
  • Book information for a book in a particular reading list
  • Details for a reading list (Book titles, year, whether they are read, and date finished)

Also an implementation for editing, creating, and deleting a reading list.

[Authorize]
public class ReadingListController : Controller
{
	private readonly IReadingListService _service;
	private readonly IAuthorService _aService;
 
	public ReadingListController() : this(new ReadingListService()) { }
 
	public ReadingListController(IReadingListService service)
	{
		_service = service;
	}
 
	// GET: ReadingList
	public ActionResult Index()
	{
		int userId = GetUserId();
 
		var readingLists = _service.GetReadingListsForUser(userId);
 
		if(readingLists == null)
		{
			ModelState.AddModelError("", "An error occurred in retrieving your reading lists.");
			return View();
		}
 
		return View(readingLists);
	}
 
	// GET: ReadingList/Details/5
	public ActionResult Details(int? id)
	{
		if (id == null)
		{
			return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
		}
 
		var readingListDetails = _service.GetReadingListDetails(id ?? 0);
 
		if (readingListDetails == null)
		{
			return HttpNotFound();
		}
 
		return View(readingListDetails);
	}
 
	// GET: ReadingList/Create
	public ActionResult Create()
	{
		return View();
	}
 
	// POST: ReadingList/Create
	// To protect from overposting attacks, please enable the specific properties you want to bind to, for 
	// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
	[HttpPost]
	[ValidateAntiForgeryToken]
	public ActionResult Create([Bind(Include = "ListTitle")] ReadingListModel readingListModel)
	{
		readingListModel.UserId = GetUserId();
		var rlValidator = new ReadingListValidator();
		var results = rlValidator.Validate(readingListModel);
 
		if (results.IsValid)
		{
			var success = _service.CreateReadingList(readingListModel);
 
			if (!success)
				return RedirectToAction("Failed", new FailedModel { Message = "Unable to create reading list, please try again.", Action = "Index" });
 
			return RedirectToAction("Index");
		}
 
		return View(readingListModel);
	}
 
	public ActionResult Failed(FailedModel fm)
	{
		return View(fm);
	}
 
	// GET: ReadingList/Edit/5
	public ActionResult Edit(int? id)
	{
		if (id == null)
		{
			return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
		}
 
		var readingList = _service.GetReadingList((int)id);
 
		if(readingList != null)
			readingList.UserId = GetUserId();
 
		//ViewBag.UserID = new SelectList(db.Users, "UserID", "Username", readingList.UserID);
		return View(readingList);
	}
 
	// POST: ReadingList/Edit/5
	// To protect from overposting attacks, please enable the specific properties you want to bind to, for 
	// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
	[HttpPost]
	[ValidateAntiForgeryToken]
	public ActionResult Edit([Bind(Include = "ReadingListID,ListTitle,UserID")] ReadingListModel readingListModel)
	{
		var rlValidator = new ReadingListValidator();
		var results = rlValidator.Validate(readingListModel);
 
		if (results.IsValid)
		{
			var success = _service.EditReadingList(readingListModel);
 
			if (!success)
				return RedirectToAction("Failed", new FailedModel { Message = "Unable to edit reading list, please try again.", Action = "Index" });
 
			return RedirectToAction("Index");
		}
 
		return View(readingListModel);
	}
 
	// GET: ReadingList/Delete/5
	public ActionResult Delete(int? id)
	{
		if (id == null)
		{
			return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
		}
 
		var rlm = _service.GetReadingList((int)id);
 
		if (rlm == null)
		{
			return HttpNotFound();
		}
 
		return View(rlm);
	}
 
	// POST: ReadingList/Delete/5
	[HttpPost, ActionName("Delete")]
	[ValidateAntiForgeryToken]
	public ActionResult DeleteConfirmed(int id)
	{
		_service.DeleteReadingList(id);
		return RedirectToAction("Index");
	}
 
	//Get: ReadingList/DeleteBook/5/4
	public ActionResult DeleteBook(int? readingListId, int? bookId, string bookTitle)
	{
		if (readingListId == null || bookId == null)
		{
			return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
		}
 
		var bm = _service.GetReadingListBookInfo((int)bookId, (int)readingListId);
 
		if (bm == null)
		{
			return HttpNotFound();
		}
 
		return View(bm);
	}
 
	// POST: ReadingList/DeleteBook/5
	[HttpPost, ActionName("DeleteBook")]
	[ValidateAntiForgeryToken]
	public ActionResult DeleteBookConfirmed(int readingListId, int bookId)
	{
		_service.DeleteBookFromList(bookId, readingListId);
		return RedirectToAction("Details", new { id = readingListId });
	}
 
	// Get: ReadingLis/AddBook/5
	public ActionResult AddBook(int? readingListId, string rlTitle)
	{
		if(readingListId == null)
		{
			return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
		}
 
		var rlam = new ReadingListAuthorsModel
		{
			Authors = _aService.GetAuthorsWithBooks().ToList(),
			ListTitle = rlTitle,
			ReadingListId = (int)readingListId
		};
 
		return View(rlam);
	}
 
	public ActionResult Books(int authorId)
	{
		var books = _aService.GetBooksForAuthor(authorId);
 
		return Json(books, JsonRequestBehavior.AllowGet);
	}
 
	//Get: ReadingList/MoveBook/5/4
	public ActionResult MoveBook(int? readingListId, int? bookId, string bookTitle)
	{
		if (readingListId == null || bookId == null)
		{
			return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
		}
 
		var bm = _service.GetReadingListBookInfo((int)bookId, (int)readingListId);
		var usersReadingLists = _service.GetReadingListsForUser(GetUserId()).Where(x => x.ReadingListId != readingListId);  //Can't move book to itself
 
		if (bm == null || usersReadingLists == null)
		{
			return HttpNotFound();
		}
 
		bm.ReadingLists.ToList().AddRange(usersReadingLists);
 
		return View(bm);
	}
 
	private int GetUserId()
	{
		int userId;
		userId = int.TryParse(User.Identity.Name.Split(':')[0], out userId) ? userId : 0;
		return userId;
	}
}

Validation for creating a new reading list is shown below.  I chose this example since it is a very simplistic validator, to implement.  The only criteria is that the list title has to be 1-255 characters long and the returned result for the number of novels in a list should be greater than or equal to zero, the highlighted rows.

using FluentValidation;
using tracker.Models.View.ReadingList;
 
namespace tracker.Validators.Novels
{
    public class ReadingListValidator : AbstractValidator<ReadingListModel>
    {
        public ReadingListValidator()
        {
            RuleFor(a => a.ListTitle).Length(1, 255).WithMessage("List name needs to be between 1 and 255 characters.");            RuleFor(a => a.NumberOfNovels).GreaterThanOrEqualTo(0).WithMessage("Number of novels was less than 0, please contact admin.");        }
    }
}

In order to utilize my view model instead of the database model for each view I changed the @model parameter to be my view model instead of the database model.   This ensures that the model is populated within the database session prior to being passed back to the view.  An example is the details view for a reading list:

@model tracker.Models.View.ReadingList.ReadingListDetailsModel

Upgrade to WordPress 4.0 Failed through GoDaddy

WordPress & Godaddy

I got myself into an interesting predicament today.  I was in going to show my mom how to update the church website and saw that there was an update to WordPress 4.0 showing in the GoDaddy console.  I decided to do the update and then that is when things went sideways.

Upon the update finishing I went to the website site only to see a 500 Internal server error message, shown below:

I knew this was not a good sign, and preceded to investigate and my first thought after some Googling was to manually upload WordPress 4.0 files (all of them except the wp-content folder & wp-config file), so I downloaded the zip and preceded to do so.  Once this didn’t fix my issue and the site is for someone else I wanted to rectify the problem as quickly as I could so I contacted GoDaddy support.  Thanks to the patience of Spencer D. and his technical contact we were able to get to the bottom of things.

The first step was to create a web.config file (back up any current one, or in my case where you deleted it, create a new one) that contains the following:

<?xml version="1.0"
encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <httpErrors errorMode="Detailed"
existingResponse="PassThrough"
/>
    </system.webServer>
</configuration>

In my case I am using the IIS 8 Plesk version; however, you should be able to find the correct version for you in this GoDaddy’s Help article if you are using GoDaddy Windows hosting.

You upload this to the root folder for your particular domain and in my specific case this led to me receiving the following when I navigated to the website, stlukesdryden.com, after waiting for a bit:

All-in-One Event Calendar: require(G:\PleskVhosts\dwcryan.com\stlukesdryden.com\wp-content\plugins\all-in-one-event-calendar\vendor\lessphp\lessc.inc.php): failed to open stream: No such file or directory @ G:\PleskVhosts\dwcryan.com\stlukesdryden.com\wp-content\plugins\all-in-one-event-calendar\lib\bootstrap\loader.php:88 #2
All-in-One Event Calendar: require(G:\PleskVhosts\dwcryan.com\stlukesdryden.com\wp-content\plugins\all-in-one-event-calendar\vendor\lessphp\lessc.inc.php): failed to open stream: No such file or directory @ G:\PleskVhosts\dwcryan.com\stlukesdryden.com\wp-content\plugins\all-in-one-event-calendar\lib\bootstrap\loader.php:88 #2
PHP Fatal error: require(): Failed opening required ‘G:\PleskVhosts\dwcryan.com\stlukesdryden.com\wp-content\plugins\all-in-one-event-calendar\vendor\lessphp\lessc.inc.php’ (include_path=’.;.\includes;.\pear’) in G:\PleskVhosts\dwcryan.com\stlukesdryden.com\wp-content\plugins\all-in-one-event-calendar\lib\bootstrap\loader.php on line 88

I now knew that an error in the All-in-One Event Calendar was causing my site to not be displayed.  In order to disable the failing plugin you can apparently either rename the plugin folder located in ../wp-content/plugins or (the way I did it) you can access your database through PhpMyAdmin and search the wp_options table for active_plugins.  I then saw this for the active_plugins option:

32       active_plugins       a:4:{i:0;s:55:”all-in-one-event-calendar/all-in-on…       yes

Editing this row, and focusing on the options_value column you want to delete the plugin information for the message you saw when navigating to your site earlier in this post, which in my case is All-in-One Event Calendar, in bold below:

a:4:{i:0;s:55:”all-in-one-event-calendar/all-in-one-event-calendar.php”;i:1;s:36:”contact-form-7/wp-contact-form-7.php”;i:2;s:24:”wordpress-seo/wp-seo.php”;i:3;s:31:”wp-google-maps/wpGoogleMaps.php”;}

It is important to delete the entire contents from the i to the ; (semicolon) for the plugin in question.  Once I had done this I was able to access my website homepage, though not the login screen (which I rectified by copying over the wp-login.php file, it wasn’t allowing me to copy earlier, because as I later realized I had the page, giving me the error, open in my browser), but no other pages on the site.  I was able to solve the broken links/pages in the same fashion as when I broke them with my publish, you can find it at the bottom of this post.

Worth noting is that Spencer mentioned my WordPress installation was indeed at version 4.0, which jives with what the GoDaddy console under ‘Manage My Web Applications’ states and when I access the WordPress Dashboard for my site it states I am running WordPress 4.0 too, so I’m hoping all is good now.  Well, that is except the Event Page is blank as I have to get the event calendar plugin working, which involves getting the latest version installed.

This process took longer than expected and I felt it was worth blogging about.  Will make sure I have a fair bit of free time on my hands before attempting to upgrade WordPress on this domain.