Join us for a grand tour of Webcrossing - it's our favorite platform for web application development, and we're dying to tell you why we think so.
Debugging with log files
The easiest way to debug with Webcrossing is to just print values to the screen:
myvar: %% myvar %%<br>
or
+ '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 ) %%
or
%% "debugFile".fileWrite( "myVar:" & myVar & crlf ) %%
In SSJS, it looks like this:
"debugFile".fileAppend( "myMacro ran" + '\r\n' );
or
"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).
myvar: %% myvar %%<br>
or
+ '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 ) %%
or
%% "debugFile".fileWrite( "myVar:" & myVar & crlf ) %%
In SSJS, it looks like this:
"debugFile".fileAppend( "myMacro ran" + '\r\n' );
or
"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!
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, myfile.auto 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 aaaaaaHello.auto. Put it in your templates directory and reset the cache. Then, in the browser location bar, call your macro hello:
http://yoursite.com?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 aaaaaaHello.auto file, rename it so that it loads after some of your template files, maybe mmmmmmHello.auto. Test again. If you see "Hello, world!", you'll know that everything alphabetically up to your mmmmmmmHello.auto 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!
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, myfile.auto 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 aaaaaaHello.auto. Put it in your templates directory and reset the cache. Then, in the browser location bar, call your macro hello:
http://yoursite.com?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 aaaaaaHello.auto file, rename it so that it loads after some of your template files, maybe mmmmmmHello.auto. Test again. If you see "Hello, world!", you'll know that everything alphabetically up to your mmmmmmmHello.auto 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.crlf();
attach.crlf();
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.
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.crlf();
attach.crlf();
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:
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.
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 = User.open(changedUserId);
theUser[changedFieldName]
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 User.open 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.
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 = User.open(changedUserId);
theUser[changedFieldName]
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 User.open 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.
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.
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:
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:
- 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.
- 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?
- 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 :)
- 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) {
$('#newContentGoesHere').html(newText);
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) {
$('#newContentGoesHere').html(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 %%
</div>
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.
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:
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!
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) {
$('#newContentGoesHere').html(removeDoctype(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."
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) {
$('#newContentGoesHere').html(removeDoctype(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:
http://www.rockettheme.com/
http://www.shape5.com/
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.
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.
Subscribe to:
Posts (Atom)