Love Your Code

Me and Code, we had something special. Like all new relationships, this one started out with the best of intentions. Although, I suppose we both knew it couldn’t last forever.

Looking back I realize that I didn’t support Code the way I should have. The kind of support that could only be fulfilled by unit tests. I got so busy and felt like there just wasn’t enough time. It didn’t take long for things got messy, as things with source code tend to do. Code would be telling me one thing, but doing another. Randomly, Code would throw a NullReferenceException and I’d have no idea why. The API was fragmented, making it difficult to communicate.

I had a feeling this it was my fault. Red, Green, Refactor – I knew the rules but by the end I only saw red. I wanted to help change Code but I couldn’t. A change here caused a problem there. Each fix just made Code angrier with me. Eventually we broke up and I started over. What else could we do?

Now I give my source code all the tests that it can handle. This relationship I’m in now was test-driven, so it’s exactly what we both need. There’s an occasional spat, but the core is strong and it’s easy to get things back on track. It’s been three years since I started up with Code 2 was and things are still going strong.

Happy Valentine’s Day, everyone! Be sure to give your code some love.

We’re Officially Booked!

A New Name

booked

It’s been many months since the last phpScheduleIt release and a lot has happened since then. The biggest change is that I decided to rename the project to Booked. I’ve never been completely happy with the previous name and received a lot of feedback about it. I landed on Booked because it succinctly gives you and idea of what the product is and it’s easy to remember.

A New Company

TTS17

The next big change is the formation of Twinkle Toes Software. Words typed here on this blog cannot express how excited I am to finally make this announcement. I’m an entrepreneur at heart and have wanted to start my own business for a while. Booked gives me an incredible opportunity to bring that dream to life. We’ll be offering professional services such as support, custom development, and hosting. We’re here to stand behind Booked and help it grow. I also have to thank my good friend Mandy for helping me with the website design and logo.

There is plenty more to come including the launch of booked website, the announcement of our hosting partner and some great new features and enhancements to the Booked community.

New Features

Oh, and I forgot to mention that there are a ton of great new features with this release! Here’s what you’re going to get with Booked 2.5. You can download it here

  • Application renamed from phpScheduleIt to Booked Scheduler (why?)
  • Added ability to reserve resource groups
  • Added ability to filter schedule resources
  • Added ability to specify resource type
  • Added enhanced resource status management
  • Added ability to specify buffer time between reservations (per resource)
  • Custom attributes now appear on all reservation emails and balloons
  • Added ability set custom attributes for an individual resource, user or resource type
  • Added ability manage config files for all plugins through the UI
  • Added ability to set reservation colors per user
  • Added ability to subscribe to reservation Atom feeds
  • Added ability update blackouts
  • Added ability attach multiple items to a reservation
  • Added Shibboleth authentication plugin (thank you to the folks at UCSF)
  • Added ability to email admin for all new account creations
  • Updates and cleanup on the API
  • Removed password regex setting in favor of password complexity settings
  • Changed schedule drop downs to exclude schedules if the user does not have permission to any of the resources belonging to it
  • Added wide and condensed booking page views
  • Added option to allow all users access to reports
  • Added setting for default ‘from’ email address
  • Changed the reservation page to default to the minimum resource reservation time
  • Changed reservation update to grant permissions to all users if auto-assign permissions is being turned on
  • Fixed showing ‘Private’ when the current user is the reservation owner
  • Fixed bug where recurring reservations across daylight savings time boundaries were not being updated to the correct time
  • Fixed bug where schedule would freeze on certain daylight savings boundaries
  • Fixed pagination bug on manage reservations page
  • Fixed bug allowing invitees to join a reservation that was already at capacity
  • Fixed bug not enforcing resource cross day reservation constraint
  • Fixed bug where quota rules were being enforced cumulatively for resources on a schedule
  • Fixed bug where reminders were being sent for deleted reservations
  • Updated all mysql_* calls to mysqli_*
  • Numerous other minor fixes and updates

Avoiding the Temptation of Record and Playback Acceptance Tests

I’m sold hook, line and sinker on the value of automated testing. Unit tests, integration tests, acceptance tests – I love em all. I joined a new company a few months back (more on that in a later post) and I’ve had the opportunity to get back into some acceptance testing using Selenium/WebDriver.

The allure of record and playback

Selenium makes it trivial to create web tests using the open source IDE. Hit record, click around, then play it back to verify that your web application works as expected. It’s so easy, in fact, that you barely have to think about what you’re actually testing. To be fair, if you’re doing this then you’re doing more automated testing than a lot of teams have. And it may be enough for simple smoke tests. There are a number of reasons why I avoid this, though.

The problems with record and playback

Ok, I admit that it’s incredibly fast to get a test up and running with record and playback. Unfortunately, writing tests is only half the battle. Your tests evolve along with your codebase. Take a look at the selectors that the IDE uses to locate elements to click and interact with. If you were to come back to this test in a month would you understand it’s intent? If you add a new element – or worse a whole new feature – to your application can you easily find a seam in your tests to inject the new verification? It is unexpectedly difficult to maintain tests that were set up through a UI recorder.

An alternative approach

I don’t always test through the UI, but when I do, I prefer Selenium WebDriver. WebDriver is an open source API for writing browser based tests. In my current role I’m using the Python driver (along with some some awesome other tools that I’ll get into soon), but there is support for a handful of other languages. Using the driver API, we can write tests with all of the power of your programming language of choice. We can perform intelligent element extractions and assertions. Tests become readable and maintainable.

We now have to think about what we’re testing, which is just as valuable as all of the technical benefits. If we have well written stories and specifications we can turn those specifications into acceptance test scenarios.

Imagine a story in the e-commerce space where the customer receives an automatic discount if they put at least $200 of products into their cart. This would be nearly impossible to understand as a record and playback test. But we can easily make this scenario crystal clear when writing the tests. Not only that, but we can clearly illustrate the edge case expectations, such as when the cart contains exactly $200 of products. Heck, we can use a variable and have a single place to make a change when the product owner decides that they want to bump the amount to $250.

Well layered tests will set you free

Using the WebDriver API is a huge leap forward over record and playback. But if we just have a long list of element selections, invocations and assertions, we’re not that much better off than we were. Your tests should be treated with the same respect as your production code. That means putting proper abstractions in place, naming things clearly, staying DRY and SOLID.

One of the most useful abstractions when testing your web application is the Page Object. The idea is to keep a clean separation of tests and HTML/page structure. This ends up making tests even more readable and maintainable by providing an API for your pages. No more hunting through test files when you make changes to your page’s DOM – everything is in a single place.

A well stocked tool belt

You can using WebDriver from your normal unit testing framework and it will work brilliantly. But there are many incredible supporting tools for acceptance testing and behavior driven development for all major languages. In Python, I’m using Behave and loving it – .NET teams can use SpecFlow or StoryQ, in Java there’s JBehave.

Behave uses the Gherkin natural language format. This allows us to describe the desired behavior of the application in a format that is easy to read and write by both business and technical folks. We then provide implementations for each of the steps in each scenario, using the API provided by our page objects to drive the tests and get values used in our assertions. Running the tests produces output that reads just like our stories which we can use to show our stakeholders exactly how the application behaves.

Try doing that with record and playback!

Extending phpScheduleIt : Getting started with the API

One of the big features we introduced in 2.4 was a RESTful API for integrating with phpScheduleIt. This is disabled by default but can be flipped on just by changing the config setting $conf['settings']['api']['enabled'] to 'true'. Once you do that, navigate to the auto-generated documentation page at http://your_phpscheduleit_root/Web/Services/index.php (if you’ve got mod_rewrite set up with Apache, you won’t need ‘index.php’) and take a look at everything that’s available.

Each service description contains some important information. You’ll see if the method is invoked via GET, POST or DELETE. You’ll also see:

Name  – This is the name of the service endpoint and the URL segment to use.
Description – A brief description of what the service does and any optional parameters.
Route – The full route and variables needed to invoke this endpoint. Any portion that begins with a : denotes a variable that must be provided. Pay attention to any trailing slashes – those are required when specified.
Response – An example JSON response (if applicable).
Request – An example JSON request (if applicable).

You’ll notice that all requests/responses are in JSON. In this post we’ll use jQuery to authenticate, list our reservations, and update a reservation. It’s important to remember that you cannot POST JSON data cross domain with JavaScript. So if you want to use this method you’ll need to run it from the same domain as your phpScheduleIt instance.

Authenticating

Just like the web front end, most of the services that the API exposes require an authenticated session. The documentation will note whether or not a service is secure or not. So step one will be to get a session token to use for subsequent calls.

Here’s our authentication fragment. It’s pretty simple, really. We POST the username and password to the Authentication/Authenticate service, then store the sessionToken and userId that we get back. We’ll just pop up and alert if the authenticate call fails.

 $.ajax(
{
	type: "POST",
	url: self.baseUrl + "Authentication/Authenticate",
	data: JSON.stringify({username: self.username(), password: self.password()}),
	dataType: "json"
})
.done(function (data)
{
	if (data.isAuthenticated)
	{
		self.token(data.sessionToken);
		self.userId(data.userId);
		self.headers = {"X-phpScheduleIt-SessionToken": data.sessionToken, "X-phpScheduleIt-UserId": data.userId}
		self.authenticated(true);
	}
	else
	{
		alert(data.message);
	}
});

Finding Our Reservations

Now that we have a session established we have all the rights and privileges of that user. We’ll start off by getting a list of our reservations. By default, this will get all of the reservations for the authenticated user in the next two weeks. This is a simple GET request to Reservations/. The important part is that we attach the headers to this request.

$.ajax(
{
	type: "GET",
	url: parent.baseUrl + "Reservations/",
	headers: parent.headers,
	dataType: "json"
})
.done(function (data)
{
	self.reservations.removeAll();

	$.each(data.reservations, function (idx, val)
	{
		self.reservations.push(new ReservationItemViewModel(val, parent));
	});
});

Updating A Reservation

Alright, that was almost too easy so far – let’s get into the nitty gritty. Create and update calls require a full object to be posted. To do that, we’ll first need to load the reservation and map it into a request object. This one is a little more involved, so I’ll walk you through it.

First, we make a GET request to Reservations/:referenceNumber with the reference number of the reservation that we want to update. Next, we walk through the response object and convert it into a request object. Most of this is straightforward. You may be asking why we’re calling $.map for the array objects. This is because the request object needs a different object structure than what what we currently have. So we’re just looping through each one and converting the item into the format needed for the request.

$.ajax(
{
	type: "GET",
	url: parent.baseUrl + "Reservations/" + reservationItem.referenceNumber(),
	headers: parent.headers,
	dataType: "json"
})
.done(function (data)
{
	existingReservation = data;
})
.then(function ()
{
	var request = {		
		accessories: $.map(existingReservation.accessories, function (n)
		{
			return {accessoryId: n.id, quantityRequested: n.quantityReserved };
		}),
		customAttributes: $.map(existingReservation.customAttributes, function (n)
		{
			return {attributeId: n.id, attributeValue: n.value};
		}),
		endDateTime: existingReservation.endDateTime,
		invitees: $.map(existingReservation.invitees, function (n)
		{
			return n.userId;
		}),
		participants: $.map(existingReservation.participants, function (n)
		{
			return n.userId;
		}),
		recurrenceRule: existingReservation.recurrenceRule,
		resourceId: existingReservation.resourceId,
		resources: $.map(existingReservation.resources, function (n)
		{
			return n.id;
		}),

		startDateTime: existingReservation.startDateTime,
		title: existingReservation.title,
		userId: existingReservation.userId,
		startReminder: existingReservation.startReminder,
		endReminder: existingReservation.endReminder
	};
	self.UpdateReservation(request, reservationItem);
}
);

We then make any changes we want (in this case, just updating the description) and issue the update. If we get an error during the update call we’ll display it. The error messages will be the same ones we get when we have a problem saving a reservation from the web. Expect to see messages like missing required fields, invalid dates, or conflicting reservations.

request.description = reservationItem.description();
$.ajax(
{
	type: "POST",
	url: parent.baseUrl + "Reservations/" + reservationItem.referenceNumber(),
	headers: parent.headers,
	dataType: "json",
	data: JSON.stringify(request)
}
)
.done(function (data)
{
	console.log("Update response message: " + data.message);

	self.LoadReservations();

}).fail(function (data)
{
	reservationItem.saveErrors(data.responseJSON.errors);
})

Putting It Together

Here’s the full example. Full Example Code – You’ll need to View Source and save this file locally.

Test Driven Development and Positive Reinforcement

I love writing code using test driven development. But this will not be a post about how to test drive your code. There are plenty of very good articles available and, of course, the Kent Beck book. I will reference the obligatory Wikipedia definition, though.

Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: first the developer writes an (initially failing) automated test case that defines a desired improvement or new function, then produces the minimum amount of code to pass that test, and finally refactors the new code to acceptable standards.

This will not be a post about how TDD improves code quality and design. These topics have been well covered. This post is much more personal.

Let’s walk through a typical TDD session: I write a test. I watch the test fail. I implement code. I watch the test pass.

At this point, I’m engaged and excited about moving on to the next test. I know that I’m on the right path and it feels good. I repeat the cycle in short intervals, each one leaving me increasingly more satisfied with what I’ve accomplished. Ballooning test counts give me a sense of how far I’m progressing. I often get into a state of flow and can lose myself in the code.

These micro goals and feedback loops have a fascinating way of reinforcing our behavior. Video game designers have been exploiting the power of positive reinforcement for years. Gamers keep playing in order to get that next win or next reward. With TDD, each passing test is a “win” and I want to keep going. I’m doubly motivated if I implement some code and the test is still failing. This is Albert Bandura’s self-efficacy – “the measure of one’s own ability to complete tasks and reach goals” – in action.

Research on game theory helps us understand why this works:

The flow we experience when playing a great game is a prime example of how we can condition other parts of our lives. Because of this, flow has become central to game theory. Good games that are responsive to player ability and game difficulty are framed as microcosms of optimal experience. They give the player a sense that their skills and abilities are adequate for coping with the challenges presented, and are based around “a goal- directed, rule-bound action system that provides clear clues as to how well one is performing”

TDD truly is about so much more than testing. The feedback loop inherent in TDD feeds us with positive reinforcement that our minds crave. The quality and design improvements that TDD lead to are nice, as well :)

“Common” Sense?

I’m sitting around, waiting for my hellfire chili to finish cooking and I came across this gem written by Ron Jeffries. If you’re involved in any way with software development, this is a phenomenal read.

His second paragraph starts off with this:

Most of us were taught to write down all our requirements at the very beginning of the project. There are only three things wrong with this: “requirements,” “the very beginning,” and “all.” At the very beginning, we know less about our project than we’ll ever know again. This is the worst possible moment to be making firm decisions about what we “require.”

And he follows that up with:

Then we demand that the developers “estimate” when they’ll be done with all this stuff. They, too, know less about this product than they ever will again, and they don’t understand most of these requirements very well.

To me, this is common sense. We don’t ask students to take an exam before learning the material. That’s not practical. Yet big requirements, long term projections, and unrealistic dates show up everywhere. Of course, the folks funding software projects want to know how much they need to invest and when they’ll get something for their investment. We’re not manufacturing a car, though. There’s no blueprint when we’re developing a new product. Why isn’t this common sense in software projects?

Tonight I wanted something spicy. The chili recipe I mentioned is like a set of requirements – this is how to make a spicy dish. I’ve never made this recipe before, so I decided to experiment with the heat level. I knew from previous experience that habanero peppers are hot. Really hot. In fact, six habaneros sounded like it may melt my face off. So I added what I thought I needed, measured the heat level, then adjusted.

I’ve posted before that I believe what we really want is to minimize the cost of change. When teams struggle to change direction, the tendency is to lock in requirements and delivery dates. If a simple change in requirements leads to a huge change in design, product teams will be instructed to define everything they want before development starts. “Changing requirements will cause us to miss our deadline” we’re told. Of course, as Ron points out, these requirements are rarely correct because they are defined when we know the least about what we’re building. Projects end up taking much longer than anyone wants and deliver features that nobody wants. So the lesson is that software developers need to make it simple to accommodate change.

Naturally, Ron is much more eloquent:

And you need to know how to build software that is soft enough, malleable enough, to grow smoothly as it needs to.

Working in short iterations frees you to change direction. To incorporate feedback. To learn. To build software that people want instead of software that visionaries think people want.

This simply makes sense to me.

And by the way, the chili was fantastic. It’s a deep burn, but delicious. If I made it exactly as the recipe stated I would have likely thrown the whole pot in the garbage and cooked up a frozen pizza.

What’s up with phpScheduleIt 2.4?

It’s been nearly 5 months since the 2.3 release of phpScheduleIt and I’m sure some are wondering what’s going on. Well, a lot of time has been spent working with BrickHost in setting up a cloud-based hosting solution for phpScheduleIt. This will be a turn-key solution with full support for a great price.

But development towards a 2.4 release is in progress! The main focus of 2.4 is a comprehensive public API. This will allow developers to easily integrate with phpScheduleIt and paves the way for mobile versions of the application. In addition, here are the other features that will be part of 2.4

GUI based configuration editor. No more need to open up the config file for basic changes.
config

Daily layouts. Full support for different schedule layouts for each day of the week.
daily

WordPress authentication plugin. Single sign-on between phpScheduleIt and WordPress.

I’m also planning to include reservation reminder emails and ReCAPTCHA support. With a little luck and a lot of free time, we should be shipping 2.4 soon.

Stay tuned!

Extending phpScheduleIt – Writing a Pre-Reservation Plugin

My previous post walked through configuring and enabling plugins in phpScheduleIt. That’s great… but what if we need something custom to our environment? The most popular support requests I get are for changing different workflows – especially around the reservation process. Today I’m going to dive into how to write plugins for the pre reservation events. If you know some PHP and a bit of object oriented design, this should be pretty straightforward.

Note: The version of phpScheduleIt at the time of writing is 2.2. While the exact details explained in this post may not apply to future versions, the core concepts will.

Getting Started

phpScheduleIt’s plugin architecture is largely convention-based. This means that a few rules apply to all plugins.

  • The plugin package structure must contain the following:
    • A directory named after the plugin, containing all plugin code.
    • A file within that directory named after the plugin.
    • If there is a configuration file needed, it must be named after the plugin, with ending with the suffix .config.php.
  • The plugin class name must must match the file name (without the .php suffix).
  • Plugins follow the decorator pattern.
    • The plugin must extend or inherit the base class or interface. It must also accept it as the first argument to the constructor (we’ll go into the different classes and interfaces available for each plugin type later).

For example, if we’re writing a PreReservation plugin named PreReservationExample, we would create a directory named PreReservationExample. Within it, we create a file named PreReservationExample.php. This file would contain a single class named PreReservationExample, which would extend the base plugin class and accept an instance of that class as the first constructor argument.

OK, enough with the abstract examples. Let’s get into some code.

Pre-Reservation Classes and Interfaces

The pre-reservation plugin allows you to do all sorts of fun things like custom validations, reservation adjustments, notifications and so on. The base class for PreReservation plugins is a PreReservationFactory. Let’s take a look at the interface.

interface IPreReservationFactory
{
	/**
	 * @param UserSession $userSession
	 * @return IReservationValidationService
	 */
	public function CreatePreAddService(UserSession $userSession);

	/**
	 * @param UserSession $userSession
	 * @return IReservationValidationService
	 */
	public function CreatePreUpdateService(UserSession $userSession);

	/**
	 * @param UserSession $userSession
	 * @return IReservationValidationService
	 */
	public function CreatePreDeleteService(UserSession $userSession);
}

Simple enough, but this gives us the power to hook into events before adding, updating, or deleting a reservation. The only argument to each of these functions is a UserSession, which gives you some insight into who is making each of these calls. Each one of these functions returns an instance of an IReservationValidationService. Let’s look at that interface.

interface IReservationValidationService
{
	/**
	 * @param ReservationSeries|ExistingReservationSeries $series
	 * @return IReservationValidationResult
	 */
	public function Validate($series);
}

Even easier! This has one function: Validate(), which accepts either a ReservationSeries (during add) or an ExistingReservationSeries (during update and delete). This is executed during the add/update/delete event and returns an instance of an IReservationValidationResult. Continuing down the path, we see that this interface is also pretty simple.

interface IReservationValidationResult
{
	/**
	 * @return bool
	 */
	public function CanBeSaved();

	/**
	 * @return array[int]string
	 */
	public function GetErrors();

	/**
	 * @return array[int]string
	 */
	public function GetWarnings(); 
}

All of these classes and interfaces can be found in /lib/Application/Reservation/Validation

Putting It All Together

Let’s say that for add and update we want to enforce some rules based on the value of a custom attribute that we added. For delete, we’re happy with the default behavior. Since we already explained how to create the plugin structure for a plugin named PreReservationExample, let’s stick with that name. So the contents of /plugins/PreReservation/PreReservationExample/PreReservationExample.php would look something like this:

class PreReservationExample extends PreReservationFactory
{
    /**
     * @var PreReservationFactory
     */
    private $factoryToDecorate;

    public function __construct(PreReservationFactory $factoryToDecorate)
    {
        $this->factoryToDecorate = $factoryToDecorate;
    }

    public function CreatePreAddService(UserSession $userSession)
    {
        $base = $this->factoryToDecorate->CreatePreAddService($userSession);
        return new PreReservationExampleValidation($base);
    }

    public function CreatePreUpdateService(UserSession $userSession)
    {
        $base =  $this->factoryToDecorate->CreatePreUpdateService($userSession);
        return new PreReservationExampleValidation($base);
    }

    public function CreatePreDeleteService(UserSession $userSession)
    {
        return $this->factoryToDecorate->CreatePreDeleteService($userSession);
    }
}

Let’s step through this. Our class extends the PreReservationFactory. It also accepts an instance of a PreReservationFactory as the only argument to the constructor with a parameter named $factoryToDecorate. This will be a fully instantiated object, loaded up with all the rules needed during add/update/delete. Our constructor just holds onto that instance as a private field.

Each of the Create* functions are then responsible for returning a service to be used before their respective event. In our case, CreatePreAddService() and CreatePreUpdateService() get the base validation service, then add their validation service onto it. Since we don’t have any custom rules for delete, we simply return the base service.

This is an important point. To extend the base PreReservation behavior, we need to return the base service, or a decorated version of that service. If we want to completely replace the base behavior, simply return your own custom object.

Now comes the fun part, our implementation of CreatePreAddService() decorates the existing instance. We do this by getting the result of the base CreatePreAddService() function, then decorating it with our own validation object. We end up returning an instance of a PreReservationExampleValidation, giving it that base service to wrap. Now, we’ll need to create the PreReservationExampleValidation to handle our custom rule when adding and updating a reservation.

We start off by adding a new file to our plugin directory named PreReservationExampleValidation.php. This contains a single class which must implement IReservationValidationService

class PreReservationExampleValidation implements IReservationValidationService
{
	/**
	 * @var IReservationValidationService
	 */
	private $serviceToDecorate;

	public function __construct(IReservationValidationService $serviceToDecorate)
	{
		$this->serviceToDecorate = $serviceToDecorate;
	}

	/**
	 * @param ReservationSeries|ExistingReservationSeries $series
	 * @return IReservationValidationResult
	 */
	public function Validate($series)
	{
		$result = $this->serviceToDecorate->Validate($series);

		// don't bother validating this rule if others have failed
		if (!$result->CanBeSaved())
		{
			return $result;
		}

		return $this->EvaluateCustomRule($series);
	}

	/**
	 * @param ReservationSeries $series
	 * @return bool
	 */
	private function EvaluateCustomRule($series)
	{
		// make your custom checks here
		$configFile = Configuration::Instance()->File('PreReservationExample');
		$maxValue = $configFile->GetKey('custom.attribute.max.value');
		$customAttributeId = $configFile->GetKey('custom.attribute.id');

		$attributeValue = $series->GetAttributeValue($customAttributeId);

		$isValid = $attributeValue <= $maxValue;

		if ($isValid)
		{
			return new ReservationValidationResult();
		}

		return new ReservationValidationResult(false, "Value of custom attribute cannot be greater than $maxValue");
	}
}

This class is also a decorator, so it accepts a class to decorate as the first parameter to the constructor. Next, lets look at the Validate() method. The first thing we do is call down to our decorated Validate() method. This runs all of the existing PreReservation steps. Then, we check to see if we're in a valid state by checking the value of $result->CanBeSaved(). If everything is OK to this point, then we go ahead and run our custom validation.

Walking through the EvaluateCustomRule() function, we first get our plugin's configuration file, reading settings for the maximum value we allow and the id of the custom attribute we are interested in. Then, we get the current value of that attribute from the ReservationSeries. Finally, if the value is less than or equal to our configured maximum, we simply return a new ReservationValidationResult. This indicates that there are no issues. If the value provided exceeds our maximum, then we return a ReservationValidationResult, passing in false to indicate the failure and an error message to explain what exactly failed. The error message will be displayed to the user.

Configuring Your Plugin

If you wanted to use phpScheduleIt's provided configuration API, you would just need to create and register your config file. This would change the constructor of the PreReservationFactory to something like this:

public function __construct(PreReservationFactory $factoryToDecorate)
{
	$this->factoryToDecorate = $factoryToDecorate;

	require_once(dirname(__FILE__) . '/PreReservationExample.config.php');

	Configuration::Instance()->Register(
				dirname(__FILE__) . '/PreReservationExample.config.php',
				'PreReservationExample');
}

And here is our config file:

$conf['settings']['custom.attribute.max.value'] = '100';
$conf['settings']['custom.attribute.id'] = '3';

We won't go into the details of configuration here, but it's enough to say that configuration files are structured as arrays. All settings are part of the $conf['settings'] array.

Wrapping It Up

As with any plugin, you'll need to tell phpScheduleIt that you want to use it by setting the plugin value in /config/config.php. Our value would be: $conf['settings']['plugins']['PreReservation'] = 'PreReservationExample';.

Hopefully this gives you a taste of what's possible. This is a powerful customization point and there's almost no limit to what you can do here. Also remember that you have all of phpScheudleIt's internal API available to you. Explore it and use it.

You can find the source code for this example in /plugins/PreReservation/PreReservationExample

Happy Coding!

Extending phpScheduleIt: Understanding and Installing Plugins

One thing I really wanted to focus on when writing version 2 of phpScheduleIt was finding ways to keep the application open for extensibility. This led me to creating a pluggable framework that can be hooked into for certain operations. In this post I’ll explain a bit of the plugin structure and walk through how to configure and use plugins in phpScheduleIt.

Note: The version of phpScheduleIt at the time of writing is 2.2. While the exact details explained in this post may not apply to future versions, the core concepts will.

Plugin Overview

Let’s first explore how plugins are structured. phpScheduleIt ships with a /plugins directory. Within that are subdirectories for each supported plugin type. phpScheduleIt’s plugin architecture is mainly convention based. Each subdirectory within a plugin type represents a plugin. Taking the Authentication plugin type for example, we have ActiveDirectory, Drupal, and so on available to use for our authentication provider. The Authentication plugins directory currently looks like this:

Configuring and Activating a Packaged Plugin

phpScheduleIt comes packaged with a a few Authentication plugins, so we’ll continue using that for our example. Each plugin directory most likely will contain a default configuration file named something like subdirectory.config.dist.php. For example, ActiveDirectory will contain a file named ActiveDirectory.config.dist.php.

The first step is to remove the .dist part of the default configuration file. In our case, the file would end up being named ActiveDirectory.config.php.

Next, you need to adjust any configuration values to match your environment. For Active Directory, you may need to change domain controllers or administrative credentials.

Finally, you need to tell phpScheduleIt which plugin you want to use. Open up phpScheduleIt’s config file, located in /config/config.php and find the plugins section. By default, it looks like this:

$conf['settings']['plugins']['Authentication'] = '';
$conf['settings']['plugins']['Authorization'] = '';
$conf['settings']['plugins']['Permission'] = '';
$conf['settings']['plugins']['PostRegistration'] = '';
$conf['settings']['plugins']['PreReservation'] = '';
$conf['settings']['plugins']['PostReservation'] = '';

To use your configured plugin, just set the plugin value to the plugin name. Sticking with ActiveDirectory for Authorization, you’ll end up with this:

$conf['settings']['plugins']['Authentication'] = 'ActiveDirectory ';

And that’s it! phpScheduleIt will now use Active Directory when users log in.

Using a 3rd Party Plugin

The process for using a 3rd party plugin is nearly the same as the using a packaged plugin. The one difference is that the plugin needs to be ‘installed’ – which merely includes copying the full plugin directory into the plugin type directory. For example, if you are installing an Authentication plugin named FooAuth, simply copy the full FooAuth directory into /plugins/Authentication

Summary

Hopefully this process is nice and easy. Next we’ll talk about how to write our own plugins, starting with a pre-reservation plugin.

Flexibility and Configuration

I’ve run into numerous folks from both the business and technical sides of the world who want flexibility built into software. Developers and managers alike spew out statements like “We want to leave our options open”, “We’ll need to support that one day”, or my favorite “The business doesn’t know what they want”. As developers, we’re constantly faced with pressure to build things faster. Architects and technical gurus ingeniously figure that nothing is faster than making a simple configuration change. “Make it flexible. We don’t want to rewrite anything.” And with that we lay the first brick on the road to the inner-platform effect.

What we really want to do is minimize the cost of change.

This is why agile software practices were created. We want to be able to accommodate change, even late in the development process after significant progress. Some software developers take this far too literally, though. Accommodating change does not mean making every last detail configurable or worse yet, building massively over-architected systems in hopes of supporting yet-to-be-defined requirements.

Think about how long it takes to design and build all of those configuration points. Think about all of the permutations that need to be tested. At the end of the day, how many of those options will even be used? Remember, we’re making our systems and architectures flexible explicitly because we don’t know what we’ll use. By the time this infinitely flexible software is built something new will come along and we’ll realize in horror that we guessed wrong.

Flexibility means you design your software in a loosely coupled manner. Dependencies are injected instead of instantiated inline. You write classes that are 50 lines instead of 5000 lines. The SOLID principles become your best friend. Your software is simple, discoverable and, most importantly, testable. You can make changes swiftly and with confidence that the software is still fit for purpose. To me, this is true flexibility.