Hesk Helpdesk (And Enhancements!)

I came across the free, open source Hesk Helpdesk (http://www.hesk.com) while I was looking for a light weight help system for a project that was starting up. The project has since been put on hold, but Hesk is still a great application for a helpdesk/knowledge base. Hesk is offered as a free downloadable PHP script, as well as a Hosted Version if you do not have a server. The hosted version will cost you $20 US a month. You can also by a license for the downloadable version for $39.95 US to remove the Hesk branding.

While this application is great…

  1. Allows unlimited staff
  2. Granular permissions to the system and help desk categories
  3. Excellent knowledge base built in
  4. Innovative system that doesn’t require user accounts for clients

It is lacking a few features. I plan to use this application to respond to clients from a number of different projects. As such, it would be nice if they didn’t see ALL of the problem types when submitting tickets. Because Hesk is open source, we can make a few tweaks to get directly to a specific problem type.

Hesk also allows custom fields for your tickets, but it shows all custom fields on all problem types. This can get confusing for users in a multi-project environment. I’m developing a little hack to get around this limitation.

I’ve outlined both of these changes required on my wiki, you can take a look here.

awstats Information

Just surfing the web and came across this excellent site with advanced awstats configuration information. As you all know, I do enjoy using awstats, and have had to modify it in the past.

Well, this site has lots of cool scripts and modifications to make awstats more useful: http://www.internetofficer.com/awstats/

My favourite modifications so far are:

And don’t forget to check out my own tutorial on adding robots (Blog Post/Wiki Entry)

Adding a robot to awstats

I’ve configured awstats to provide statistics on visitors of the websites on the server. I’ve also configured xymon to notify me when the website is not accessible. xymon uses a bot to check every 5 minutes to make sure the site is still up. As you can imagine, this generates a lot of hits, that aren’t really people.

So, since I don’t want to disable monitoring, I thought I should at least configure my stats so that they reflect people, and this bot.

awstats has an excellent robot detecting feature, but they do not yet detect xymon. So, in order to make the change, you need to dive into the robots.pm file, normally saved in /usr/local/awstats/wwwroot/cgi-bin/lib/

You will need to make two changes for it to detect this new robot (it tells you this at the top of the file). So, at the end of the @RobotsSearchIDOrder_list1 = ( array, I added:

And then at the top of Other robots reported by users in %RobotsHashIDLib = (, I added:

Once I deleted the files in /var/lib/awstats/ and reprocessed my logs, the statistics better reflected my visitors. Beware that if you delete files from /var/lib/awstats/ and don’t have the original log files around, you will lose the history for that period of time. Also, this change should be reflected on all sites that you have running on this server and using awstats.

My webserver only had logs for August, so I only deleted the file for August, and left the file for July with the missed detections since I cannot rebuild it. I’ve since changed the configuration of the log rotater.

Later I’ll be posting a more in depth tutorial on installing awstats, and also on modifying the robots.pm file, since I couldn’t find much about it online.

UPDATE: As promised, here is a more detailed how-to on updating the robots file: http://wiki.cornempire.net/doku.php?id=awstats:awstatsrobots

Xymon Graph All Blue

I just noticed this today. I monitor a number of sites and servers using the great Xymon system monitoring tool (Linky). It is based on early Big Brother code. Anyway, today I noticed that the graphs were all blue. Something like this:

Bad Xymon Graph

Bad Xymon Graph

I’m using Xymon 4.2.3 on the servers, and both servers show the graph like this using Firefox 3.5 on Linux. Google Chrome development version, SeaMonkey 1.1.5, Epiphany and Galeon all display the graph correctly.

I have no solution, but I thought I’d post my experience.

D2L Bulk Deactivation Part 2

Note:

Instead of editing my previous post to pieces, I thought it might be best if I repost my script with a better description of how it works. I’ve written up documentation of the script on the CornEmpire Software wiki which is available at http://wiki.cornempire.net. The documentation directly related to this script is available at http://wiki.cornempire.net/doku.php?id=d2l:bulkdeactivate

Introduction

One of the often requested features is to be able to bulk deactivate courses in D2L after they have completed. You can currently deactivate courses, but this is a one at a time effort. This has become more important as version 8.3 of D2L brought along a new My Courses widget, which allows users to see updates from each of their courses. Unfortunately, when course access ends, they can still see the updates. This has caused confusion for many users.
Another reason to deactivate courses is minimize the amount of clutter in the view of users. If course access has ended, and they cannot access the course, there is no need for the course to remain active (this could vary depending on how your roles are configured). A way around this is to script the bulk deactivation of courses. Through the use of some Javascript coding, we can instruct the web browser to visit each course, and deactivate it for us.

See the script in action.

Would you like to see more? I have the script, and it’s documentation available on this page: http://wiki.cornempire.net/doku.php?id=d2l:bulkdeactivate

If you have any questions, please post them below.

Desire2Learn Bulk Course Deactivation

Update

Please see my new post on this topic. Available at: http://www.cornempire.net/2009/07/07/d2l-bulk-deactivation-part-2/

Introduction

One of the often requested features is to be able to bulk deactivate courses in D2L after they have completed. You can currently deactivate courses, but this is a one at a time effort. This has become more important as version 8.3 of D2L brought along a new My Courses widget, which allows users to see updates from each of their courses. Unfortunately, when course access ends, they can still see the updates. This has caused confusion for many users.

Another reason to deactivate courses is minimize the amount of clutter in the view of users. If course access has ended, and they cannot access the course, there is no need for the course to remain active (this could vary depending on how your roles are configured). A way around this is to script the bulk deactivation of courses. Through the use of some Javascript coding, we can instruct the web browser to visit each course, and deactivate it for us.

Example

Take a look here for this script in action.

If you think that this might be useful for your organization, you can download the html file which includes the javascript.

For D2L Version 8.3
For D2L Version 8.4 (Thanks Dennis!)

Instructions on Use

Limitations

Getting a List Of Courses

The tricky part in all of this is actually getting the OU numbers for each of the courses you need to deactivate. For those who don’t know, the OU number is the org unit number, and is an internal identifier for D2L. You will notice the ou number in the URL of courses as ou=#####.

We run a report using the reporting tool that gives us the ou numbers (in one, non-ideal form. Hopefully D2L web services will eliminate the need for this).

In the Reporting tool, create a report using the Org Units dataset. With in the report, include the column for the course Path. This path contains the OU number for the course. Under the filter tab, add a few filters that will narrow down your search to the information you require. Our organization adds a semester code to all of our courses, so all courses that end in 200802 are courses that ran in the last semester. We can query these courses, and get a list of the ou numbers through the course offering paths. See below for some images of our report in action.

report1

report2

report3

report4

Then you have to do some work in manipulating the report to filter out the OU numbers. I select the whole column using firefox, and dump it into OpenOffice. Customize the import to break the lines on all of the dividing characters, and then I have a column of ou numbers. Then take that, and add in commas after all of the entries using a find and replace. You will need a text editor that can find and replace on characters you cannot see. Replace the new lines with commas.

Hopefully you can find a better way to get your list of OU numbers ;) . If you have a good way, leave a comment and let me know.

IE Error: Object Expected

I’ve been recently doing some Javascript coding and ran across an error in IE that I wasn’t noticing in Firefox (well…there are hundreds of these, but this one is a show stopper).

It is a small validation script for a form to do some checking before submitting.  It’s pretty simple, take a look:


function validateForm(){
	/*
	 * First, get the fields.
	 */
	var name = document.getElementById('name').value;
	var title = document.getElementById('title').value;
	var phone = document.getElementById('phone').value;
	var email = document.getElementById('email').value;
	var description = document.getElementById('description').value;
	var cost = document.getElementById('cost').value;
	//pic1 = document.getElementById('pic1').value;
	//pic2 = document.getElementById('pic2').value;
	if(name == ""){
		alert('You must enter your name.');
	}
	else if(title == ""){
		alert('You must enter give your ad a title.');
	}
	else if(phone == "" && email == ""){
		alert('You must enter a phone number or email address.');
	}
	else if(description == ""){
		alert('You must enter a description of the item you want to buy, sell or trade.');
	}
	else if(isNaN(cost)){
		alert('You must enter a numeric cost, without the $ symbol.');
	}
	else{
		document.getElementById('submit').style.display = 'block';
		alert('Please press the submit button below.');
	}
}

Executing the script on a blank form in FF would give the correct response, Enter in a name. In IE, I would get an error on Line 15 about Object Expected (in the script, line 15 is actually a commented out line, so that error isn’t very helpful).

I then remembered something about IE automatically creating variables from a form by the name/id of the form field on the page. Since my variable name, and field name were the same, it would try to compare its self created object against my test, instead of the variable that I was assigning, thus creating an error.

To work around this, I had to rename all of my variables (I could have also renamed all of my fields):


function validateForm(){
	/*
	 * First, get the fields.
	 */
	var bsname = document.getElementById('name').value;
	var bstitle = document.getElementById('title').value;
	var bsphone = document.getElementById('phone').value;
	var bsemail = document.getElementById('email').value;
	var bsdescription = document.getElementById('description').value;
	var bscost = document.getElementById('cost').value;
	//pic1 = document.getElementById('pic1').value;
	//pic2 = document.getElementById('pic2').value;
	if(bsname == ""){
		alert('You must enter your name.');
	}
	else if(bstitle == ""){
		alert('You must enter give your ad a title.');
	}
	else if(bsphone == "" && bsemail == ""){
		alert('You must enter a phone number or email address.');
	}
	else if(bsdescription == ""){
		alert('You must enter a description of the item you want to buy, sell or trade.');
	}
	else if(isNaN(bscost)){
		alert('You must enter a numeric cost, without the $ symbol.');
	}
	else{
		document.getElementById('submit').style.display = 'block';
		alert('Please press the submit button below.');
	}
}

After this, it runs fine in both browsers. I remember hitting my head off the wall for a LONG time when I originally encountered this error, so hopefully this will save someone else some time.

Migrating pwman3 Database

I recently updated my server and I store a good number of passwords in my pwman database. It is a great little command line program for storing passwords in a secure manner. I do this so that I can log into my machine from anywhere in the world to retrieve passwords if needed, and I’m not tied to a thumb drive that I could lose/break, or a computer that may not have the necessary OS/ports for me to use.

If you need to migrate your database, you can follow these steps:

  1. Grab a copy of your old database. It is usually called pwman.db and is stored in your /home/yourname/.pwman/ If you can’t find it, there is a config file in this directory that will tell you where it is.
  2. Install pwman3 on your new server. (command for Debian based Linux OSes)
    apt-get install pwman3
  3. Run pwman3 and configure the same master password as your old database. (Not sure if this is required, but I thought it was a good idea)
  4. Make a copy of your database, and then put your old one in your new /home/yourname/.pwman/ folder.

The next time you run pwman3, you will have all your old passwords!

Mythtv: Unable to access MythTV Perl API

In my last few attempts at configuring my mythtv installation (my most recent being today when I replaced the hard drive), I’ve gotten this error when I log into my box through SSH:

MythTV status for localhost
===========================
Status...........: Wed Feb 18 2009, 8:30 PM
Total Disk Space.: Total space is 548,770 MB, with 158,049 MB used (28.8%)
Next Recording In: 2 Hours, 59 Minutes
 
Encoders:
mythbox (1) - Idle
 
Scheduled Recordings:
2009-02-18 23:30:00 - CSI: NY (WBZ)
2009-02-19 00:30:00 - Stargate SG-1 (Space: The Imagination Station)
2009-02-19 10:30:00 - Stargate SG-1 (Space: The Imagination Station)
2009-02-19 12:30:00 - First Wave (Space: The Imagination Station)
2009-02-19 19:30:00 - Coronation Street (CBNT CBC St. John's)
2009-02-19 22:30:00 - CSI: Crime Scene Investigation (WBZ)
 
Schedule Conflicts:
Unable to access MythTV Perl API. Try with --verbose to find out why.

I get most of the status for the server, but I don’t get the read outs of the conflicts because of the error.

What happens (as far as I understand it) is that when the cronjob generates the motd, it runs as root, and root does not have a link to the mythtv config, so it can’t get the data from the database.  So, to get around this, we need to create a link in /root/.mythtv/ to the config.xml file for the server.  For me, this runs from my home directory /home/thomas/.mythtv/config.xml so:

sudo ln -s /home/thomas/.mythtv/config.xml /root/config.xml

Once you make this change, the next time the cronjob runs it should populate the motd correctly.

Notes: I’m running this server on Ubuntu 8.04.02 64-bit.

You can find more information on this issue here.

Desire2Learn / Javascript Picture Library

Introduction

The picture library built into Desire2Learn doesn’t allow for instructors to create their own libraries. Rather, all images are shared across all courses. I wrote this bit of javascript to simulate and extend the idea of a picture library in D2L (this code can also be used for any HTML page). There are two parts to the library:

The Widget Code (or HTML code for any page)
This code is the skeleton of the library. The javascript code modifies this code in order to display the images.
The Javascript Code
This is the brain of the library. It must be installed somewhere locally and linked to the widget/HTML page. There is a configuration section at the top of the file where you can configure the library.

Here is the widget/HTML code that must be displayed on the pages.
You should not edit this code as it will cause the library to malfunction.


<html>
<head>
	<script src="picturelibrary.js" type="text/javascript"></script>
</head>
<body>

	<div id="imgwdgt">
		<img src="piclibimages/test.png" id="imghldr" />
		<p id="imgcptn"></p>
	</div>
	<script type="text/javascript">
		startLibrary();
	</script>

</body>
</html>

Here is the javascript code: picturelibrary.js
You will need to right click this link and save the file to your computer before you can upload it to your D2L shell/website.

Notes About The Code

Not all the variables need to be set a certain way in order for the code to function. However, it is a good idea to set up any variables that you don’t want to use to be false. Some variables require others, so read the comments in the code carefully. I’ve also included them below:


/**
	This script presents a picture library to the user.  It has several configurable
	options which are outlined below.
	With this script, you can:
		1) Present a slide show to users
		2) Present a navigatable interface for users to scroll through the library
		3) Release an image to them based on a set date/time
		4) Release a random image from the library
**/
/**
	=== CONFIG START ===
	Here you need to specify the path to this script.  To do this you must go to the
	Edit Course section of the site, and go to Manage Files.  Then, copy the URL for
	this javascript file.  Paste the URL into the variable below.  Then remove the
	filename at the end of the URL "picturelibrary.js".  Replace the file name with
	"piclibimages/" (no quotes)
*/
var path = "http://online.mun.ca/content/Sandbox/TT/Sp07/piclibimages/";

/**
	List your images here.  By default, it will look under the piclibimages folder
	in your course files.  Change the path above if you want to load from a different
	location.

	Place the file names in the array below.  A comma must be placed after each entry
	except for the last entry in the list.
*/

var images = new Array(
	"dataspace-code.gif",
	"MedSchoolCrestSmall.gif",
	"crest-toothpaste.png"
);

/**
	List your captions here.  

	If you enable captions below, you must have at least as many captions as you have images
	above or you will probably get a javascript error when the script scrolls through the
	images.  You can have blank captions, denoted with "" in the array below.
*/
var captions = new Array(
	"The icon I created for the phpLive -> Dataspace integration.",
	"Medicine School Crest",
	"Crest Toothpaste"
);

/**
	This array contains dates for the date release function of this script.  If you do not
	want to use date release, you can leave this blank.

	Each entry goes with an entry in the images array and should be formatted with
	the startdate-enddate for the image.  Only one image can be displayed at a time
	with this method.
	A sample entry is: 200809190000-200809222359
		This will allow the	image to be viewed on Friday the 19th until Monday the 22nd.

	If you want your images	to rotate through a day, you can place a * for the date and
	just include the time.
	A sample entry is: *0000-*1200
		This will allow your image to appear on the site between midnight and noon every
		day.
*/

var releasedates = new Array(
	"*0000-*1230",
	"*1231-*1300",
	"*1301-*2359"
);
//var releasedates = new Array(
//	"200809170000-200809182359",
//	"200809190000-200809202359",
//	"200809210000-200809222359"
//);

/**
	Here you can set your options for the script.
	resize = controls if images are automatically resized.  If true, width and
			 height must be set.  These are measured in pixels.  Be careful using this
			 as it will likely distort your pictures horribly.  It is much better to
			 resize your pictures before uploading them to the system.
	navigation = controls if users can navigate through images, or if they see
				 a loop of images.
	imagepause = the amount of time in milliseconds that an image appears before
				 changing.  Only used if navigation is false. 4000 = 4 seconds
	captionsdisplay = if this is set to true, the caption is pulled from the above array
			   		  and displayed with the picture.
	daterelease = Shows an image on a specific date/for a specific time.  The datearray
			      must store the dates in the format yearmonthday-yearmonthday or *time-*time.
			      the first date/time being when the image is displayed, and the second
			      when it ends.
	imagestyle = This is a CSS style tag that will be added to the image tag.
	captionstyle = This is a CSS style take that will be added to the caption.
	widgetsytle = This is a CSS style tag that well be added to the div around the image
				  and caption.
	randomstart = This variable controls whether or not the script begins on a random image.
	singlerandom = If true, this displays a single random image when the page is loaded.
*/			      

var resize = false;
var width = "";
var height = "";
var navigation = true;
var imagepause = "5000";
var captionsdisplay = true;
var daterelease = true;
var imagestyle = "margin: 0 auto; display: block; padding: 2px;";
var captionstyle = "margin: 1px; text-size: 10px; text-align: center; border: 1px dashed gray;";
var widgetstyle = "border: 2px solid black;";
var randomstart = true;
var singlerandom = false;

/**
	=== CONFIG END ===
	This is the end of configurable items for this script.  Do not edit the code below.
*/

Also of note is the use of CSS styles. I cannot figure out how to apply an arbitrary CSS style string to an HTML element. I can change specific things, but I cannot allow any style tags. If anyone knows how, please contact me.

Because of this limitation, the CSS code will only recognize:

You can add in your own code to the bottom by editing the doStyle function, or you can apply your own styles to the raw widget/HTML code. You must leave the style variables blank in this script.

Final Thoughts

I hope you find this useful, either for your D2L course, or for your personal homepage. I’ll probably post updates from time to time on this script depending on the feedback I receive. If you have any comments, you can leave them below. Take a look at some of the variable configurations below to see examples of how the code can be used.

Examples

This configuration allows you to display a slide show that updates every 5 seconds. It has a random starting point, and has captions enabled.
var resize = false;
var width = “”;
var height = “”;
var navigation = false;

var imagepause = “5000″;
var captionsdisplay = true;
var daterelease = false;
var imagestyle = “”;
var captionstyle = “”;
var widgetstyle = “”;

var randomstart = true;

View Example 1


This configuration allows you to navigate the images and it also displays captions. Styles are also applied to the image, captions and box around them both.

var resize = false;
var width = “”;
var height = “”;
var navigation = true;
var imagepause = “5000″;
var captionsdisplay = true;

var daterelease = false;
var imagestyle = “margin: 0 auto; display: block; padding: 2px;”;
var captionstyle = “margin: 1px; font-size: 10px; text-align: center; border: 1px dashed gray;”;
var widgetstyle = “border: 2px solid black;”;
var randomstart = false;

View Example 2


This configuration shows a different image depending on the time of day. To best simulate Newfoundland weather. No captions are displayed. Not all images are displayed at any part of the day.

var resize = false;
var width = “”;
var height = “”;
var navigation = false;
var imagepause = “5000″;
var captionsdisplay = false;

var daterelease = true;
var imagestyle = “”;
var captionstyle = “”;
var widgetstyle = “”;
var randomstart = false;

var releasedates = new Array(

“*-*”,
“*0800-*0830″,
“*0926-*1000″,
“*1001-*1030″,
“*0831-*0855″,
“*0546-*0759″,

“*1031-*1130″,
“*1146-*1200″,
“*-*”,
“*1201-*1230″,
“*-*”,
“*-*”,

“*-*”,
“*1131-*1145″,
“*-*”,
“*1231-*1300″,
“*-*”,
“*-*”,

“*1301-*1500″,
“*-*”,
“*-*”,
“*-*”,
“*-*”,
“*1501-*1600″,

“*1601-*1700″,
“*1701-*1800″,
“*1801-*1900″,
“*1901-*2000″,
“*2001-*2229″,
“*2230-*2330″,

“*0000-*0030″,
“*0431-*0500″,
“*0031-*0130″,
“*2330-*2359″,
“*0501-*0545″,
“*0131-*0300″,

“*0301-*0330″,
“*0331-*0430″,
“*0000-*0000″,
“*-*”,
“*-*”,
“*-*”,

“*0856-*0925″,
“*-*”
);

View Example 3


This configuration shows a single image. It is random from the list of images.
var resize = false;
var width = “”;
var height = “”;
var navigation = false;

var imagepause = “5000″;
var captionsdisplay = true;
var daterelease = false;
var imagestyle = “”;
var captionstyle = “”;
var widgetstyle = “”;

var randomstart = true;
var singlerandom = true;

View Example 4


This configuration shows a slideshow with a 5 second pause, and a random start.
var resize = false;
var width = “”;
var height = “”;
var navigation = false;
var imagepause = “5000″;
var captionsdisplay = false;

var daterelease = false;
var imagestyle = “”;
var captionstyle = “”;
var widgetstyle = “”;
var randomstart = false;
var singlerandom = false;

View Example 5