Steve Jobs, 1955-2011

As remembered on Apple's home page,

 ...and by

You may leave other found tributes or your own in the comments. Farewell, Steve.

Funday Monday - Who was the Wild Child?

Who was Peter the Wild Boy?

From feral child to "human pet" at court in Georgian England, Peter the Wild Boy caused a sensation. And new analysis of his portraits may have solved the mystery of his unusual characteristics.

New analysis of this portrait suggests Peter had a rare genetic condition known as Pitt-Hopkins Syndrome, indicated by:
  1. His short stature
  2. Lustrous mop of thick curly hair
  3. Hooded eyelids
  4. Cupid's bow mouth, with a pronounced curve to the upper lip
  5. He disliked clothes, but was wrestled daily into a green suit
  6. Pictured holding acorns and oak leaves - symbolic of living wild in the woods - and some fingers on his left hand (not seen) were fused

Building a Conversation with 500 Students

This article appeared as a Syllabus "Case Study", July 2001, page 10. Even after all these years, I find that it still holds up. It was exciting to create a multi-directional Socratic dialogue with a class of 500 students - using Webcrossing software for the dialogue backbone freed me to focus on the students.

Building a Conversation with 500 Students
Syllabus "Case Study", July 2001, page 10

Americ Azevedo has 500 students in his Introduction to Computers course at the University of California, Berkeley. Despite that, his teaching philosophy embodies the principles of the Socratic method. What makes this apparent contradiction possible is discussion software, a technology that enables him to “teach” and interact with his students outside of class in a flexible, dynamic way. Discussion software connects him, his students, and the TAs for the course, and allows them to communicate much more directly and frequently than they otherwise could.

Azevedo, who has taught courses in computers and human/computer interaction at several institutions, has always believed that the Socratic method is right approach to teaching. In courses with smaller enrollments, he has usually relied upon open-ended questions and in-class discussion while simultaneously tinkering with online discussion formats to supplement the ongoing Socratic dialogue. However, faced with the prospect of teaching hundreds of students in a large lecture hall three days a week for 50 minutes, Azevedo knew that in-class discussion would be nearly impossible and that he’d have to rely on online discussions to achieve his goals.

The immediate benefit of using discussion software has been an increase in participation by his students and a chance for him to get to know some of them quite well. “I can call up a list of all of their contributions to the course and see what they’re like quite easily.” Says Azevedo. “Through the use of the software I get a chance to hear the ideas and concerns of a large number of students. It also allows the shy students to participate as actively as the more outgoing ones.”

Flexibility was one of his primary concerns. “Most of the software that exists for course management is content-centered,” notes Azevedo. “It locks you into specific topics and doesn’t allow discussions to grow naturally.” Because it had many of the features he was looking for, Azevedo decided to try Webcrossing in the course. “What like about Webcrossing is that it’s discussion-centered and allows students to generate new topics, or threads, if they want to. It is really an experiment in developing a pattern language, a new way of submitting thoughts.”

Azevedo’s class site had topics that he’s generated, where he can post course information, posit questions, and link to other useful sites. It also includes many student-generated threads. In addition, the course is supported by the full array of UC Berkeley’s technology tools. For instance, through the Berkeley Internet Broadcasting Network, Azevedo has captured streaming video of all of his lectures. Students can link to these from the class site. Coupled with dynamic lecture notes, PowerPoint slides, and online assignments, the site is a rich resource for both discussion and study. “With these resources in place,” he notes, “we could expand the enrollment of the course to a distance learning situation with twice or four times as many students.”

E-mail is a part of Azevedo’s package as well. He is experimenting with the use of e-mail to communicate with his students, posting information about upcoming lectures to a listserv. Based on student responses, he can tailor the lecture to address the material they need specific help with, and skip what they’ve already absorbed. This has led to some interesting student-contributed material. “I asked them to define some terms that are used in the computer industry,” he says. “The students generated some very interesting metaphors that I used in lecture.”

Azevedo’s Introduction to Computers also has a lab requirement. “With 18 sections of labs 4 hours a week, we’re using all the available lab space,” he says. In the future, he hopes to use discussion software to enhance the laboratory experience, allowing the students in each lab section to communicate with each other and with the TA on projects. He’s also planning to experiment with a CyberLab that would replace some of the lab sections.

According to Azevedo, discussion software can enhance not only large lecture courses, but smaller enrollment courses as well. “When I used it with small classes, it generated a lot of excitement,” he notes. “There was more personal engagement than you get with a large course. My students generated hundreds of pages of text, and the themes just grew and expanded organically.”

In his large course, Azevedo has found that students don’t necessarily participate as much, but he has been surprised by the level of response at times. “I started a debate about participation credits: should students get credit for participating in these large lecture courses? This produced a raging debate on Webcrossing. It was a level of expression that you would normally never get in a large class. Students contributed ideas about how the course should be graded, some of which I incorporated into the course.”

He adds, “My co-teachers, Nicholas Cravotta, quickly adopted the new environment. For instance, he was able to give quick public feedback to a discussion topic led by student-generated questions. Normally, students would never get this kind of feedback in a large class.”

Debugging with log files

The easiest way to debug with Webcrossing is to just print values to the screen:

myvar: %% myvar %%<br>


+ 'myvar: ' + myvar + '<br>';

But sometimes - especially inside filters - it isn't possible (well, not practical) to do this.  Or maybe you are trying to catch an elusive bug on a production server and printing to the screen is not an option.  In that case, you need to write a log file with the values in it which you can examine after processing.

Let's use a debug file called, well, "debugFile".

In WCTL, the syntax is like this:

%% "debugFile".fileWrite( "myMacro ran" & crlf ) %%


%% "debugFile".fileWrite( "myVar:" & myVar & crlf ) %%

In SSJS, it looks like this:

"debugFile".fileAppend( "myMacro ran" + '\r\n' );


"debugFile".fileAppend( "myVar:" + myVar + '\r\n' );

You can add timestamps too if you intend to leave the debug code in for a while.

When you're ready to look at it, the debugFile file is in the /system directory on your server (if viewing from FTP) or the /webx directory (if viewing from the server's operating system itself).

Debugging SSJS - What the heck am I looking at?

If you have an error in a WCTL script file, the error and its location will be written into the webx.log file, a warning message will appear on the cache reset screen, and as Sue has pointed out the existence of the error - if not its provenance - is often made obvious when your site appearance goes pear-shaped. Errors in SSJS syntax can be a little more difficult to track down, if for no other reason than JS is more frequently used for backend-type functions where nothing gets written to the screen directly. If you have an error in a filter, for example, the only sign of it may be that the filter doesn't give the expected results.

One way to deal with this is to put all the significant pieces of your code inside a try/catch structure and write an exception handler. That's certainly best-practice for production code, but during development the Webcrossing server will provide you with a wealth of information, really an embarrassment of riches, for any uncaught exception in your JS code.

All you have to do is look in your /system folder for files named in the format of logJsErrorN.html (where N is an integer). One gets written whenever an exception is thrown. It's an HTML file, it can run to be upwards of 3000 lines, and there will rarely be a time when anything past the first couple of dozen lines will be of use to you. Here's an example:

The heading gives you a timestamp for the exception, tells you the name of the routine that failed, and the type of exception it threw. Then the next section gives the values of the routine's arguments and any local variables when the exception occurred. In the next section is listed the exact line of the function where the exception occurred - so it's worthwhile to keep your code clean and well-blocked. The last likely-to-be-useful line is the stack trace, showing you exactly how you got to where the exception was thrown.

Now, the next 2000-odd lines are an enumeration of every single object in the execution context at the moment of the exception: every global, every function, every built-in method and property. I'm sure there are circumstances where this information can be useful, but I've never run into one!

Dealing with compile errors and the dreaded Square Toolbar

So let's say you're happily developing along with Webcrossing and after your latest cache reset you happen to notice that your toolbar has gone from this:

to this:

Congratulations! You've just encountered the dreaded Square Toolbar. Often there is a lot more than that amiss, but sometimes the Square Toolbar is the most obvious thing wrong. What it means is that you've got a compile error - somewhere in your code you likely have a missing or an extra WCTL %%, or a missing or extra SSJS curly brace { }.  Or perhaps your SSJS functions are not surrounded by the requisite %%'s.

Technically, what has happened is that the script version of the toolbar, either a custom one or one of the toolbar plugins, is failing to load because of your compile error, so the square toolbar is coming directly from the code in the binary - a fallback, if you will.

The first thing to do is to check webx.log. The exact compile error should be listed there, with at least some clue about what is wrong in what macro or function. Usually from that you can figure out what to fix. Rarely, though, especially if you have two offsetting errors (two separate extra %%'s, for example, in different places), you will find there is no actual complile error.

What to do then? The most logical place to look, of course, is the file or files you've just updated. Often a quick glance is all it takes to find the error. But sometimes it's not all that obvious what's wrong. Maybe someone else just handed something off to you and you're not at all sure what they just did, or where to start looking. If you have any suspicions at all, it's worth trying to turn off those suspicious template files by renaming them (say, becomes myfile.autoOFF). If your error goes away when you reset the cache, you know where to look.

But sometimes you simply have no clue at all.

Then you have to resort to the brute force method. It's ugly, but it works. First, you need to identify the file the error is in, and secondly, where it is in the file. To do this, create a small text file with only this in it:

%% macro hello %%
Hello, world!
%% endmacro %%

Name it with a name which will ensure it loads alphabetically BEFORE any of your other template files, something like Put it in your templates directory and reset the cache. Then, in the browser location bar, call your macro hello:

You should see "Hello, world!" on the screen. Good. (If not, your problem is probably in webx.tpl itself, or perhaps one of the handful of template files that load unconditionally before anything else. If you encounter this, the log file webxIncludes.log, which is rewritten after each cache reset, can be helpful to show you what's loading and in what order.)

If all was well with your file, rename it so that it loads after some of your template files, maybe Test again. If you see "Hello, world!", you'll know that everything alphabetically up to your file is OK, no errors. Your error is in one of the other files further down alphabetically. Otherwise, your error is somewhere before. Rename your file again and try again, until you narrow down a single file with the error. Rename that suspect file so it doesn't load, as described above. Test again, and you shouldn't have an error.

Now that you've identified the problem file, you've got to find the exact function or macro with the error. If it's not obvious looking at the file, remove your small text file with macro hello in it, and copy macro hello into the middle of the problem file. Test again. If all is well, your error is further down the file. Keep moving macro hello until you identify exactly which function or macro is causing trouble. If you can't find it by eyeballing it at that point, remove chunks of code from the function or macro in logical blocks (like an entire "if" structure, etc.) so that the removal itself doesn't cause a complile error. Eventually you will find the chunk of code that is causing issues and from there, you can fix it.

Whew. And I bet you've still got hair left!

Creating Attachments Manually

In a Webcrossing standard install, the forms to add a folder, discussion, or message all have a field to attach a file. Attached files show as hotlinks when the node is displayed. (There's also a configuration option to show images inline rather than as a download.)

While there are commands in both WCTL and SSJS to add an attachment to a node, the command takes as an argument an already-existing attachment on another node. One might reasonably ask, how do you script the creation of a new attachment from scratch? It's far from unusual to have to home-brew your node-creation process; sometimes a spec is odd enough that not even with the use of the pre- and post- filters can you shim the program's default behavior. There are scripting commands that directly replicate every part of the node-creation process, except for this one.

Fortunately, it only takes a little extra work. What's really going on is that when attachments are uploaded, they get saved with a MIME wrapper. That sounds like voodoo, but in truth all it means is that a couple of lines of plaintext are prepended to the file's binary content. The content of that header will be familiar to anyone who's ever perused the full headers on an email message. It might look something like this:

Content-Disposition: form-data; name="enclosure0"; filename="IMG_0002heads.jpg"
Content-Type: image/jpeg

This information gets sent by the browser when the upload is transmitted, and it's used to determine how the file should be handled by other clients.

The first step to capturing this is to make sure that your <form> element includes the attribute enctype="multipart/form-data". Leave that off, and only the file names will be uploaded, and you'll be snatching yourself bald trying to figure out why. But once you do that, all you need is to include this SSJS in your processing routine:

var z = form["inputName"];
var h = Mime.formHeader("inputName");
var attach = new ByteBuffer();
attach.append( h );
attach.append( z );
if (z.length > 0) var attachmentLocation = location.nodeAddAttachment( attach );

This code takes the upload from an file input field whose name is "inputName" and extracts the MIME wrapper, then concatenates it with the binary data to make a well-formed attachment. And that's all there is to it.

Webcrossing Subscriptions

Webcrossing Community allows registered users to "subscribe" to various folders and discussions. What this means is that the software will track which messages they've read and show them only the new messages.  What happens after that depends on the type of subscription the user signs up for.

You can allow users to do any (or none!) of the following:
  • Subscribe by web - users either go to the Message Center for a list of their new messages, or click Check Subscriptions in the toolbar on any page. Each time Check Subscriptions is clicked, the users will be taken directly to the next new message on their list without having to return to the Message Center in between.

    This is really an amazing "power tool" that many sites don't understand and subsequently turn off.  It allows somebody to instantly view all the new material on the site (in the areas they're interested in) without clicking around or being inundated by email notifications.
  • Subscribe to email for individual posts - users receive whole posts in email
  • Subscribe to email notification for individual posts - users receive links back to the forum and notification that a post has been made, but must visit the forum to read the new messages. Users will not receive further notification until they have visited the forum and read the unread messages. This prevents spamming people with notification messages before they are ready to visit.
  • Subscribe to email digests - users receive an email digest of all new posts, arranged by discussion, emailed at the time of their choice. The interesting thing about this is that digests don't come at some time determined by the site owner or whenever there is enough material, but instead at the time the user chooses. You an get 24 digests a day if you want - one an hour. Or just one a day. Or just one a week.

Users can subscribe to everything, nothing, or any combination of subscription methods - to the entire site, or to just a subset of areas that interest them.  You could subscribe by "check messages" to the Dogs folder and by email digest to the Cats folder.  The only limitation is that full notifications vs. individual post notifications is a sitewide setting for the user.

Subscriptions are definitely a "power feature" of Webcrossing Community.  Webcrossing Neighbors has some additional subscription features by virtue of its social networking emphasis.

My Favorite Commands - Filter userChangeFilterPost

This command is a personal favorite of mine, not just because it's uniquely useful but because it was the first major piece of code that I wrote in the binary all by my lonesome. :-)

This SSJS filter gives you a real-time hook into any change of a user record, in much the same way that the *editFilterPre and -Post give you a real-time hook into changes of nodes in the folder/discussion/message hierarchy. When is this useful? Well, originally it was implemented to keep an external authentication database in sync with Webcrossing's database. For various reasons the client needed to be able to change user data from either direction so we wrote this filter so that it would fire whenever a user changed any field in their preferences or any other field in the user record was changed by a system process.

The filter also proved useful in a realtime forensics situation where a client suspected unauthorized access; with a userChangeFilterPost filter configured to send alerts it was possible to watch events as they unfolded rather than spelunking the logs afterwards.

The filter works simply: when it fires, two special globals are defined: changedUserId is the userId that was changed and changedFieldName is the name of the property that was altered; that includes both the built-in and custom properties. So the basic syntax is

var theUser =;

to access the altered property. (Note the brackets syntax; theUser.changedFieldName treats the variable as a string literal which is not what you want.)

Like always, the should be inside a try/catch structure because if the user has been deleted by some other process, it will throw an exception and halt processing.

My Favorite Commands: Image manipulation

Way back in the dark ages when Webcrossing was "just" a message board, its developers came up with the radical idea of allowing users to upload little pictures to go with their user accounts. Wacky, right?

As an outgrowth of that, Webcrossing has a small but powerful set of image manipulation commands, so that users need not be limited to uploading files of only certain sizes or dimensions. Using these commands you can post-process any JPEG or GIF uploads to fit whatever requirements you choose to establish. The commands are:

image.imageCropToJpeg(top, left, width, height, quality)

top and left define the upper left corner of the portion of the image to crop, based on 0,0 as the upper left corner of the image. width and height are in pixels and define the size of the portion to crop, and quality is a percentage defining the compression and hence quality of the JPEG the command returns.

image.imageResizeToJpeg(percent, quality)

This command proportionally resizes your image proportionally. percent is the percent to scale (which can be larger than 100).

image.imageResizeWHJpeg(width, height, quality)

This command allows you to resize to a specific width and height (in pixels), and can be non-proportional.

image.imageRotateToJpeg(degreesCounterclockwise, quality)

This command rotates the image counterclockwise. To rotate clockwise, use 360-[clockwise rotation].

All of these commands return the altered image as a stream of binary data which you can save directly to file. The parameter image is the binary data of the original file, and getting that may not always be completely obvious. Here's a little helper function that will extract the binary data from an attachment. This function can also be used to get the data of a file attached to an incoming email message.

%% function getImageData(enclosureLoc) {
        var image = Stored.lookup(enclosureLoc);
        if (image.storedIsDocument && image.documentIsImage) {
                return Mime.getBody(image.documentData);
        } else {
                return "";
} %%

Creating Email Lists in Webcrossing

Building on the concept of email subscriptions, Webcrossing allows you to create email lists - archived on the web and fully searchable.

First, turn on full-email subscriptions as an option.  This means users who subscribe by this method will receive a copy of each post by email - as you can see, this is already half of the equation.  Now you just need to enable post-by-email. Now create a folder you want to hold your email archives. In the folder settings, there is an option to "make this folder an email list."  That's all you need to do.  Anybody who is subscribed by email can reply back and it will be posted on the web, filed appropriately in the correct discussion in the correct folder.

There is a page of settings (image at right) where you can tweak various aspects of your list, like what the reply address is, or add any special headers you want added.

You can also mirror external email lists or copy all posts from your list to some other address (such as another external list).

There is also a "Mailing List Administration" plug-in to make managing your user lists easier.

All in all, it's quite handy, and quite flexible.

Webcrossing Quirks

We've talked about WCTL before, about how it is a fast, easy language which is great for UI, but that it's a little, well, quirky.

There are a few quirks in Webcrossing itself, just kind of goofy "what were they thinking" settings. Quirky, but a little cute, like a slightly cross-eyed Siamese kitten is quirky.  The answer, of course, to what they were thinking is that in most cases some customer way back when paid to have that setting added.  It made sense then, to them, but not to anybody since then.

I want to emphasize to anyone reading this that except for #1, these are all OPTIONS.  They are not turned on by default and an administrator has to explicitly turn them on before the server will function that way.  Just to be clear that Webcrossing is not totally whacko.

Here are some staff favorites:
  1. Shut down server link.  Remember, you need shell access to a terminal to start Webcrossing.  So why would you want a "Shut down server" link in the control panel?  Once you shut it down that way, you can't start it up again that way.  Right now, it is two or three links from the bottom of the page so is more difficult to click accidentally. But I am old enough to remember it being right above the "Return to site" button, and I have clicked it accidentally more than once.

  2. Provisional users have full access before their email addresses are validated. You can set the site to require that people respond to an email validation challenge.  Before they respond, you can give them read-only access, moderated access, or full (participant) access.  Why would you turn on provisional users and give them full access before you were sure their email address was valid?  What's the point?

  3. Registered users can edit or delete anyone's posts, not just their own.  Excuse me?  Edit someone else's post if I'm not an administrator?  DELETE someone else's post?  I guess if you were building a wiki this might be useful, but in the ordinary course of forum management I can just imagine the havoc this could wreak :)

  4. And, the more bizarre sibling of #3, Guest users can edit or delete anyone's posts, not just their own.  You don't believe me?  Here it is, right next to all the more reasonable settings like "guests can post messages" and "guests can add discussions."  I dare you to turn this one on on your forum. ;-)

Passing clientside data to the server with AJAX

Sue wrote a post about mixing clientside and serverside Javascript in Webcrossing. I want to touch briefly on a related topic, which is passing data back and forth between client- and server-side. It can get confusing, especially when you're writing in essentially the same language throughout, to remember what context you're in and what you have access to and what you don't.

Where things can get really wacky is when you're using AJAX calls to change the contents of the browser window without a page refresh. Specifically, you have to remember that any local server-side variables you may have set outside of the AJAX'd content won't be there once you make an AJAX request. Here's a simple example, where I'll use WCTL to make it a bit easier to separate client from server.

First, let's assume your page has the following AJAX call on it. (This example uses jQuery, the Javascript framework with which I'm most familiar.)

function whenYouClick() {

  $.get("/someNewContent",,function(newText) {

If you're not familiar with jQuery, just take my word for it that this code will call the WCTL macro or WCJS command "someNewContent", and whatever it returns will replace the current contents of the DOM element with ID "newContentGoesHere".

Now, let's move down into the body of the page...

%% set initValue 25 %%
<div id="newContentGoesHere">
%% use someNewContent %%

And then, separate from this page macro, you have this macro:

%% macro someNewContent %%
%% set returnVal 2*initVal %%
%% returnVal %%
%% endmacro %%

It should be obvious what will happen when you first load this entire page. someNewContent will execute and evaluate to "50" which will become the content of the div. But now, what happens if the clientside function whenYouClick() makes the AJAX call? someNewContent will execute again, but outside of the context of the entire page, that local variable initVal is not defined. returnVal will be 0.

As long as you keep it straight in your head which data is available where, this problem is not difficult to avoid. Here are two common approaches:
  • You can store needed data in session variables. If you used session.initVal instead of the local variable, it would still be available inside the AJAX call (with some caveats that affect guest users who, depending on system settings, may not have a unique session).
  • Alternatively, you can pass the data as arguments in your clientside function (or put it inside a DOM element where your clientside code can retrieve it) and then through to your AJAX call. Once your AJAX code is executing, any arguments you pass will be part of the form object.
This is a very brief look at a rather challenging topic, but I hope it gives you some idea of the issues involved.

To Thread or Not to Thread

Within the realm of message boards, there are a couple of different ways to organize the messages. Many people don't realize this, but it can make all the difference in the world in how people interact on your message boards. Some people have a strong preference for one or the other, but either will work. Each has its strengths and weaknesses. Even if you prefer one style, the needs of your community may dictate that you at least consider the other style.

There are two basic ways to view message board posts: threaded and conversational.

Conversational (Linear, Chronological) View

In conversational view, also known as linear or chronological view, posts are placed in chronological order, one after the other. This organizational method is good for a group of regular participants engaged in free-flowing, deep conversation. For example, this might work well with a health-related community in which there are groups for various illnesses and members are providing support to one another. It's harder to reply to individual messages, and more difficult to find "the answer" out of the bunch, however, so if you need to be able to find needles in haystacks, you might be better off with a threaded organizational structure. Conversational mode is more of a many-to-many discussion.

Threaded View

In threaded forums, the posts are arranged like an outline, with one post immediately following the one to which it is replying. Threaded organization works well in situations where people need to be able to find a particular piece of information in a hurry, such as in technical support forums. Threaded organization fosters multiple small one-to-one conversations rather than one group discussion.

A Comparison

While threaded conversations might seem more organizationally intuitive, forums generally get more participation when they use conversational mode. This is because people tend to find conversations straightforward and easy to follow.

If you have not experienced both modes, I would encourage you to do so, to get a feel for which one you prefer. Quite frankly, I have a strong bias toward conversational mode. Threading works well for newsgroups and other non-Web-based applications, but I feel that it's more difficult to use it on the Web.

Webcrossing Settings

Whatever you decide is best for your community, Webcrossing's got you covered.  In the Control Panel > Discussions page, you can choose from:

  • Default to threaded, but user can choose conversational in their preferences
  • Default to conversational, but user can choose threaded in their preferences
  • Mandate threaded
  • Mandate conversational

And whatever you choose can be overridden for an individual discussion on the Edit Discussion page, even in mid-discussion.

My favorite combination of settings is to set the actual discussions for conversational view (mandate it), and then in the "Tabular Message View" settings, turn on the option to show replies.  This puts links between the message and the message being replied to.  At the bottom of the message being replied to is a list of all the replies.  At the top of each reply is a link back to the one it was a reply to.  In that way, you can still enjoy the advantages of conversational view while still being able to follow threads if necessary.

Feel free to experiment!

Code snippet: What's up, DOCTYPE?

In a previous post I mentioned using AJAX to call server-side functions and write dynamic content to the page. Here I introduce an easy workaround for one of the places where Webcrossing trips over its own flexibility.

There's a system setting to include a DOCTYPE in every page response for compliance with W3C standards. But once defined, this happens automatically for every request, including those which are not intended to be a full page of HTML, such as the response to an AJAX call. In order to avoid breaking your page, use this simple clientside Javascript function to remove the DOCTYPE:

function removeDoctype( txt ) {
return txt.replace(/<!DOCTYPE[^>]+>/g, "");

Implemented with a jQuery AJAX call, it looks like this:

$.get("/someNewContent",,function(newText) {

A colleague of mine likes to say, "Every so often a programmer will determine that the best solution for a problem is a regular expression. Now she has two problems."

WebCrossing Theme Development

The challenge of Webcrossing at this point in time from an educational implementation perspective is the theme development possibilities. Our organization lacks the skill set to truly customize the appearance of Webcrossing to meet the aesthetic expectations of our users. We have tested Joomla, Moodle and Drupla and I still keep coming back to Webcrossing for a number of features that are either poorly designed in the other platforms or do not exist. There could be a whole new market for Webcrossing if it had third party development sites that users could easily access like:

Any feedback would be appreciated.

5 Tips to Prevent Flames

Some amount of disagreement among community members is probably inevitable. Just as in face-to-face communities, sometimes people get on each other's nerves. Rather than hoping against hope that it won't happen in your community, it's better to plan for problems from the outset and have a strategy in place for dealing with them.

What You Can Do

You can minimize the unpleasantness -- and help douse the flames when it happens -- with a judicious application of software tools and common-sense people skills. Here are five strategies you can use:

1. Choose software that allows you to deal with -- and prevent -- problems. One approach is to use moderation, which is when some or all posts are read and approved by a moderator before they go live. Reading every single post is obviously time-consuming for your community staff, but some software allows you to specify a list of objectionable words, and then it queues for moderation only posts that contain those words. Webcrossing allows you to set a different objectionable word list for each folder, if you wish.

Another tactic is to deny problem users access to some or all of your site, or to withdraw certain privileges. These users can either be denied access completely (you delete their existing user name, and they can't register again), or they can be denied access to a given area within the site (since they're logged in, the software knows who they are and can be programmed to not let them into certain areas). Or you can set it so they can read posts but not post anything themselves. You may not need these tools often, but when you do, they can be lifesavers. You can do this with Access Lists with Webcrossing.

A third approach puts the responsibility in the hands of the users by giving them filtering tools that allow them to block postings from people whom they find difficult. Webcrossing provides a "bozo filter" if you use the default Tabular Message View.

2. Write a watertight "terms of service" document. Require your participants to agree to the document when they register. It doesn't have to be legalistic and scary, nor should it discourage the discussion of issues. But it should specify that attacks on individuals are not permitted. The advantage of having something in writing is that if someone gets unruly and you need to take action, you have an existing document that you can direct them to, and your enforcement actions won't seem arbitrary.  Webcrossing's Register Plus plugin allows you to require agreement to your Terms of Service in order to register.

3. Plan a "seeding" period. It's amazing how many new sites skip this step.  But it can be crucial for setting the stage and settings expectations for behavior.  Before you open your community, invite a couple dozen people to come in behind the scenes. These should be experienced message board participants and people whom you can count on to help you get some interesting conversation going. Besides simply making it seem like "somebody's home" when the public arrives, these folks will set the stage for what will come when the doors open, including being role models for your users-to-come about what kind of behavior is usual and expected.

4. Use facilitators to shape the discussion. Faciliators can model civil disagreement and discussion and deal with problems when they arise. They can deal with your participants via private e-mail and encourage them to work out their issues with each other without making your community an unpleasant place to be. When all else fails, they can use the software tools available to them, as outlined in strategy #1, above. A good facilitator is worth his or her weight in gold!

5. Resign yourself to the fact that you're never going to please everyone. Taking action when things get out of hand is sometimes the only way to prevent your community from self-destructing; your "good" users will usually appreciate your taking action against people who are causing trouble. Otherwise, the positive contributors will get disgusted and may start to spend their time elsewhere. But at the same time, when a facilitator acts with a "cop hat" on, he or she is going to raise the ire of those in your community who have problems with authority, no matter who is wearing the hat. You may get complaints about First Amendment rights and censorship. The trick is to not take it personally and remember that for both you and your participants, everything online feels more intense than it would offline. Thus, if you can encourage people to take a "time out" of a few days to get perspective, it's likely to help a great deal.

Using a combination of Webcrossing tools and good facilitation, you can minimize the flames in your community.

Sorting Items in Folders

OK, you've got your forum set up, and have several folders full of discussions, blogs, and other folder items.  You may decide you want things in a certain order, or that you always want the most recently-created items to appear at the top of the list.

Fortunately, Webcrossing has a couple of different tools to determine the order of items in a folder.

First, in the Control Panel > Folders page, you can set some defaults:

But there may be some specific items you always want to be in a certain position, like at the top, or maybe an Archives folder at the bottom.

To make this happen, you need to set a sort sequence number for the item in question.  Go to the Edit page (edit folder > basic folder appearance and hidden files, edit discussion, edit whatever :) ) and find the blank to set a sort sequence number.  By default, for folders and objects derived from folders (blogs, brainstorm folders, etc.), the sort sequence number is 100000.  For discussions and objects derived from discussions it is 0.

So to set something to appear at the bottom, set a negative sort sequence number below 0.  To set something to appear at the top (or several somethings to appear at the top), set a sort sequence number above 100000.

All items with the same sort sequence number (in other words all folders and folder-derived items in a group, and all discussions and discussion-derived objects in a group) will sort in a separate little group using the default sort setting you chose in the Control Panel.

If you have a number of items you want in a particular position, or if, say, you want one folder to sort alphabetically and another to sort in a particular custom order you set, go to the Edit Folder page in for the folder containing the items you want to sort, and find the "Other Folder Settings" pulldown menu.  There, you'll find an option for "Sorting for Items in this Folder".

That page will allow you to move items up and down in sequence until you get them in just the order you want.  There are handy presets for Alphabetical, etc. so you don't have to alphabetize them manually.

The important part to realize is that the way this works under the hood is to set sort sequence numbers for everything in the folder, ordered in such a way that things sort the way you want them to.  You can see them in the screenshot above, 99990, 99980, etc. So when you have sorted a folder using this tool, and you add new items, those new items will all have the default sort sequence numbers of 100000 or 0.  Thus, new folders will sort at the top of the list in a little group using the Control Panel default order.  New discussions will sort at the bottom of the list in a little group using the Control Panel default order.  You can go back at any time and re-set the custom sort to make the new items sort according to your desired order.

Thus, it's probably best to save the custom sort order for folders where new items are not added frequently. For folders with a lot of activity, set the Control Panel default to whatever works best for those folders, and use the Edit option to set an individual sort sequence number for one or two items in those folders you want to make stick to the top or bottom of the list.

As you can see, this mechanism is way more powerful than a simple "sticky" radio button on a single discussion.

WCMS Host Permissions (WCMS Part 8)

So far in our series on the WCMS (Webcrossing Customization Management Suite), we've covered Themes, Components, Extensions, User and Folder Items, the Localization Manager, and the Developer Tools. In this, the final installment, we'll discuss how you can control whether non-sysops have access to the WCMS.

At the very bottom of the suite of WCMS tools and menus is a link which says "WCMS Host Permissions."

That link leads to a page that looks like this:

First, decide whether you want to let your Host users access the WCMS. Many site owners feel that letting Hosts into the WCMS is asking for trouble. Other sites may have a situation where there are one or more very knowledgeable Hosts that can be trusted with WCMS settings.

If you don't want any of your Hosts to use the WCMS, check the box and fill in none into the blank asking for the group name. Submit the form, and you're done.

If you want all of your Hosts to be able to use the WCMS, DEcheck the box and submit the form, and you're done too.

However, you can also allow just some of your Hosts to use the WCMS. This is how to set that up: If you check the box, the system will use a user group to control Host access to the WCMS tools. So the first thing you'll need to do is create a user group manually the normal way, being sure to set an access list on the user group to disallow Hosts from editing it (adding or removing themselves or others from the group). Once you've done that, come back here and fill in the name of your user group.

Then, decide if it's simpler to have a list of people you want to exclude from the WCMS functions, or to exclude everybody except a few people you want to allow to use the WCMS functions. Set the radio button appropriately.

Voila. Only the Hosts in your user group (or all hosts except the Hosts in your user group) will have access to the WCMS.

It's worth remembering that none of these settings have a thing to do with either sysop or superhosts. Sysop and superhosts can always do everything, everywhere. So this only applies to Host users as defined in an access list. Furthermore, since Hosts can't enter the Control Panel, this only applies to folder-level WCMS pages.

You can apply your host permission settings sitewide, or if you need even more granular control, you can apply them in any folder, using a form almost identical to that above.

And that completes our tour of the WCMS. I hope you've enjoyed getting a taste of what's available under the hood of Webcrossing - most of it not requiring scripting at all!

WCMS Developer Menu (WCMS Part 7)

Our latest installment about the WCMS, (Webcrossing Customization Management Suite), is about the developers tools available.

You first must click the link to "Enable developer mode," which will expose an additional pulldown menu below the Localization Manager menu.

Once you've done that, you will see 4 tools available:

  1. Rebuild Include Files
    The WCMS system does its magic by writing a number of special script files which turn on various plugins in various locations in the forum hierarchy.  These files are called "Include" files because, like a server-side include, they load the right script files in the right place.  If something goes wrong, or you accidentally delete one of those include files, this tool will look through all the locations in the forum hierarchy and delete and recreate all those include files. If you have a large, deep hierarchy, it could take some time and possibly impact performance, but if you have this problem you probably won't mind taking your site down temporarily in order to fix it.

  2. Enable "std" Strings
    Each plugin has its own project name. Some older script files from previous versions may use the now-deprecated "std" project for the localization strings.  If you are using one of those older files as a base for your own customization project, you may need to turn this setting on in order to get the localization strings to load for your project.  You only need to worry about this if your file has localization calls using the "std" project, like:

    lang("std", "somestring")
    %% "lang".jsCall("std", "somestring") %%

  3. Enable String Debugging
    If there is a missing localization string sometimes it can be difficult to figure out which plugin project the string is coming out of.  Enabling this setting will add HTML comments after each string call, telling you which project the string is coming from, which string is being called, the language being returned, and information about the user's language settings. This can be really handy when you need it, but be fore-warned, it makes your submit buttons really ugly because the HTML comment will be displayed as part of the submit value and displayed literally on the page.  So you don't want to do this on a production server.

  4. View Log Files
    Admittedly the best way to view log files is on the server via FTP, but if you just need a quick look and aren't in a position to use FTP, this can provide you with that peek you need.  You can choose to see:
    • the last 50 lines of webx.log (showing server status and errors)
    • webxincludes.log (showing which script files are being loaded)
    • the last 50 lines of the logLangErrors file (showing missing localization strings)
    • the most-recently-written JS error file (showing a stack trace of the last SSJS error)

And that's it! To hide this menu, just use the "Disable developer mode" link just below the Developer Menu.

We've got just one more post on the WCMS - next time on how to set granular host permissions to uses the WCMS tools.

Date manipulation in WCTL

For the most part, date manipulation in Webcrossing scripting is more familiar and intuitive in Server-Side Javascript (SSJS). The Date object has a full suite of getters and setters, objects can be directly compared using the < = > operators, and Webcrossing extends the object with the dateFormat() method that makes it easy to present a date in any format. But there are always cases where you're doing layouts in WCTL and it may be inconvenient to do the mode switch into JS. (There's also a tiny performance penalty for doing so, insignificant in all but the highest-traffic sites.) WCTL has directives for comparing and manipulating dates, but since it is an untyped language - basically, everything is a string except when it's a string treated like an integer - it may strike you as a giant kludge. Nevertheless, its date-manipulation routines are powerful and useful once you get accustomed to the weirdness.

The basis to WCTL date manipulation is a string in the format Y4-M2-D2-H4.I2.S2[.L3], for example 2011-06-23-15.05.00 to represent today at five minutes past three in the afternoon. You can optionally add another three digits to represent milliseconds. Note that month, day, hour, minute, second must all be two digits; note hours, minutes, and seconds delimited by a dot instead of a colon; note that the month index is 1-based instead of 0-based as in Javascript. This format string is referred to as a dateObject or dateObj, and this is the only format that WCTL recognizes as a date. All of the directives discussed here use a dateObj.

To compare dates, there are .dateEqual, .dateLessThan, and .dateGreaterThan. All three use the syntax

%% date1.dateLessThan(date2) %%

where both date1 and date2 are dateObj-format strings. In this example, the directive evaluates to 1 if date1 is before date2, 0 otherwise. Similarly,

%% date1.dateDeltaSeconds(date2) %%

evaluates to the seconds elapsed from date1 to date2. If the former is after the latter, the value is negative.

To alter a dateObj string you can use .dateSetHours, .dateSetMinutes, .dateSetSeconds in the format

%% dateString.dateSetHours(int) %%

Where int is the hour (0-23) or in the other functions the minute or second (0-59). How, you may ask, do you set the year, month, or day? There are no dedicated directives for that purpose; instead you must use string manipulation to parse out and replace those numbers.

For raw numbers there are %% milliseconds %% that evaluates to the elapsed milliseconds since the most recent midnight, server time, and %% secsFrom1970 %% that evals to the elapsed seconds since the midnight before January 1, 1970. Less well-known is the directive %% sexFrom1970 %% that will reveal an interesting fact about the past 41 years of the user's personal life.

Finally, %% date %% and %% time %% are synonyms; both return the current time in the site's default format (set in the Control Panel). If you call them with dateObj (the literal string) as an argument, the return value is in dateObj format.

Time and Date Formatting in Webcrossing

Webcrossing has built-in date formatting, which is identical in both WCTL and SSJS. This is date formatting not available in standard JavaScript. A time and date format is a list of time/date specifiers, mixed with literal characters such as : or @.

For example, in SSJS:

var now = new Date();
+ now.dateFormat( "H1:I2 a" );

This will produce the current time of day in Hours(one digit):Seconds(two digits) am/pm format, or, for example, 3:07 pm.

To do the same thing in WCTL:

%% set now date( dateObj ) %%
%% now.dateFormat( "H1:I2 a" ) %%

Alternatively in WCTL, if you don't care about the formatting particularly, you can just do %% date %% or %% time %% and get the default formatting settable in the Control Panel.

Here is the full list of the date formatting strings you can use.
Y2  2 character year
  Y4  4 character year
  M1  1 or 2 digit month
  M2  2 digit month
  MMM 3 character month, all caps
  Mmm 3 character month, leading cap
  M   full month name, leading cap
  mmm 3 character month, all lower case
  m   full month name, all lower case
  D1  1 or 2 digit day of month
  D2  2 digit day of month
  D3  1st, 2nd, 3rd, 4th...
  WWW 3 character day of week, all caps
  Www 3 character day of week, leading cap
  W   full day of week, leading cap
  H1  1 or 2 digit hour, 12 hour clock
  H2  2 digit hour, 12 hour clock
  H3  1 or 2 digit hour, 24 hour clock
  H4  2 digit hour, 24 hour clock
  I1  1 or 2 digit minutes
  I2  2 digit minutes
  S1  1 or 2 digit seconds
  S2  2 digit seconds
  L1  1, 2, or 3 digit milliseconds
  L3  3 digit milliseconds
  A   AM/PM, upper case
  a   am/pm, lower case
  N   output a "-" if the date/time is negative

  toutc delta from local time to GMT/UTC, as +HHMM or -HHMM
  $c  escape to embed "c" into the output string

For example:

FormatSample output
W, D2-Mmm-Y2 H4:I2:S2Monday, 05-Mar-11 09:52:13
M D1, Y4Jan 4, 2011

One important difference between standard Javascript and the Webcrossing extension to the Date object is that in Webcrossing, the month is 1-based instead of 0-based. That is to say, if it's  March, Date.getMonth() returns 2 while Date.dateFormat("M1") returns 3. The dateFormat is more oriented towards producing human-readable strings.

WCTL String Manipulation

Another reason I like WCTL is because it has a number of very handy string methods.

Here are some of my favorite slightly less common ones:
  1. markObjectionable: mark objectionable words

    This ties into the moderation system, but if you get a tiny bit creative you can do interesting things with it. First, you need to define some objectionable words in the Control Panel or folder in which you are working. Then, when you display message content, you do this:

    %% string.markObjectionable( HTMLbefore, HTMLafter ) %%

    This is normally used to highlight objectionable words in the preview pane before moderated users submit their posts, and in fact, the default values for HTMLbefore and HTMLafter are: ( "<font color=red>", "</font>" )

    However, you can also use this to comment out the actual objectionable word and replace it with @#$%* as we do in the Tabular Message View plugin:

    %% pathBodyFormatted.markObjectionable( "<!--", "-->@#$%*" ) %%

    If you aren't using the moderation machinery for anything else you could also use it to highlight desirable terms such as names of sponsors, etc.

  2. randomString: a random 11-character string

    %% randomString %% simply spits out 11 random mixed upper- and lower-case letters, which is handy for setting default passwords, creating unique names of things, etc. (Astute observers will notice it's not actually random, but for most purposes it will do just fine.)

    For example: nojfarsSJmD

  3. htmlToPlainText: converts HTML to similarly-formatted plain text

    Specifically, all SGML-escaped characters are converted. HTML-formatted white space is converted to plain-text. <p>, <br>, and <pre> are converted to similar-looking newlines. <li> is converted to an asterisk and a space. &nbsp; is converted to a normal space. </table> and </tr> are converted to newlines, and </td> is converted to a space.

    Handy if you are creating plain-text emails from HTML content.
These are just a few examples!

How to put HTML on the page with SSJS without quoting it

In the series of articles comparing and contrasting WCTL and SSJS, one of the points I made was that with SSJS, all html going to the response buffer has to be quoted, like this:

+ 'some text';

or this

var bb = new ByteBuffer();
bb += 'some text';
return bb;

But in the fine print on the summary table, I said there was an exception I would write about later.  Well, later is now.

But first, let's talk best practices. As illustrated above, there are two ways to get HTML into the response buffer:
  1. response.append( 'some text' ), or use the shortcut for that, + 'some text';
  2. concatenate everything into a ByteBuffer() and return that at the end of the function or page
Generally speaking, method #2 is better.  You avoid a lot of weird issues when mixing SSJS and WCTL related to the order in which things appear on the page, and there is a slight performance advantage.

So with that out of the way, let's look at that exception.  As it turns out, you can use pairs of %%'s (remember those from WCTL?) to delimit blocks of plain HTML to go to the response buffer.  And of course, you can include client-side JavaScript in that block if you want to.  But you can't replace any dynamic variables.  (So you might as well put that client-side JS into a separate file, it seems to me, and save the bandwidth).

The other thing about using this method is that the HTML inside those pairs of %%'s goes immediately into the response buffer as if you had +'d or response.appended it. Try as you might, you can't do something like this, because the text "this will not work" will appear immediately on the page.

var myHTML = %% <b>this will not work</b> %%

So that means you can't use it with the better method #2 above.  Keep it under your hat in case you run across the perfect use for it, but in my experience it's not all that helpful.

What to look for when you hire a community manager

Great article here. Most job ads ask for some degree of technical skills plus experience with social media. In this blog post, Richard Millington explains why that's exactly the wrong approach.

Filters: Login and Registration filters

Webcrossing "out of the box" already gives you a fair amount of flexibility to control the login and registration processes. You can choose to allow new users to register without any intervention. You can turn that feature off so that new users can only be registered by a sysadmin. You can set the registration process to include a validation email to ensure that new users provide a working email address, and you can restrict user privileges until the validation process is completed.

You can set the login process to be handled by the standard web form. You can also allow login via the HTTP basic authentication protocol. Webcrossing also supports validating login credentials with an external RADIUS, CalNet, or LDAP server.

And if all of that still does not meet your needs exactly, Webcrossing provides a handful of filters to modify the registration and login processes. They are:


All four of these filters manage the process by returning one of the following formats in the response buffer:

  • An empty string to continue with normal processing. This is as if the filter was not present at all. If working in WCTL it's usually wise to use the %% clearOutput %% directive to make sure the buffer is empty.
  • A fully-formatted page of HTML that begins with either <html> or HTTP. (If the latter, you must create all the HTTP headers yourself rather than letting the system supply them. You can use this feature to redirect the user to another page or site.) The page is returned to the user immediately with no further processing.
  • A WCTL userID. If the filter evaluates to a user ID, this user becomes the current user. Webcrossing creates an authentication certificate and/or cookies depending on site settings and they are available in subsequent page requests.
  • for the registration filters only, any other output (such as an HTML or text snippet) will be treated as an error message; the registration form will be redisplayed with this output as a message.
registerProcessFilter fires after a registration form is submitted but before any other processing. The current user inside the filter will always be userIsUnknown or, if the sysadmin is registering another user, userIsSysop.

registerFailureFilter is called after processing a registration form when the registration failed for any reason.

loginFilter fires after a successful login, but before processing the login's actionPath (that is, before redirecting to the original requested page if the login page was interposed). The current user is available; the original request including the query string and any cookies can be accessed by using the envir pseudo-object in WCTL or the request object in SSJS.

Finally, loginFailureFilter fires after a failed login. If the form submitted contained a valid user name but an incorrect password, the user object is set. As with loginFilter the original request is available, and the contents of the username and password form fields can be recovered from the form object.

Localization Manager (WCMS Part 6)

We've talked about a lot of different things regarding the WCMS - the Webcrossing Customization Management Suite - but so far, all of them have had to do with various flavors of plugins: what they do, how to install them, what kinds of plugins there are.

But the Localization Manager is a little different. It allows you - as the name suggests - to localize, or translate, the user interface into different languages, allowing users to view the site in the language of their choice (if you support more than one).

But the other interesting thing it allows you to do is change the verbiage in the default (US English) interface to account for UK spelling, change terminology like replacing the word "discussions" everywhere with "topics," and reword instructions as you see fit. It's very powerful, and requires no scripting!

As you can see, there are 4 basic parts to the Localization Manager. We'll start with the site charset.

What this does is place a meta tag with the charset in the <HEAD> of your document. You may want to choose ISO-8859-1 for most Latin character sets, or UTF-8 if you are supporting non-Latin languages.

The next control down in the menu is where you add and remove languages, and define what the default language is for users who have not yet made a choice in their preferences.

User preferences:

You can see here that we have added Pig Latin as a supported language, but left English as the default. It's important to note that this doesn't actually provide a translation, but just adds support for that language to your site so you can begin the process. If you are just using US English and only want to change some of the wording, you don't have to visit this page at all.

To provide the translations (or reword the text in English as you see fit), you can use either of the last two options: Edit language strings using web forms or Edit language strings using spreadsheets.

The first option brings up a page like this, where you select the language to edit or translate, and select the plugin whose resource strings you want to edit or translate.

You can either search for a specific string, like "discussions" in our example above, or view them all.

Once you've made the changes you need to make, you can save your changes.

If you have a large number of strings to edit or translate, it may be more convenient to use the Spreadsheet option. What this does is produce a spreadsheet for you to pass off to your translation team. When they've done their work, the finished spreadsheet is run through this page again to apply the translations.

The final option, not in the Localization Manager menu, is to use scripting to create and edit your custom strings file. If you are not afraid of JavaScript, this is perhaps the fastest option. Just put in the strings you need to change, though, or you will not be able to take advantages of future updates.

It's worth noting that the translation machinery will first attempt to deliver a given resource string in the language of the user's choice. If that is missing (due to an error, or an incomplete translation project), it will use the string for the "fallback" language - usually US English, but that can be changed by scripting if necessary.

As you can see, the WCMS Localization Manager is quite flexible, and entirely possible to use without any scripting! Next time we'll talk about some developer tools included with the WCMS.