<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>ics.php</filename>
    </added>
    <added>
      <filename>images/ics.gif</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -93,7 +93,10 @@ class action_plugin_task extends DokuWiki_Action_Plugin {
         // save .task meta file
         $my =&amp; plugin_load('helper', 'task');
         $task = array(
-          'user'     =&gt; $user,
+          'user'     =&gt; array(
+            'id'   =&gt; $_SERVER['REMOTE_USER'],
+            'name' =&gt; $INFO['userinfo']['name'],
+            'mail' =&gt; $INFO['userinfo']['mail']),
           'date'     =&gt; array('due' =&gt; $my-&gt;_interpretDate($date)),
           'priority' =&gt; strspn($priority, '!'),
           'status'   =&gt; $status,
@@ -153,11 +156,16 @@ class action_plugin_task extends DokuWiki_Action_Plugin {
     
     // assign task to a user
     if (!$task['user'] &amp;&amp; ($status == 1)){
-      if (!$INFO['userinfo']['name']) return 'show'; // no name of logged in user
+      if (!$_SERVER['REMOTE_USER']) return 'show'; // no logged in user
       $wiki = rawWiki($ID);
       $summary = $this-&gt;getLang('mail_changedtask').': '.$this-&gt;getLang('accepted');
       $new = preg_replace('/~~TASK:?/', '~~TASK:'.$INFO['userinfo']['name'], $wiki);
       if ($new != $wiki) saveWikiText($ID, $new, $summary, true); // save as minor edit
+      $task['user'] = array(
+        'id'   =&gt; $_SERVER['REMOTE_USER'],
+        'name' =&gt; $INFO['userinfo']['name'],
+        'mail' =&gt; $INFO['userinfo']['mail'],
+      );
     }
         
     // save .task meta file and clear xhtml cache</diff>
      <filename>action.php</filename>
    </modified>
    <modified>
      <diff>@@ -139,7 +139,7 @@ class helper_plugin_task extends DokuWiki_Plugin {
       $result[$task['key']] = array(
         'id'       =&gt; $id,
         'date'     =&gt; $date,
-        'user'     =&gt; $task['user'],
+        'user'     =&gt; $task['user']['name'],
         'status'   =&gt; $this-&gt;statusLabel($task['status']),
         'priority' =&gt; $task['priority'],
         'perm'     =&gt; $perm,
@@ -161,10 +161,11 @@ class helper_plugin_task extends DokuWiki_Plugin {
    */
   function readTask($id){
     $file = metaFN($id, '.task');
-    if (!@file_exists($file)){ //@remove
+    if (!@file_exists($file)){
       $data = p_get_metadata($id, 'task');
       if (is_array($data)){
         $data['date'] = array('due' =&gt; $data['date']);
+        $data['user'] = array('name' =&gt; $data['user']);
         $meta = array('task' =&gt; NULL);
         if ($this-&gt;writeTask($id, $data)) p_set_metadata($id, $meta);
       }
@@ -189,13 +190,15 @@ class helper_plugin_task extends DokuWiki_Plugin {
     unset($data['exists']);
     
     // set creation or modification time
-    if (!is_array($data['date'])) $data['date'] = array('due' =&gt; $data['date']); //@remove
+    if (!is_array($data['date'])) $data['date'] = array('due' =&gt; $data['date']);
     if (!@file_exists($file) || !$data['date']['created']){
       $data['date']['created'] = time();
     } else {
       $data['date']['modified'] = time();
     }
     
+    if (!is_array($data['user'])) $data['user'] = array('name' =&gt; $data['user']);
+    
     if (!isset($data['status'])){    // make sure we don't overwrite status
       $current = unserialize(io_readFile($file, false));
       $data['status'] = $current['status'];
@@ -204,7 +207,7 @@ class helper_plugin_task extends DokuWiki_Plugin {
     }
     
     // generate vtodo for iCal file download
-    // $data['vtodo'] = $this-&gt;_vtodo($data);
+    $data['vtodo'] = $this-&gt;_vtodo($id, $data);
     
     // generate sortkey with priority and creation date
     $data['key'] = chr($data['priority'] + 97).(2000000000 - $data['date']['created']);
@@ -260,7 +263,8 @@ class helper_plugin_task extends DokuWiki_Plugin {
     global $INFO;
     
     if (!$user) return false;
-    if (($user == $_SERVER['REMOTE_USER']) || ($user == $INFO['userinfo']['name']))
+    if (($user['id'] == $_SERVER['REMOTE_USER'])
+      || ($user['name'] == $INFO['userinfo']['name']))
       return true;
     return false;
   }
@@ -329,7 +333,92 @@ class helper_plugin_task extends DokuWiki_Plugin {
   
     mail_send($to, $subject, $text, $conf['mailfrom'], '', $bcc);
   }
-
+  
+  /**
+   * Generates a VTODO section for iCal file download
+   */
+  function _vtodo($id, $task){
+    if (!defined('CRLF')) define('CRLF', &quot;\r\n&quot;);
+    
+    $meta = p_get_metadata($id);
+    
+    $ret = 'BEGIN:VTODO'.CRLF.
+      'UID:'.$id.'@'.$_SERVER['SERVER_NAME'].CRLF.
+      'URL:'.wl($id, '', true, '&amp;').CRLF.
+      'SUMMARY:'.$this-&gt;_vsc($meta['title']).CRLF;
+    if ($meta['description']['abstract'])
+      $ret .= 'DESCRIPTION:'.$this-&gt;_vsc($meta['description']['abstract']).CRLF;
+    if ($meta['subject'])
+      $ret .= 'CATEGORIES:'.$this-&gt;_vcategories($meta['subject']).CRLF;
+    if ($task['date']['created'])
+      $ret .= 'CREATED:'.$this-&gt;_vdate($task['date']['created']).CRLF;
+    if ($task['date']['modified'])
+      $ret .= 'LAST-MODIFIED:'.$this-&gt;_vdate($task['date']['modified']).CRLF;
+    if ($task['date']['due'])
+      $ret .= 'DUE:'.$this-&gt;_vdate($task['date']['due']).CRLF;
+    if ($task['date']['completed'])
+      $ret .= 'COMPLETED:'.$this-&gt;_vdate($task['date']['completed']).CRLF;
+    if ($task['user']) $ret .= 'ORGANIZER;CN=&quot;'.$this-&gt;_vsc($task['user']['name']).'&quot;:'.
+      'MAILTO:'.$task['user']['mail'].CRLF;
+    $ret .= 'STATUS:'.$this-&gt;_vstatus($task['status']).CRLF;
+    if (is_numeric($task['priority']))
+      $ret .= 'PRIORITY:'.(7 - ($task['priority'] * 2)).CRLF;
+    $ret .= 'CLASS:'.$this-&gt;_vclass($id).CRLF.
+      'END:VTODO'.CRLF;
+    return $ret;
+  }
+  
+  /**
+   * Encodes vCard / iCal special characters
+   */
+  function _vsc($string){
+    $search = array(&quot;\\&quot;, &quot;,&quot;, &quot;;&quot;, &quot;\n&quot;, &quot;\r&quot;);
+    $replace = array(&quot;\\\\&quot;, &quot;\\,&quot;, &quot;\\;&quot;, &quot;\\n&quot;, &quot;\\n&quot;);
+    return str_replace($search, $replace, $string);
+  }
+  
+  /**
+   * Generates YYYYMMDD&quot;T&quot;hhmmss&quot;Z&quot; UTC time date format (ISO 8601 / RFC 3339)
+   */
+  function _vdate($date, $extended = false){
+    $date = $date + date('Z', $date); // calculate UTC time
+    if ($extended) return date('Y-m-d\TH:i:s\Z', $date);
+    else return date('Ymd\THis\Z', $date);
+  }
+  
+  /**
+   * Returns VTODO status
+   */
+  function _vstatus($status){
+    switch ($status){
+    case -1:
+      return 'CANCELLED';
+    case 1: case 2:
+      return 'IN-PROCESS';
+    case 3: case 4:
+      return 'COMPLETED';
+    default:
+      return 'NEEDS-ACTION';
+    }
+  }
+  
+  /**
+   * Returns VTODO categories
+   */
+  function _vcategories($cat){
+    if (!is_array($cat)) $cat = explode(' ', $cat);
+    return join(',', $this-&gt;_vsc($cat));
+  }
+  
+  /**
+   * Returns access classification for VTODO
+   */
+  function _vclass($id){
+    global $USERINFO; // checks access rights for anonymous user
+    if (auth_aclcheck($id, '', $USERINFO['grps'])) return 'PUBLIC';
+    else return 'PRIVATE';
+  }
+  
 }
   
 //Setup VIM: ex: et ts=4 enc=utf-8 :</diff>
      <filename>helper.php</filename>
    </modified>
    <modified>
      <diff>@@ -8,18 +8,22 @@ div.dokuwiki #task__newtask_form input.edit {
   width: 95%;
 }
 
-div.dokuwiki div.task {
+div.dokuwiki div.vcalendar {
   margin-bottom: 1em;
 }
 
-div.dokuwiki div.task fieldset {
+div.dokuwiki div.vcalendar fieldset {
   padding: 0 0.5em 0.5em 0.5em;
   border: 1px solid #ffc561;
   width: 90%;
 }
 
+div.dokuwiki div.vcalendar img.summary {
+  vertical-align: middle;
+}
+
 div.dokuwiki div.newtask_form table.blind,
-div.dokuwiki div.task table.blind {
+div.dokuwiki div.vcalendar table.blind {
   border: 0;
   padding: 0;
   border-spacing: 3px;
@@ -27,7 +31,7 @@ div.dokuwiki div.task table.blind {
 }
 
 div.dokuwiki div.newtask_form table.blind th,
-div.dokuwiki div.task table.blind th {
+div.dokuwiki div.vcalendar table.blind th {
   text-align: right;
   color: __text_neu__;
   font-weight: normal;
@@ -35,23 +39,24 @@ div.dokuwiki div.task table.blind th {
 }
 
 div.dokuwiki div.newtask_form table.blind td,
-div.dokuwiki div.task table.blind td {
+div.dokuwiki div.vcalendar table.blind td {
   width: 50%;
+  text-align: left;
 }
 
-div.dokuwiki div.task table.blind tr.due { color: #ffa200; font-weight: bold; }
-div.dokuwiki div.task table.blind tr.overdue { color: #ff0040; font-weight: bold; }
+div.dokuwiki div.vcalendar table.blind tr.due { color: #ffa200; font-weight: bold; }
+div.dokuwiki div.vcalendar table.blind tr.overdue { color: #ff0040; font-weight: bold; }
 
-div.dokuwiki div.task fieldset.priority1 { background-color: #fff7e8; }
-div.dokuwiki div.task fieldset.priority2 { background-color: #fff1d9; }
-div.dokuwiki div.task fieldset.priority3 { background-color: #ffe9c2; }
+div.dokuwiki div.vcalendar fieldset.priority1 { background-color: #fff7e8; }
+div.dokuwiki div.vcalendar fieldset.priority2 { background-color: #fff1d9; }
+div.dokuwiki div.vcalendar fieldset.priority3 { background-color: #ffe9c2; }
 
 div.dokuwiki tr.priority1 { background-color: #fff7e8; }
 div.dokuwiki tr.priority2 { background-color: #fff1d9; }
 div.dokuwiki tr.priority3 { background-color: #ffe9c2; }
 
-div.dokuwiki div.task fieldset.due { border: 1px solid #ffa200; }
-div.dokuwiki div.task fieldset.overdue { border: 1px solid #ff0040; }
+div.dokuwiki div.vcalendar fieldset.due { border: 1px solid #ffa200; }
+div.dokuwiki div.vcalendar fieldset.overdue { border: 1px solid #ff0040; }
 
 div.dokuwiki th.status,
 div.dokuwiki td.status {</diff>
      <filename>style.css</filename>
    </modified>
    <modified>
      <diff>@@ -11,12 +11,15 @@ if (!defined('DOKU_INC')) die();
 
 if (!defined('DOKU_LF')) define('DOKU_LF', &quot;\n&quot;);
 if (!defined('DOKU_TAB')) define('DOKU_TAB', &quot;\t&quot;);
-if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
+if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC.'lib/plugins/');
 
 require_once(DOKU_PLUGIN.'syntax.php');
  
 class syntax_plugin_task_task extends DokuWiki_Syntax_Plugin {
 
+  var $my   = NULL;
+  var $task = array();
+
   function getInfo(){
     return array(
       'author' =&gt; 'Esther Brunner',
@@ -50,48 +53,51 @@ class syntax_plugin_task_task extends DokuWiki_Syntax_Plugin {
       $date = $my-&gt;_interpretDate($date);
       
       $current = $my-&gt;readTask($ID);
-      if (($current['user'] != $user)
+      if (($current['user']['name'] != $user)
         || ($current['date']['due'] != $date)
         || ($current['priority'] != $priority)){
         $task = array(
-          'user'     =&gt; $user,
-          'date'     =&gt; $date,
+          'user'     =&gt; array('name' =&gt; $user),
+          'date'     =&gt; array('due' =&gt; $date),
           'priority' =&gt; $priority,
-        );
+        );                                        // @todo: re-check!
         $my-&gt;writeTask($ID, $task);
       }
     }   
     return array($user, $date, $priority);
   }      
  
-  function render($mode, &amp;$renderer, $data){
-    global $conf;
-  
+  function render($mode, &amp;$renderer, $data){  
     list($user, $date, $priority) = $data;
     
     // XHTML output
     if ($mode == 'xhtml'){
       
-      // strip time from preferred date format
-      $onlydate = trim($conf['dformat'], 'AaBGgHhiOTZ:');
-      
       // prepare data
+      $this-&gt;_loadHelper();
       $status = $this-&gt;_getStatus($user, $sn);
       $due = '';
       if ($date &amp;&amp; ($sn &lt; 3)){
         if ($date + 86400 &lt; time()) $due = 'overdue';
         elseif ($date &lt; time()) $due = 'due';
       }
-      if ($priority) $class = ' class=&quot;priority'.$priority.($due ? ' '.$due : '').'&quot;';
-      if ($due) $due = ' class=&quot;'.$due.'&quot;';
+      $class = ' class=&quot;vtodo';
+      if ($priority) $class .= ' priority'.$priority;
+      if ($due){
+        $class .= ' '.$due;
+        $due = ' class=&quot;'.$due.'&quot;';
+      }
+      $class .= '&quot;';
       
       // generate output
-      $renderer-&gt;doc .= '&lt;div class=&quot;task&quot;&gt;'.DOKU_LF.
-        '&lt;fieldset'.$class.'&gt;'.DOKU_LF.
-        DOKU_TAB.'&lt;legend&gt;'.$this-&gt;getLang('task').'&lt;/legend&gt;'.DOKU_LF.
+      $renderer-&gt;doc .= '&lt;div class=&quot;vcalendar&quot;&gt;'.DOKU_LF.
+        '&lt;fieldset'.$class.'&gt;'.DOKU_LF.DOKU_TAB.
+        '&lt;legend&gt;'.$this-&gt;_icsDownload().$this-&gt;getLang('task').'&lt;/legend&gt;'.DOKU_LF.
         '&lt;table class=&quot;blind&quot;&gt;'.DOKU_LF;
-      if ($user) $this-&gt;_tablerow('user', hsc($user), $renderer);
-      if ($date) $this-&gt;_tablerow('date', date($onlydate, $date), $renderer, $due);
+      if ($user)
+        $this-&gt;_tablerow('user', $this-&gt;_hCalUser($user), $renderer, '', 'organizer');
+      if ($date)
+        $this-&gt;_tablerow('date', $this-&gt;_hCalDate($date), $renderer, $due);
       $this-&gt;_tablerow('status', $status, $renderer);
       $renderer-&gt;table_close();
       $renderer-&gt;doc .= '&lt;/fieldset&gt;'.DOKU_LF.
@@ -108,48 +114,59 @@ class syntax_plugin_task_task extends DokuWiki_Syntax_Plugin {
   /**
    * Outputs a table row
    */
-  function _tablerow($header, $data, &amp;$renderer, $class = ''){
-    $renderer-&gt;doc .= DOKU_TAB.'&lt;tr'.$class.'&gt;'.DOKU_LF.DOKU_TAB.DOKU_TAB;
+  function _tablerow($header, $data, &amp;$renderer, $trclass = '', $tdclass = ''){
+    if ($tdclass) $tdclass = ' class=&quot;'.$tdclass.'&quot;';
+  
+    $renderer-&gt;doc .= DOKU_TAB.'&lt;tr'.$trclass.'&gt;'.DOKU_LF.DOKU_TAB.DOKU_TAB;
     $renderer-&gt;tableheader_open(1, '');
-    $renderer-&gt;doc .= hsc($this-&gt;getLang($header)).':';
+    if ($header) $renderer-&gt;doc .= hsc($this-&gt;getLang($header)).':';
     $renderer-&gt;tableheader_close();
-    $renderer-&gt;tablecell_open();
-    $renderer-&gt;doc .= $data;
+    $renderer-&gt;doc .= '&lt;td'.$tdclass.'&gt;'.$data;
     $renderer-&gt;tablecell_close();
     $renderer-&gt;tablerow_close();
   }
   
   /**
+   * Loads the helper plugin and gets task data for current ID
+   */
+  function _loadHelper(){
+    global $ID;
+    $this-&gt;my   =&amp; plugin_load('helper', 'task');
+    if (!is_object($this-&gt;my)) return false;
+    $this-&gt;task = $this-&gt;my-&gt;readTask($ID);
+    return $true;
+  }
+  
+  /**
    * Returns the status cell contents
    */
   function _getStatus($user, &amp;$status){
-    global $INFO, $ID;
+    global $INFO;
     
     $ret = '';
-    
-    $my =&amp; plugin_load('helper', 'task');
-    $task = $my-&gt;readTask($ID);
-    $status = $task['status'];
-    $responsible = $my-&gt;_isResponsible($user);
+    $status = $this-&gt;task['status'];
+    $responsible = $this-&gt;my-&gt;_isResponsible($user);
     
     if ($INFO['perm'] == AUTH_ADMIN){
-      $ret = $this-&gt;_statusMenu(array(-1, 0, 1, 2, 3, 4), $status, $my);
+      $ret = $this-&gt;_statusMenu(array(-1, 0, 1, 2, 3, 4), $status);
     } elseif ($responsible){
-      if ($status &lt; 3) $ret = $this-&gt;_statusMenu(array(-1, 0, 1, 2, 3), $status, $my);
+      if ($status &lt; 3) $ret = $this-&gt;_statusMenu(array(-1, 0, 1, 2, 3), $status);
     } else {
-      if ($status == 0) $ret = $this-&gt;_statusMenu(array(0, 1), $status, $my);
-      elseif ($status == 3) $ret = $this-&gt;_statusMenu(array(2, 3, 4), $status, $my);
+      if ($status == 0) $ret = $this-&gt;_statusMenu(array(0, 1), $status);
+      elseif ($status == 3) $ret = $this-&gt;_statusMenu(array(2, 3, 4), $status);
     }
        
-    if (!$ret &amp;&amp; $my) $ret = $my-&gt;statusLabel($status);
+    if (!$ret &amp;&amp; $this-&gt;my) $ret = $this-&gt;my-&gt;statusLabel($status);
     
-    return $ret;
+    return '&lt;abbr class=&quot;status&quot; title=&quot;'.$this-&gt;my-&gt;_vstatus($status).'&quot;&gt;'.
+      $ret.
+      '&lt;/abbr&gt;';
   }
     
   /**
    * Returns the XHTML for the status popup menu
    */
-  function _statusMenu($options, $status, &amp;$my){
+  function _statusMenu($options, $status){
     global $ID, $lang;
         
     $ret = '&lt;form id=&quot;task__changetask_form&quot; method=&quot;post&quot; action=&quot;'.script().'&quot; accept-charset=&quot;'.$lang['encoding'].'&quot;&gt;'.DOKU_LF.
@@ -160,7 +177,7 @@ class syntax_plugin_task_task extends DokuWiki_Syntax_Plugin {
     foreach ($options as $option){
       $ret .= DOKU_TAB.DOKU_TAB.'&lt;option value=&quot;'.$option.'&quot;';
       if ($status == $option) $ret .= ' selected=&quot;selected&quot;';
-      $ret .= '&gt;'.$my-&gt;statusLabel($option).'&lt;/option&gt;'.DOKU_LF;
+      $ret .= '&gt;'.$this-&gt;my-&gt;statusLabel($option).'&lt;/option&gt;'.DOKU_LF;
     }
     $ret .= DOKU_TAB.'&lt;/select&gt;'.DOKU_LF.
       DOKU_TAB.'&lt;input class=&quot;button&quot; type=&quot;submit&quot; value=&quot;'.$this-&gt;getLang('btn_change').'&quot; /&gt;'.DOKU_LF.
@@ -168,7 +185,41 @@ class syntax_plugin_task_task extends DokuWiki_Syntax_Plugin {
       '&lt;/form&gt;';
     return $ret;
   }
-   
+  
+  /**
+   * Returns the download link for the iCal file
+   */
+  function _icsDownload(){
+    global $ID, $INFO;
+    $uid = hsc($ID.'@'.$_SERVER['SERVER_NAME']);
+    $title = hsc($INFO['meta']['title']);
+    $link = DOKU_BASE.'lib/plugins/task/ics.php?id='.$ID;
+    $src = DOKU_BASE.'lib/plugins/task/images/ics.gif';
+    return '&lt;a href=&quot;'.$link.'&quot; class=&quot;uid&quot; title=&quot;'.$uid.'&quot;&gt;&lt;img src=&quot;'.$src.'&quot;'.
+      ' class=&quot;summary&quot; alt=&quot;'.$title.'&quot; title=&quot;'.$title.'&quot; width=&quot;16&quot; height=&quot;16&quot;/&gt;'.
+      '&lt;/a&gt; ';
+  }
+  
+  /**
+   * Returns the organizer in hCalendar format as hCard
+   */
+  function _hCalUser($user){
+    return '&lt;span class=&quot;vcard&quot;&gt;&lt;span class=&quot;fn&quot;&gt;'.hsc($user).'&lt;/span&gt;&lt;/span&gt;';
+  }
+  
+  /**
+   * Returns the date in hCalendar format
+   */
+  function _hCalDate($date){
+    global $conf;
+    
+    // strip time from preferred date format
+    $onlydate = trim($conf['dformat'], 'AaBGgHhiOTZ:');
+    
+    return '&lt;abbr class=&quot;due&quot; title=&quot;'.$this-&gt;my-&gt;_vdate($date, true).'&quot;&gt;'.
+      date($onlydate, $date).
+      '&lt;/abbr&gt;';
+  }
 }
  
 //Setup VIM: ex: et ts=4 enc=utf-8 :</diff>
      <filename>syntax/task.php</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>9847e190ec1788342ea26a67218896163bb82543</id>
    </parent>
  </parents>
  <author>
    <name>wikidesign</name>
    <email>chi@chimeric.de</email>
  </author>
  <url>http://github.com/dokufreaks/plugin-task/commit/3567ed0a4272b2574e8ea870b481b1ca54c10957</url>
  <id>3567ed0a4272b2574e8ea870b481b1ca54c10957</id>
  <committed-date>2007-01-04T12:33:12-08:00</committed-date>
  <authored-date>2007-01-04T12:33:12-08:00</authored-date>
  <message>6 hCalendar and .ics download support

darcs-hash:20070104203312-89468-251fba2022f36d9446132d9c4a132114be2950d9.gz</message>
  <tree>78eddfbaa45c770969dbb3ca3680d9a5f723999a</tree>
  <committer>
    <name>wikidesign</name>
    <email>chi@chimeric.de</email>
  </committer>
</commit>
