Event Notification Handler
Ticket:56
Believe it or not, I wrote this code before I found out about PageWatches but I think my approach is a little different.
Basically, I wanted something that would email people in my office whenever somebody posted a comment on a page of interest. I thought I would make it a little more robust however. This should be a framework that will allow wikka to automatically react to a variety of page events (i.e. commenting a page, editing a page etc) and trigger some response (i.e. emailing a user, sending an http request to a server etc.)
This isn't finished, yet it is currently running in my office, so I know it works.
Steps:
- Create new event watching table.
- Create a handler to register an event for a page to watch.
- Create a response function (i.e. email notification).
- Add code to look for and process a page event. Then call a response function.
- Make handler easily accessible for users.
1.) Create Table
CREATE TABLE `wikka_watches` (
`page_tag` VARCHAR(75) NOT NULL,
`watcher` VARCHAR(75) NOT NULL,
`comment` ENUM('Y','N') DEFAULT 'N' NOT NULL,
`edit` ENUM('Y','N') DEFAULT 'N' NOT NULL
)
TYPE = myisam;
ALTER TABLE `wikka_watches` ADD UNIQUE (`page_tag`,`watcher`)
ALTER TABLE `wikka_watches` ADD INDEX (`page_tag`)
ALTER TABLE `wikka_watches` ADD INDEX (`watcher`)
`page_tag` VARCHAR(75) NOT NULL,
`watcher` VARCHAR(75) NOT NULL,
`comment` ENUM('Y','N') DEFAULT 'N' NOT NULL,
`edit` ENUM('Y','N') DEFAULT 'N' NOT NULL
)
TYPE = myisam;
ALTER TABLE `wikka_watches` ADD UNIQUE (`page_tag`,`watcher`)
ALTER TABLE `wikka_watches` ADD INDEX (`page_tag`)
ALTER TABLE `wikka_watches` ADD INDEX (`watcher`)
This will create a table that will store a page_tag and watcher pair. The watcher isn't restricted to being a username. It could be the name of a wikipingserver or something like that. Each column after that corresponds to a particular page event with a simple indication of whether or not that event is being watched for.
2.) Create 'addwatch' handler
Create a file called addwatch.php in your handlers directory. Add this code:
<?php
/**
* Creates a watch on this page for a watcher.
*
* Usage: append /addwatch to the URL of the page
*
* This handler registers a watch in the database on this page
* for a watcher. Watches can be used to generate events when
* certain things are done to a page. (i.e. email a user when a page
* is commented on.)
*
* @package Handlers
* @subpackage
* @name addwatch
*
* @author {@link http://wikka.jsnx.com/RyeBread RyeBread} - original idea and code - Dec.06.2005
* @copyright Copyright © 2005, Ryan Jarvis
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
* @version 0.1
* @since Wikka 1.1.6.X
*
* @todo
* 1. Allow handler to take in params specifying what kind of watch should be made
* 2. Enable setting of watcher that isn't a user (i.e. a wikiping server).
*/
do {
if (!$this->page) {
$this->SetRedirectMessage("Found no page for this handler.");
break;
}
if (!$this->GetUser()) {
$this->SetRedirectMessage("You must be registered to add a watch.");
break;
}
//TODO: get type of watch from a param
//passed into the handler.
//
//get type of watch
$type = "comment";
//check if watch type is valid
$validity = $this->Query("SHOW COLUMNS FROM " . $this->config["table_prefix"] . "watches LIKE '" . $type . "'");
if (mysql_num_rows($validity) == 0) {
$this->SetRedirectMessage("Watch type is invalid.");
break;
}
//get page name
$tag = $this->GetPageTag();
//TODO: optionally specify the id of the
//watcher instead of assuming user name.
//
//get watcher
$watcher = $this->GetUserName();
//register watch in database
$existing = $this->Query(
"SELECT * FROM " . $this->config["table_prefix"] . "watches " .
"WHERE page_tag = '" . mysql_real_escape_string($tag) . "'" .
"AND watcher = '" . mysql_real_escape_string($watcher) . "'");
if (mysql_num_rows($existing) == 0 ) {
$this->Query(
"INSERT INTO " . $this->config["table_prefix"] . "watches SET ".
"page_tag = '" . mysql_real_escape_string($tag) . "', " .
"watcher = '" . mysql_real_escape_string($watcher) . "', " .
$type . " = 'Y'");
$this->SetRedirectMessage("You are now watching this page for " . $type . "s.");
break;
}
if (mysql_result($existing, 0, $type) == 'Y') {
$this->SetRedirectMessage("You are already watching this page for " . $type . "s.");
} else {
$this->Query(
"UPDATE " . $this->config["table_prefix"] . "watches SET ".
"page_tag = '" . mysql_real_escape_string($tag) . "', " .
"watcher = '" . mysql_real_escape_string($watcher) . "', " .
$type . " = 'Y'");
$this->SetRedirectMessage("You are now watching this page for " . $type . "s.");
}
mysql_free_result($existing);
} while (0);
$this->Redirect($this->Href());
?>
/**
* Creates a watch on this page for a watcher.
*
* Usage: append /addwatch to the URL of the page
*
* This handler registers a watch in the database on this page
* for a watcher. Watches can be used to generate events when
* certain things are done to a page. (i.e. email a user when a page
* is commented on.)
*
* @package Handlers
* @subpackage
* @name addwatch
*
* @author {@link http://wikka.jsnx.com/RyeBread RyeBread} - original idea and code - Dec.06.2005
* @copyright Copyright © 2005, Ryan Jarvis
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
* @version 0.1
* @since Wikka 1.1.6.X
*
* @todo
* 1. Allow handler to take in params specifying what kind of watch should be made
* 2. Enable setting of watcher that isn't a user (i.e. a wikiping server).
*/
do {
if (!$this->page) {
$this->SetRedirectMessage("Found no page for this handler.");
break;
}
if (!$this->GetUser()) {
$this->SetRedirectMessage("You must be registered to add a watch.");
break;
}
//TODO: get type of watch from a param
//passed into the handler.
//
//get type of watch
$type = "comment";
//check if watch type is valid
$validity = $this->Query("SHOW COLUMNS FROM " . $this->config["table_prefix"] . "watches LIKE '" . $type . "'");
if (mysql_num_rows($validity) == 0) {
$this->SetRedirectMessage("Watch type is invalid.");
break;
}
//get page name
$tag = $this->GetPageTag();
//TODO: optionally specify the id of the
//watcher instead of assuming user name.
//
//get watcher
$watcher = $this->GetUserName();
//register watch in database
$existing = $this->Query(
"SELECT * FROM " . $this->config["table_prefix"] . "watches " .
"WHERE page_tag = '" . mysql_real_escape_string($tag) . "'" .
"AND watcher = '" . mysql_real_escape_string($watcher) . "'");
if (mysql_num_rows($existing) == 0 ) {
$this->Query(
"INSERT INTO " . $this->config["table_prefix"] . "watches SET ".
"page_tag = '" . mysql_real_escape_string($tag) . "', " .
"watcher = '" . mysql_real_escape_string($watcher) . "', " .
$type . " = 'Y'");
$this->SetRedirectMessage("You are now watching this page for " . $type . "s.");
break;
}
if (mysql_result($existing, 0, $type) == 'Y') {
$this->SetRedirectMessage("You are already watching this page for " . $type . "s.");
} else {
$this->Query(
"UPDATE " . $this->config["table_prefix"] . "watches SET ".
"page_tag = '" . mysql_real_escape_string($tag) . "', " .
"watcher = '" . mysql_real_escape_string($watcher) . "', " .
$type . " = 'Y'");
$this->SetRedirectMessage("You are now watching this page for " . $type . "s.");
}
mysql_free_result($existing);
} while (0);
$this->Redirect($this->Href());
?>
As it stands right now, when a registered user invokes this handler on a page, the wikka_watches table will be updated to indicate that this user wants to be notified whenever this page is commented on.
3.) Create 'notify' base function
In wikka.php, I added this function at line 972 before MAINTENANCE
//COMMUNICATION
function Notify($to = "", $subject = "", $body = "", $altbody = "")
{
<insert your specific mailing code here>
}
function Notify($to = "", $subject = "", $body = "", $altbody = "")
{
<insert your specific mailing code here>
}
Obviously, how you send mail is dependent on your server setup. I don't use the default mail() function. Instead, I have phpmailer setup, and I use it on an SMTP configuration. If somebody wants to see what I wrote for this function that uses phpmailer, let me know.
Also, it should probably be called 'NotifyOnEmail' as there might be additional notify functions created that belong to this family.
4.) Invoke Event Processing somewhere
Keeping in mind that I want people to be notified of comments made on pages, I decided to invoke the event processing in the 'addcomment.php' action.
Firstly, I have no idea if this is the best place for it to go (as opposed to being added directly in the SaveComment() function). Secondly, there needs to be a generic event-processing-wrapper of some kind that takes a callback function (such as notify) as a parameter. I'm working on that.
Add the following to 'addcomment.php', line 18, right after STORE NEW COMMENT
//trigger watches
$watches_table = $this->config["table_prefix"] . "watches";
$users_table = $this->config["table_prefix"] . "users";
$watch_query = "SELECT " . $users_table . ".email " .
"FROM " . $watches_table . "," . $users_table . " " .
"WHERE " . $watches_table . ".page_tag = '" . $this->tag . "' " .
"AND " . $watches_table . ".watcher = " . $users_table . ".name";
$watches = $this->LoadAll($watch_query);
foreach ($watches as $email) {
if ($email["email"] == '') {
continue;
}
$this->Notify($email["email"], $this->tag . " commented by " . $this->GetUserName(), $body);
}
$watches_table = $this->config["table_prefix"] . "watches";
$users_table = $this->config["table_prefix"] . "users";
$watch_query = "SELECT " . $users_table . ".email " .
"FROM " . $watches_table . "," . $users_table . " " .
"WHERE " . $watches_table . ".page_tag = '" . $this->tag . "' " .
"AND " . $watches_table . ".watcher = " . $users_table . ".name";
$watches = $this->LoadAll($watch_query);
foreach ($watches as $email) {
if ($email["email"] == '') {
continue;
}
$this->Notify($email["email"], $this->tag . " commented by " . $this->GetUserName(), $body);
}
5.) Make handler visible
Obviously, I want to make it easy for my users to add watches, so I just hacked the following. It adds a link to the footer.
Add the following to footer.php, line 5 after the 'Edit Page'
echo $this->GetUser() ? "<a href=\"".$this->href("addwatch")."\" title=\"Click to watch this page\">Watch page</a> ::\n" : "";
Now I don't really want to hack footer directly, plus, I want to be able to support adding watches of different types, so I need to eventually construct a separate menu altogether.
- How about a {{watch}} action instead? --NilsLindenberg
Conclusion
Whenever a user in my wiki comes across a particular page of interest, he or she can click on the "Watch page" link at the bottom, and it will record the request to be notified. Thereafter, whenever somebody leaves a comment on that page, the user will receive an email containing the comment in the body of the message.
TODO:
- Create an "removewatch" handler.
- Create a form in the user settings page that will allow people to manage their watches and add/remove/edit at will.
Comments/feedback highly desired. Thank you.
:)
CategoryUserContributions