diff --git a/include/class.task.php b/include/class.task.php
index 5f35fd5420d92575dcfa2b6afcc8a8718e79632e..676cb9d5e04ac8d82f7e67d0a6bea7e8163bd7c4 100644
--- a/include/class.task.php
+++ b/include/class.task.php
@@ -63,6 +63,7 @@ class TaskModel extends VerySimpleModel {
     const PERM_EDIT     = 'task.edit';
     const PERM_ASSIGN   = 'task.assign';
     const PERM_TRANSFER = 'task.transfer';
+    const PERM_REPLY    = 'task.reply';
     const PERM_CLOSE    = 'task.close';
     const PERM_DELETE   = 'task.delete';
 
@@ -87,6 +88,11 @@ class TaskModel extends VerySimpleModel {
                 /* @trans */ 'Transfer',
                 'desc'  =>
                 /* @trans */ 'Ability to transfer tasks between departments'),
+            self::PERM_REPLY => array(
+                'title' =>
+                /* @trans */ 'Post Reply',
+                'desc'  =>
+                /* @trans */ 'Ability to post task update'),
             self::PERM_CLOSE     => array(
                 'title' =>
                 /* @trans */ 'Close',
@@ -193,12 +199,36 @@ class TaskModel extends VerySimpleModel {
 RolePermission::register(/* @trans */ 'Tasks', TaskModel::getPermissions());
 
 
-class Task extends TaskModel implements Threadable {
+class Task extends TaskModel implements RestrictedAccess, Threadable {
     var $form;
     var $entry;
 
     var $_thread;
     var $_entries;
+    var $_answers;
+
+    var $lastrespondent;
+
+    function __onload() {
+        $this->loadDynamicData();
+    }
+
+    function loadDynamicData() {
+        if (!isset($this->_answers)) {
+            $this->_answers = array();
+            foreach (DynamicFormEntryAnswer::objects()
+                ->filter(array(
+                    'entry__object_id' => $this->getId(),
+                    'entry__object_type' => ObjectModel::OBJECT_TYPE_TASK
+                )) as $answer
+            ) {
+                $tag = mb_strtolower($answer->field->name)
+                    ?: 'field.' . $answer->field->id;
+                    $this->_answers[$tag] = $answer;
+            }
+        }
+        return $this->_answers;
+    }
 
     function getStatus() {
         return $this->isOpen() ? __('Open') : __('Completed');
@@ -281,6 +311,27 @@ class Task extends TaskModel implements Threadable {
         return $assignees ? implode($glue, $assignees):'';
     }
 
+    function getLastRespondent() {
+
+        if (!isset($this->lastrespondent)) {
+            $this->lastrespondent = Staff::objects()
+                ->filter(array(
+                'staff_id' => static::objects()
+                    ->filter(array(
+                        'thread__entries__type' => 'R',
+                        'thread__entries__staff_id__gt' => 0
+                    ))
+                    ->values_flat('thread__entries__staff_id')
+                    ->order_by('-thread__entries__id')
+                    ->limit(1)
+                ))
+                ->first()
+                ?: false;
+        }
+
+        return $this->lastrespondent;
+    }
+
     function getParticipants() {
         $participants = array();
         foreach ($this->getThread()->collaborators as $c)
@@ -402,7 +453,9 @@ class Task extends TaskModel implements Threadable {
             return false;
         }
 
-        $this->save(true);
+        if (!$this->save(true))
+            return false;
+
         if ($comments) {
             $errors = array();
             $this->postNote(array(
@@ -449,8 +502,15 @@ class Task extends TaskModel implements Threadable {
     }
 
     /* util routines */
+
+    function logEvent($state, $data=null, $user=null, $annul=null) {
+        $this->getThread()->getEvents()->log($this, $state, $data, $user, $annul);
+    }
+
     function assign(AssignmentForm $form, &$errors, $alert=true) {
+        global $thisstaff;
 
+        $evd = array();
         $assignee = $form->getAssignee();
         if ($assignee instanceof Staff) {
             if ($this->getStaffId() == $assignee->getId()) {
@@ -462,6 +522,11 @@ class Task extends TaskModel implements Threadable {
                 $errors['assignee'] = __('Agent is unavailable for assignment');
             } else {
                 $this->staff_id = $assignee->getId();
+                if ($thisstaff && $thisstaff->getId() == $assignee->getId())
+                    $evd['claim'] = true;
+                else
+                    $evd['staff'] = $assignee;
+
             }
         } elseif ($assignee instanceof Team) {
             if ($this->getTeamId() == $assignee->getId()) {
@@ -471,7 +536,7 @@ class Task extends TaskModel implements Threadable {
                         );
             } else {
                 $this->team_id = $assignee->getId();
-
+                $evd = array('team' => $assignee->getId());
             }
         } else {
             $errors['assignee'] = __('Unknown assignee');
@@ -480,6 +545,8 @@ class Task extends TaskModel implements Threadable {
         if ($errors || !$this->save(true))
             return false;
 
+        $this->logEvent('assigned', $evd);
+
         $this->onAssignment($assignee,
                 $form->getField('comments')->getClean(),
                 $alert);
@@ -487,39 +554,90 @@ class Task extends TaskModel implements Threadable {
         return true;
     }
 
-    function onAssignment($assignee, $note='', $alert=true) {
-        global $thisstaff;
+    function onAssignment($assignee, $comments='', $alert=true) {
+        global $thisstaff, $cfg;
 
         if (!is_object($assignee))
             return false;
 
         $assigner = $thisstaff ?: __('SYSTEM (Auto Assignment)');
+
         //Assignment completed... post internal note.
-        $title = sprintf(__('Task assigned to %s'),
-                (string) $assignee);
+        $note = null;
+        if ($comments) {
 
-        if (!$note) {
-            $note = $title;
-            $title = '';
+            $title = sprintf(__('Task assigned to %s'),
+                    (string) $assignee);
+
+            $errors = array();
+            $note = $this->postNote(
+                    array('note' => $comments, 'title' => $title),
+                    $errors,
+                    $assigner,
+                    false);
         }
 
-        $errors = array();
-        $note = $this->postNote(
-                array('note' => $note, 'title' => $title),
-                $errors,
-                $assigner,
-                false);
-
-        // Send alerts out
-        if (!$alert)
+        // Send alerts out if enabled.
+        if (!$alert || !$cfg->alertONTaskAssignment())
             return false;
 
+        if (!($dept=$this->getDept())
+            || !($tpl = $dept->getTemplate())
+            || !($email = $dept->getAlertEmail())
+        ) {
+            return true;
+        }
+
+        // Recipients
+        $recipients = array();
+        if ($assignee instanceof Staff) {
+            if ($cfg->alertStaffONTaskAssignment())
+                $recipients[] = $assignee;
+        } elseif (($assignee instanceof Team) && $assignee->alertsEnabled()) {
+            if ($cfg->alertTeamMembersONTaskAssignment() && ($members=$assignee->getMembers()))
+                $recipients = array_merge($recipients, $members);
+            elseif ($cfg->alertTeamLeadONTaskAssignment() && ($lead=$assignee->getTeamLead()))
+                $recipients[] = $lead;
+        }
+
+        if ($recipients
+            && ($msg=$tpl->getTaskAssignmentAlertMsgTemplate())) {
+
+            $msg = $this->replaceVars($msg->asArray(),
+                array('comments' => $comments,
+                      'assignee' => $assignee,
+                      'assigner' => $assigner
+                )
+            );
+            // Send the alerts.
+            $sentlist = array();
+            $options = $note instanceof ThreadEntry
+                ? array(
+                    'inreplyto' => $note->getEmailMessageId(),
+                    'references' => $note->getEmailReferences(),
+                    'thread' => $note)
+                : array();
+
+            foreach ($recipients as $k => $staff) {
+                if (!is_object($staff)
+                    || !$staff->isAvailable()
+                    || in_array($staff->getEmail(), $sentlist)) {
+                    continue;
+                }
+
+                $alert = $this->replaceVars($msg, array('recipient' => $staff));
+                $email->sendAlert($staff, $alert['subj'], $alert['body'], null, $options);
+                $sentlist[] = $staff->getEmail();
+            }
+        }
+
         return true;
     }
 
     function transfer(TransferForm $form, &$errors, $alert=true) {
-        global $thisstaff;
+        global $thisstaff, $cfg;
 
+        $cdept = $this->getDept();
         $dept = $form->getDept();
         if (!$dept || !($dept instanceof Dept))
             $errors['dept'] = __('Department selection required');
@@ -531,25 +649,77 @@ class Task extends TaskModel implements Threadable {
         if ($errors || !$this->save())
             return false;
 
-        // Transfer completed... post internal note.
-        $title = sprintf(__('%s transferred to %s department'),
-                __('Task'),
-                $dept->getName());
+        // Log transfer event
+        $this->logEvent('transferred');
 
+        // Post internal note if any
         $note = $form->getField('comments')->getClean();
-        if (!$note) {
-            $note = $title;
-            $title = '';
+        if ($note) {
+            $title = sprintf(__('%1$s transferred from %2$s to %3$s'),
+                    __('Task'),
+                   $cdept->getName(),
+                    $dept->getName());
+
+            $_errors = array();
+            $note = $this->postNote(
+                    array('note' => $note, 'title' => $title),
+                    $_errors, $thisstaff, false);
         }
-        $_errors = array();
-        $note = $this->postNote(
-                array('note' => $note, 'title' => $title),
-                $_errors, $thisstaff, false);
 
         // Send alerts if requested && enabled.
-        if (!$alert)
+        if (!$alert || !$cfg->alertONTaskTransfer())
             return true;
 
+        if (($email = $dept->getAlertEmail())
+             && ($tpl = $dept->getTemplate())
+             && ($msg=$tpl->getTaskTransferAlertMsgTemplate())) {
+
+            $msg = $this->replaceVars($msg->asArray(),
+                array('comments' => $note, 'staff' => $thisstaff));
+            // Recipients
+            $recipients = array();
+            // Assigned staff or team... if any
+            if ($this->isAssigned() && $cfg->alertAssignedONTaskTransfer()) {
+                if($this->getStaffId())
+                    $recipients[] = $this->getStaff();
+                elseif ($this->getTeamId()
+                    && ($team=$this->getTeam())
+                    && ($members=$team->getMembers())
+                ) {
+                    $recipients = array_merge($recipients, $members);
+                }
+            } elseif ($cfg->alertDeptMembersONTaskTransfer() && !$this->isAssigned()) {
+                // Only alerts dept members if the task is NOT assigned.
+                if ($members = $dept->getMembersForAlerts())
+                    $recipients = array_merge($recipients, $members);
+            }
+
+            // Always alert dept manager??
+            if ($cfg->alertDeptManagerONTaskTransfer()
+                && ($manager=$dept->getManager())) {
+                $recipients[] = $manager;
+            }
+
+            $sentlist = $options = array();
+            if ($note instanceof ThreadEntry) {
+                $options += array(
+                    'inreplyto'=>$note->getEmailMessageId(),
+                    'references'=>$note->getEmailReferences(),
+                    'thread'=>$note);
+            }
+
+            foreach ($recipients as $k=>$staff) {
+                if (!is_object($staff)
+                    || !$staff->isAvailable()
+                    || in_array($staff->getEmail(), $sentlist)
+                ) {
+                    continue;
+                }
+                $alert = $this->replaceVars($msg, array('recipient' => $staff));
+                $email->sendAlert($staff, $alert['subj'], $alert['body'], null, $options);
+                $sentlist[] = $staff->getEmail();
+            }
+        }
 
         return true;
     }
@@ -569,12 +739,77 @@ class Task extends TaskModel implements Threadable {
         if (!($note=$this->getThread()->addNote($vars, $errors)))
             return null;
 
+        $assignee = $this->getStaff();
+
         if (isset($vars['task_status']))
             $this->setStatus($vars['task_status']);
 
+        $this->onActivity(array(
+            'activity' => $note->getActivity(),
+            'threadentry' => $note,
+            'assignee' => $assignee
+        ), $alert);
+
         return $note;
     }
 
+    /* public */
+    function postReply($vars, &$errors, $alert = true) {
+        global $thisstaff, $cfg;
+
+
+        if (!$vars['poster'] && $thisstaff)
+            $vars['poster'] = $thisstaff;
+
+        if (!$vars['staffId'] && $thisstaff)
+            $vars['staffId'] = $thisstaff->getId();
+
+        if (!$vars['ip_address'] && $_SERVER['REMOTE_ADDR'])
+            $vars['ip_address'] = $_SERVER['REMOTE_ADDR'];
+
+        if (!($response = $this->getThread()->addResponse($vars, $errors)))
+            return null;
+
+        $assignee = $this->getStaff();
+        // Set status - if checked.
+        if ($vars['reply_status_id']
+            && $vars['reply_status_id'] != $this->getStatusId()
+        ) {
+            $this->setStatus($vars['reply_status_id']);
+        }
+
+        /*
+        // TODO: add auto claim setting for tasks.
+        // Claim on response bypasses the department assignment restrictions
+        if ($thisstaff
+            && $this->isOpen()
+            && !$this->getStaffId()
+            && $cfg->autoClaimTasks)
+        ) {
+            $this->staff_id = $thisstaff->getId();
+        }
+        */
+
+        $this->lastrespondent = $response->staff;
+        $this->save();
+
+        // Send activity alert to agents
+        $activity = $vars['activity'] ?: $response->getActivity();
+        $this->onActivity( array(
+                    'activity' => $activity,
+                    'threadentry' => $response,
+                    'assignee' => $assignee,
+                    ));
+        // Send alert to collaborators
+        if ($alert && $vars['emailcollab']) {
+            $this->notifyCollaborators($response,
+                array('signature' => $signature)
+            );
+        }
+
+        return $response;
+    }
+
     function pdfExport($options=array()) {
         global $thisstaff;
 
@@ -596,6 +831,233 @@ class Task extends TaskModel implements Threadable {
         exit;
     }
 
+    /* util routines */
+    function replaceVars($input, $vars = array()) {
+        global $ost;
+
+        return $ost->replaceTemplateVariables($input,
+                array_merge($vars, array('task' => $this)));
+    }
+
+    function asVar() {
+       return $this->getNumber();
+    }
+
+    function getVar($tag) {
+        global $cfg;
+
+        if ($tag && is_callable(array($this, 'get'.ucfirst($tag))))
+            return call_user_func(array($this, 'get'.ucfirst($tag)));
+
+        switch(mb_strtolower($tag)) {
+        case 'phone':
+        case 'phone_number':
+            return $this->getPhoneNumber();
+            break;
+        case 'staff_link':
+            return sprintf('%s/scp/tasks.php?id=%d', $cfg->getBaseUrl(), $this->getId());
+            break;
+        case 'create_date':
+            return new FormattedDate($this->getCreateDate());
+            break;
+         case 'due_date':
+            if ($due = $this->getEstDueDate())
+                return new FormattedDate($due);
+            break;
+        case 'close_date':
+            if ($this->isClosed())
+                return new FormattedDate($this->getCloseDate());
+            break;
+        case 'last_update':
+            return new FormattedDate($this->last_update);
+        default:
+            if (isset($this->_answers[$tag]))
+                // The answer object is retrieved here which will
+                // automatically invoke the toString() method when the
+                // answer is coerced into text
+                return $this->_answers[$tag];
+        }
+        return false;
+    }
+
+    static function getVarScope() {
+        $base = array(
+            'assigned' => __('Assigned agent and/or team'),
+            'close_date' => array(
+                'class' => 'FormattedDate', 'desc' => __('Date Closed'),
+            ),
+            'create_date' => array(
+                'class' => 'FormattedDate', 'desc' => __('Date created'),
+            ),
+            'dept' => array(
+                'class' => 'Dept', 'desc' => __('Department'),
+            ),
+            'due_date' => array(
+                'class' => 'FormattedDate', 'desc' => __('Due Date'),
+            ),
+            'number' => __('Task number'),
+            'recipients' => array(
+                'class' => 'UserList', 'desc' => __('List of all recipient names'),
+            ),
+            'status' => __('Status'),
+            'staff' => array(
+                'class' => 'Staff', 'desc' => __('Assigned/closing agent'),
+            ),
+            'subject' => 'Subject',
+            'team' => array(
+                'class' => 'Team', 'desc' => __('Assigned/closing team'),
+            ),
+            'thread' => array(
+                'class' => 'TaskThread', 'desc' => __('Task Thread'),
+            ),
+            'last_update' => array(
+                'class' => 'FormattedDate', 'desc' => __('Time of last update'),
+            ),
+        );
+
+        $extra = VariableReplacer::compileFormScope(TaskForm::getInstance());
+        return $base + $extra;
+    }
+
+    function onActivity($vars, $alert=true) {
+        global $cfg, $thisstaff;
+
+        if (!$alert // Check if alert is enabled
+            || !$cfg->alertONTaskActivity()
+            || !($dept=$this->getDept())
+            || !($email=$cfg->getAlertEmail())
+            || !($tpl = $dept->getTemplate())
+            || !($msg=$tpl->getTaskActivityAlertMsgTemplate())
+        ) {
+            return;
+        }
+
+        // Alert recipients
+        $recipients = array();
+        //Last respondent.
+        if ($cfg->alertLastRespondentONTaskActivity())
+            $recipients[] = $this->getLastRespondent();
+
+        // Assigned staff / team
+        if ($cfg->alertAssignedONTaskActivity()) {
+            if (isset($vars['assignee'])
+                    && $vars['assignee'] instanceof Staff)
+                 $recipients[] = $vars['assignee'];
+            elseif ($this->isOpen() && ($assignee = $this->getStaff()))
+                $recipients[] = $assignee;
+
+            if ($team = $this->getTeam())
+                $recipients = array_merge($recipients, $team->getMembers());
+        }
+
+        // Dept manager
+        if ($cfg->alertDeptManagerONTaskActivity() && $dept && $dept->getManagerId())
+            $recipients[] = $dept->getManager();
+
+        $options = array();
+        $staffId = $thisstaff ? $thisstaff->getId() : 0;
+        if ($vars['threadentry'] && $vars['threadentry'] instanceof ThreadEntry) {
+            $options = array(
+                'inreplyto' => $vars['threadentry']->getEmailMessageId(),
+                'references' => $vars['threadentry']->getEmailReferences(),
+                'thread' => $vars['threadentry']);
+
+            // Activity details
+            if (!$vars['message'])
+                $vars['message'] = $vars['threadentry'];
+
+            // Staff doing the activity
+            $staffId = $vars['threadentry']->getStaffId() ?: $staffId;
+        }
+
+        $msg = $this->replaceVars($msg->asArray(),
+                array(
+                    'note' => $vars['threadentry'], // For compatibility
+                    'activity' => $vars['activity'],
+                    'message' => $vars['message']));
+
+        $isClosed = $this->isClosed();
+        $sentlist=array();
+        foreach ($recipients as $k=>$staff) {
+            if (!is_object($staff)
+                // Don't bother vacationing staff.
+                || !$staff->isAvailable()
+                // No need to alert the poster!
+                || $staffId == $staff->getId()
+                // No duplicates.
+                || isset($sentlist[$staff->getEmail()])
+                // Make sure staff has access to task
+                || ($isClosed && !$this->checkStaffPerm($staff))
+            ) {
+                continue;
+            }
+            $alert = $this->replaceVars($msg, array('recipient' => $staff));
+            $email->sendAlert($staff, $alert['subj'], $alert['body'], null, $options);
+            $sentlist[$staff->getEmail()] = 1;
+        }
+
+    }
+
+    /*
+     * Notify collaborators on response or new message
+     *
+     */
+    function  notifyCollaborators($entry, $vars = array()) {
+        global $cfg;
+
+        if (!$entry instanceof ThreadEntry
+            || !($recipients=$this->getThread()->getParticipants())
+            || !($dept=$this->getDept())
+            || !($tpl=$dept->getTemplate())
+            || !($msg=$tpl->getTaskActivityNoticeMsgTemplate())
+            || !($email=$dept->getEmail())
+        ) {
+            return;
+        }
+
+        // Who posted the entry?
+        $skip = array();
+        if ($entry instanceof Message) {
+            $poster = $entry->getUser();
+            // Skip the person who sent in the message
+            $skip[$entry->getUserId()] = 1;
+            // Skip all the other recipients of the message
+            foreach ($entry->getAllEmailRecipients() as $R) {
+                foreach ($recipients as $R2) {
+                    if (0 === strcasecmp($R2->getEmail(), $R->mailbox.'@'.$R->host)) {
+                        $skip[$R2->getUserId()] = true;
+                        break;
+                    }
+                }
+            }
+        } else {
+            $poster = $entry->getStaff();
+        }
+
+        $vars = array_merge($vars, array(
+            'message' => (string) $entry,
+            'poster' => $poster ?: _S('A collaborator'),
+            )
+        );
+
+        $msg = $this->replaceVars($msg->asArray(), $vars);
+
+        $attachments = $cfg->emailAttachments()?$entry->getAttachments():array();
+        $options = array('inreplyto' => $entry->getEmailMessageId(),
+                         'thread' => $entry);
+
+        foreach ($recipients as $recipient) {
+            // Skip folks who have already been included on this part of
+            // the conversation
+            if (isset($skip[$recipient->getUserId()]))
+                continue;
+            $notice = $this->replaceVars($msg, array('recipient' => $recipient));
+            $email->send($recipient, $notice['subj'], $notice['body'], $attachments,
+                $options);
+        }
+    }
+
+    /* static routines */
     static function lookupIdByNumber($number) {
         $sql = 'SELECT id FROM '.TASK_TABLE
               .' WHERE `number`='.db_input($number);
@@ -710,7 +1172,7 @@ class Task extends TaskModel implements Threadable {
                         .sprintf('task.flags & %d != 0 ', TaskModel::ISOPEN)
                         .')';
 
-        if(!$staff->showAssignedOnly() && ($depts=$staff->getDepts())) //Staff with limited access just see Assigned tickets.
+        if(!$staff->showAssignedOnly() && ($depts=$staff->getDepts())) //Staff with limited access just see Assigned tasks.
             $where[] = 'task.dept_id IN('.implode(',', db_input($depts)).') ';
 
         $where = implode(' OR ', $where);
diff --git a/include/class.thread.php b/include/class.thread.php
index 20cb0f8f246a26e351813595d7c778a640422c70..eb1cd24ada471e2712634009370c0a24d91cf741 100644
--- a/include/class.thread.php
+++ b/include/class.thread.php
@@ -54,6 +54,7 @@ class Thread extends VerySimpleModel {
 
     var $_object;
     var $_collaborators; // Cache for collabs
+    var $_participants;
 
     function getId() {
         return $this->id;
@@ -211,13 +212,33 @@ class Thread extends VerySimpleModel {
 
         return true;
     }
+
+
+    //UserList of participants (collaborators)
+    function getParticipants() {
+
+        if (!isset($this->_participants)) {
+            $list = new UserList();
+            if ($collabs = $this->getActiveCollaborators()) {
+                foreach ($collabs as $c)
+                    $list->add($c);
+            }
+
+            $this->_participants = $list;
+        }
+
+        return $this->_participants;
+    }
+
+
     // Render thread
     function render($type=false, $options=array()) {
 
         $mode = $options['mode'] ?: self::MODE_STAFF;
 
         // Register thread actions prior to rendering the thread.
-        include_once INCLUDE_DIR . 'class.thread_actions.php';
+        if (!class_exists('tea_showemailheaders'))
+            include_once INCLUDE_DIR . 'class.thread_actions.php';
 
         $entries = $this->getEntries();
         if ($type && is_array($type))
@@ -1037,6 +1058,10 @@ implements TemplateVariable {
         return $this->email_info->save();
     }
 
+    function getActivity() {
+        return new ThreadActivity('', '');
+    }
+
     /* variables */
 
     function __toString() {
@@ -2189,6 +2214,12 @@ class ResponseThreadEntry extends ThreadEntry {
 
     const ENTRY_TYPE = 'R';
 
+    function getActivity() {
+        return new ThreadActivity(
+                _S('New Response'),
+                _S('New response posted'));
+    }
+
     function getSubject() {
         return $this->getTitle();
     }
@@ -2238,6 +2269,12 @@ class NoteThreadEntry extends ThreadEntry {
         return $this->getBody();
     }
 
+    function getActivity() {
+        return new ThreadActivity(
+                _S('New Internal Note'),
+                _S('New internal note posted'));
+    }
+
     static function create($vars, &$errors) {
         return self::add($vars, $errors);
     }
@@ -2521,4 +2558,46 @@ interface Threadable {
     function getThread();
     function postThreadEntry($type, $vars, $options=array());
 }
+
+/**
+ * ThreadActivity
+ *
+ * Object to thread activity
+ *
+ */
+class ThreadActivity implements TemplateVariable {
+    var $title;
+    var $desc;
+
+    function __construct($title, $desc) {
+        $this->title = $title;
+        $this->desc = $desc;
+    }
+
+    function getTitle() {
+        return $this->title;
+    }
+
+    function getDescription() {
+        return $this->desc;
+    }
+    function asVar() {
+        return (string) $this->getTitle();
+    }
+
+    function getVar($tag) {
+        if ($tag && is_callable(array($this, 'get'.ucfirst($tag))))
+            return call_user_func(array($this, 'get'.ucfirst($tag)));
+
+        return false;
+    }
+
+    static function getVarScope() {
+        return array(
+          'title' => __('Activity Title'),
+          'description' => __('Activity Description'),
+        );
+    }
+}
+
 ?>