I've been struggling with Contenture for the past couple days. I think it's a great idea: users pay a nominal fee (starting from about $6) to gain membership in the Contenture network. Whenever a user visits a Contenture network site, that site gets a portion of the money contributed, and the visiting user gets any number of perks, including an ad-free visit or access to exclusive content. Anyone can easily add themselves to the Contenture network by dropping some javascript code into their site's footer, like I did. But there have been a few issues.

Yesterday, the network script couldn't even detect whether a paying user was a member. That has since been corrected, but there are a number of other odd things that leave me wondering whether the Contenture folks did any prototyping of their system.

For example, my biggest complaint about the system right now (assuming that I'm even getting paid visitors, which I can't tell by looking at my account on the Contenture site) is that there is no clear way to direct network members to log in to receive their benefits. If a person visits my site and is a member, but they're using a public computer and have not logged into Contenture, there should be a link on my site they can click on to log them in. Currently, the only way to associate my site with Contenture is to forward them to my affiliate link (if they sign up via that link, I make a dollar). The affiliate page on the Contenture site doesn't have a login form or link at all. This doesn't make any sense if my visitor is already a user. Worse yet, there's no way for a user, after logging in, to get back to my site.

Another problem with Contenture is that there is currently no visible tracking of the visitors that hit your site who are members. Fortunately, I've discovered a way to track that using Contenture's sister site, CetClicky.

If you have a GetClicky account and have already set up your site for Contenture, the setup for tracking Contenture users is not too difficult. Here's how you do it:

Step 1

Visit your GetClicky dashboard for your site, and click the Goals tab. On the Goals tab, select the Setup link, and create a new goal.

Basically, we're going to define being a logged-in Contenture user as a goal. So in the "Goal Name" field, put "Contenture". Set the goal to be a Manual Goal - one that we're going to tell GetClicky has happened using javascript instead of a URL. Leave the rest of the fields alone (or fill them in, if you have some idea of what a Contenture visitor earns you -- I don't have a clue), but be sure your goal is active, and then submit the form. Your form should look something like this:

Return to the Goals setup page, and you should see Contenture in the list. It looks like this:

Make a note of the number indicated here by the arrow. We're going to use that in Step 2.

Step 2

Open up your site in an editor, and locate your Contenture code.

The script looks something like this by default:

In the javascript there is a location for customizations. This is where we're going to be inserting things. The Contenture documentation is kind of light on what needs to go in here, but hopefully future enhancements to the service will help create this javascript for you, so you won't need to know any of this unless you're really wanting to get your hands dirty.

We're going to add these lines below the comment:

if( _ctr.is_user()){ // line A _ctr.hide_ads(); // line B _ctr.hide('#local_ads'); // line C var clicky_goal = { id: "294" }; // line D }

Line by line, here's what we're doing. Line A checkes to see if the user is a Contenture user. If so, it executes the three lines inside the braces. Line B hides all of the standards ads for registered users. This includes my Google ads. Line C hides my local ads for registered users. I do this by putting ads that I have sold on my own (or any other content I don't want to annoy registered users with) into a div like this:

Buy Viagra!

And the last line, Line D sets the GetClicky goal id for this action. The id is the one that we saw at the end of step 1. When you paste in this code, replace the id here with yours from your GetClicky goal. Note that this goal-setting code is only executed if the user is a registered Contenture member.

Finally, one more important thing. Put your GetClicky tracking code after your Contenture code. Otherwise, the goal tracking won't ever be sent back to GetClicky, and you'll just see a big 0 on the Goal page.

What's it look like?

You might be interested in knowing what the thing looks like when it's all set up. Well, when you've got it all connected properly, you can visit your Goal page in GetClicky, then choose the Contenture goal. When you do, you'll hopefully see larger numbers than I do:

Yes, that one Contenture user from Verizon is me. I clicked on my own site. Hopefully some of my $6 will eventually end up back in my pocket. I've applied the Contenture code here too, but it's having some issues (my fault) on the non-blog pages, and I don't track traffic here with GetClicky, but with Google Analytics instead.

All of that said about GetClicky, there are also ways you can track similar goals using Google Analytics instead. I'll leave that exercise for later.

What next?

Contenture remains a great idea in my mind. I think if people stop looking at it negatively as "people will only sign up if everyone uses it" and instead see it as a way for sites to put together a working, paid subscription system in a few minutes, then they'll see it in a better light. Nonetheless, on my laundry list of "What the heck?" missing features:

  • OAuth for membership
  • Javascript protection of premium content? Seriously?
  • Live visitor earnings reports
  • Full GetClicky integration
  • A directory of Contenture-enabled sites
  • A list of Contenture sites I've visited as a member
  • A system to opt-out of payment (and any advantages) on specific sites I visit
  • A combined login/signup form that is affiliate-aware
  • A way to direct logins and signups back to my site if I sent them there in the first place

I'm a bit put off by the kinks in the system that was announced like it was supposed to be ready for prime time at launch, especially from the nicely assembled GetClicky product team, but I'm looking forward to improvements in the system with hope that some of the above items enter the product soon.

I've noticed what I think are becoming clearer segregators between the Drupal, Habari, and WordPress platforms; some details that differentiate the products more in my mind than they had before.

Drupal is constructed to let users build sites entirely from its UI. Much effort is made to make it unnecessary even to write any HTML code, yet still allow users to configure the type and quantity of data fields assigned to their content. Users can set options to determine the layout and formatting of output.

As a result of Drupal's user-configurability though, any little things that fall outside of what the system can do for you are often crazily more complex to apply than other systems. For example, if you want to apply any special formatting to a field of data that is entered on a node, you must write theme functions or alter a template's output of that field. This is often a more complex task than what would be required to output the same data in other systems, which is explicitly what Drupal is trying to avoid in the first place.

WordPress is constructed to have a fairly fixed set of data, in comparison to Drupal's field flexibility. By doing so, it can confine the user to a very strict set of data to output via a finite set of specific PHP functions - aka "Template Tags". This is a benefit to people producing WordPress themes because it gives them the flexibility to design HTML and CSS code, and then insert the functions for data output into predefined spaces in their templates.

WordPress is different from Drupal in that it assumes you will want to edit the template code to change the output, rather than configure the organization and display of data from inside the software. As a result, WordPress' code must stay simple to appeal to users who wouldn't otherwise grasp database design, and so would likely never have the level of data configurability that Drupal offers.

Habari is constructed to have a reasonably fixed set of data, more like WordPress than Drupal. A key difference though, is that Habari doesn't confine itself to simple tags for the benefit of non-coders. By leveraging an assumption of some coding ability, or at worst, the ability to cut and paste existing working code snippets that produce effective output, more dynamic data can be extruded and manipulated from the initial basic offerings.

Unlike Drupal, Habari is not designed to allow users to change the layout or presentation of the data on the page. Like WordPress, Habari assumes that a theme designer will want more control over the layout of output than could be provided by a generic UI. Still, in many cases, Habari requires that theme developers have a more developed coding skill to make use of the tools that it provides.

Recently at work we started a weekly lunch time online and via conference call gathering that we call a "brain session", where the person who is selected for the week brings up a topic of interest to himself, shares it with the group, and produces some discussion. One of the technical difficulties of this project is producing some kind of shared visual prop that everyone can look at while the person is talking. In some cases this is a demonstration of code, in other cases it's just a PowerPoint slide, and sometimes it's as complicated as a real screen cast of the desktop.

My turn is up next week and I would really like to have a solution to this that doesn't involve some complicated process and crazy screen sharing software. PowerPoint is great but for the particular topic that I want to present I want to make sure that nobody skips ahead, so I want some control over explicitly what slide everyone is looking at. It would also be nice if there was some interaction with the people who were viewing.

For example, the presentation that I'm thinking of giving has some simple test-like opportunities. Each viewer of the presentation would hear me speak a question aloud over our telephone conference bridge, and several options would appear onscreen. Everyone watching the presentation would be able to click on one of the options on screen, and a tally of the chosen selections would be sent to my console. Even better, the next slide could display the tally of votes so that everyone could see the results.

PowerPoint has this functionality when presenting in special conference rooms with specialized equipment. There are places where there are keypads attached to the chairs and participants can enter their responses into the keypads, which are forwarded to the presenter at the podium. This works great, except we don't have any of that hardware, and everyone will be looking at this presentation from the desktop at their own home. So what I need is a software solution, and that's why this post is relevant.

I've started writing some software that I call "presenter". This presentation software lets me publish a series of slides to the web, produce a list of participants who can log in and see it, and most importantly, control the display of slides remotely without having to install any special software or use any kind of streaming server.

Some of the other solutions might just say to use Flash, but to do that you need to use some kind of server component that multicasts whatever I'm broadcasting from my PC, and I don't really want to do that. My solution simply uses a combination of HTML and jQuery to make Ajax calls to the server looking for updates. I think the solution that I've developed is somewhat unique, at least I've not heard many people talk about this kind of solution. That's not to say that the solution is the best solution, because I see at least one big problem with that which will become evident as I describe it.

Basically what happens is the Ajax call hits a PHP script on the server. The PHP script checks for a value stored in the APC cache. Since the APC cache is shared between all scripts, the value of the cache can change even while the script is running. If the value in the cache is the same then the script merely sleeps for one second and then checks again. It will do that a preconfigured number of times for every request, currently defaulting to 30 checks per request. If the value changes or the maximum checks are reached in the script sends back a JSON response that explains to the viewing component (written in HTML and jQuery ) what it should currently display. The viewing component is smart about it and will not refresh a slide if changes weren't detected.

Using a memory-based cache lets me as the operator control what slide all the participants should see. Requesting a different URL, I get a thumbnail list of all slides that are present. When I click the slide, it sends a request to the server that sets the value in the APC cache. Immediately, all of the PHP processes running on the server or in sleep mode have access to that new value. When they come out of their one second sleep, they notice this change and immediately exit the sleeping loop and send back the JS and slide information.

Obviously the problem with this approach is that every viewer produces a PHP process on the server. I'm not sure there is a great way around this without using some specialized server software. But given that there will only be potentially eight participants in my presentation, this should not be a problem when I show them the slides next week. Also I will be running the presentation from a server that is not very taxed. It will be interesting to see if there is any real performance issue.

Most of the functionality that I've described is already complete. The system already will show slides and let me select which slide I want to be the active slide. Remaining in the process is producing a nicer administrative interface for pre-configuring the names of guests, and potentially including some of those interactive features that I was describing. I was thinking of making them a bit more elaborate, but I don't think I will have time to do that and produce the material I need for the presentation itself.

Actually, I'm pretty excited about the topic that I'm going to bring to the group, and I'm hoping that I can produce it in a less interactive form for general publication also.

I was working my way mentally through a little side project for work this morning, and I came up with an interesting puzzle: How can you compute the number of workdays left between two dates?

The purpose of this is mostly to help estimate projects by figuring out the amount of time left before project completion, and then dividing the estimated hours of workload over that amount of time. This could be helpful in planning out individual days, knowing how to ration out a day's worth of hours depending on how much time is left for various projects.

The trick with counting available days lies mostly in knowing what days aren't available. Since this is just an estimation tool, I'm not going to worry about vacation days or holidays. Knowing that, it's relatively trivial to start off with the absolute number of days between two dates using PHP:

// $start_date and $end_date are timestamps define('SECONDS_IN_DAY', 86400); $total_days_between = ceil(($date_end - $date_start) / SECONDS_IN_DAY);

PHP 5.3's DatTime and DateInterval classes make this even easier, but our target platform is only 5.2.8, and those classes aren't fully available according to the PHP documentation.

What we need to do is account for what weekdays the start and end dates are so that we can subtract the weekends from the total value.

First, I'll push start dates that are on a weekend forward to the next Monday, and end dates on the weekend to the previous Friday.

// Reduce units to days from seconds $start_date = floor($start_date / SECONDS_IN_DAY); $end_date = ceil($end_date / SECONDS_IN_DAY); // Push the start date forward do { $start_info = getdate($start_date); $start_date += 1; } while ($start_info['wday'] 1 || $start_info['wday'] > 5); $start_date -= 1; // Push the end date forward do { $end_info = getdate($end_date); $end_date -= 1; } while ($end_info['wday'] 1 || $end_info['wday'] > 5); $end_date += 1;

My thinking here is that computers are better at integer math than they are at conditions. Rather than saying "if()" something, I'm just adding a day to the date regardless, and then removing it when it's not needed.

With the above code, I have the start day and end day both on weekdays, not weekends. Then all I need is to remove the weekend days from the total count:

// Get the total days between the two dates $days_between = ceil($end_date - $start_date); // Get the number of weekends between $weekends_between = floor(($end_date - $start_date) / 7); // Remove the weekend days from the full count $workdays_between -= $weekends_between * 2;

The above is pretty straightforward. For every 7 days - a week - there is one weekend, and for each weekend, remove two days from the count. Our total $workdays_between shows a reasonably accurate estimate of the number of work days between the start and end dates.

There's been a good amount of action in Habari commits lately concerning permissions. The system is finally, after much too long a delay, working well enough that it's inevitable that 0.6 will appear shortly. The permission system in Habari is steeped in a great amount of unwarranted mystery, and is pretty straightforward if you clear your mind of preconceptions and start over with some basic concepts.

First, you should be familiar with the vocabulary of permissions. Habari has the obvious user which represents a user who has successfully logged in using a username and password. There is also an anonymous user that is used to represent any user that has not logged in. Users can be members of one or more groups. Typically, permission to resources is extended by Habari to groups, which make them easier to maintain, but Habari also supports assigning permissions to users. These concepts are pretty easy, but some others might sound new.

Habari uses a thing called a token in regard to permissions. Groups and users can be given different combinations of access to any token. Access is a combination of the standard CRUD permission set: create, read, update, and delete. (Actually, in Habari currently these accesses are named create, read, edit, and delete.) To put these terms together in a sentence, a group (or user) can be assigned read and update permissions to a token. Habari also allows you to assign deny access to a token. Deny access overrides any granted CRUD permissions to a token you might have.

What are these tokens good for? Good question.

One or more tokens can be assigned to any post or action. So if you have a post that is assigned the token "artichoke", and your user (or a group your user is a member of) has "read" access to the "artichoke" token, then you are able to read that post. Likewise, if you have "edit" permission on the "artichoke" token, you may edit that post.

There are some tokens that are applied to posts in general. For example, the "post_entry" token is implicit on any post of type "entry". If your user has "read" access to "post_entry", then you can read entry-type posts. In fact, the Anonymous group, of which the anonymous user is a member, has this permission, which is why anonymous visitors to Habari sites can see the posts. If this permission is revoked, then anonymous users see nothing, and you must be logged in as a user with appropriate permissions to read any posts.

Token can also be applied to actions within the system. So a token "manage_comments" might control your user's ability to manage comments. The CRUD access rights don't apply to these tokens. Any access right on the "manage_comments" token would allow you to manage comments. Only if you have no access or have been explicitly denied access to the "manage_comments" token would you be unable to manage posts. These types of tokens are called "binary tokens" versus the "CRUD tokens" that apply to posts. Some upcoming schema changes in the Habari database will delineate these more clearly for the purpose of displaying them more effectively in the admin.

The beauty of the system is that permissions are queried with every request for posts by default, and no additional permissions checking or filtering is required. A single query returns the posts that are available for a specified user, allowing the system to operate both efficiently and effectively.

This system may seem more complex than what one would expect, and under the hood it's not trivial, but it provides an unprecedented level of permission configuration that is managed by the core software, and an API that is easy for plugins to manipulate. With this permission system it is possible to create simple private post functionality, a multi-tier subscription model, and even custom permissions to existing or brand new features within the admin.

For people that run single-user blogs, the permission system stays nicely tucked out of the way until such time as the site needs it. Guest posting anyone?