From 0dbac3218130dfe418d6c7dc162f819c746bec2d Mon Sep 17 00:00:00 2001
From: thomascube <thomas@roundcube.net>
Date: Thu, 04 Sep 2008 14:20:27 -0400
Subject: [PATCH] Enable export of contacts as vCard + DRY

---
 program/js/list.js                      |    6 +
 CHANGELOG                               |    4 +
 program/include/rcube_shared.inc        |    8 +
 program/include/rcube_vcard.php         |   11 +-
 skins/default/templates/addcontact.html |    2 
 program/steps/addressbook/export.inc    |   43 ++++++++++
 program/steps/mail/get.inc              |   18 +---
 program/js/app.js                       |  101 ++++++++++++++-----------
 8 files changed, 131 insertions(+), 62 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 3bba337..92a08c6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,10 @@
 CHANGELOG RoundCube Webmail
 ---------------------------
 
+2008/09/04 (thomasb)
+----------
+- Enable export of address book contacts as vCard
+
 2008/09/04 (alec)
 ----------
 - Truncate very long (above 50 characters) attachment filenames when displaying
diff --git a/program/include/rcube_shared.inc b/program/include/rcube_shared.inc
index 0dd661e..5008251 100644
--- a/program/include/rcube_shared.inc
+++ b/program/include/rcube_shared.inc
@@ -37,8 +37,14 @@
 
   header("Expires: ".gmdate("D, d M Y H:i:s")." GMT");
   header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
-  header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
+  header("Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
   header("Pragma: no-cache");
+  
+  // We need to set the following headers to make downloads work using IE in HTTPS mode.
+  if (isset($_SERVER['HTTPS'])) {
+    header('Pragma: ');
+    header('Cache-Control: ');
+  }
 }
 
 
diff --git a/program/include/rcube_vcard.php b/program/include/rcube_vcard.php
index 560d37d..3ad47a5 100644
--- a/program/include/rcube_vcard.php
+++ b/program/include/rcube_vcard.php
@@ -28,7 +28,10 @@
  */
 class rcube_vcard
 {
-  private $raw = array();
+  private $raw = array(
+    'FN' => array(),
+    'N' => array(array('','','','','')),
+  );
 
   public $business = false;
   public $displayname;
@@ -98,7 +101,7 @@
    * @param string Field value
    * @param string Section name
    */
-  public function set($field, $value, $section = 'home')
+  public function set($field, $value, $section = 'HOME')
   {
     switch ($field) {
       case 'name':
@@ -222,7 +225,7 @@
 
   private static function rfc2425_fold($val)
   {
-    return preg_replace('/:([^\n]{72,})/e', '":\n  ".rtrim(chunk_split("\\1", 72, "\n  "))', $val) . "\n\n";
+    return preg_replace('/:([^\n]{72,})/e', '":\n  ".rtrim(chunk_split("\\1", 72, "\n  "))', $val) . "\n";
   }
 
 
@@ -325,7 +328,7 @@
       }
     }
 
-    return "BEGIN:VCARD\nVERSION:3.0\n{$vcard}END:VCARD\n";
+    return "BEGIN:VCARD\nVERSION:3.0\n{$vcard}END:VCARD";
   }
 
 
diff --git a/program/js/app.js b/program/js/app.js
index 7ba8572..3e49fc3 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -205,10 +205,7 @@
         if (this.env.messagecount)
           this.enable_command('select-all', 'select-none', 'expunge', true);
 
-        if (this.env.messagecount 
-            && (this.env.mailbox == this.env.trash_mailbox || this.env.mailbox == this.env.junk_mailbox 
-              || this.env.mailbox.match('^' + RegExp.escape(this.env.trash_mailbox) + RegExp.escape(this.env.delimiter)) 
-              || this.env.mailbox.match('^' + RegExp.escape(this.env.junk_mailbox) + RegExp.escape(this.env.delimiter))))
+        if (this.purge_mailbox_test())
           this.enable_command('purge', true);
 
         this.set_page_buttons();
@@ -282,6 +279,9 @@
           this.enable_command('save', true);
         else
           this.enable_command('search', 'reset-search', 'moveto', 'import', true);
+          
+        if (this.contact_list && this.contact_list.rowcount > 0)
+          this.enable_command('export', true);
 
         this.enable_command('list', true);
         break;
@@ -1002,7 +1002,17 @@
         }
         else
           this.goto_url('import');
-        break
+        break;
+        
+      case 'export':
+        if (this.contact_list.rowcount > 0) {
+          var add_url = '';
+          if (this.env.search_request)
+            add_url = '_search='+this.env.search_request;
+        
+          this.goto_url('export', add_url);
+        }
+        break;
 
       // collapse/expand folder
       case 'collapse-folder':
@@ -1490,7 +1500,15 @@
     return true;
     };
 
-  
+  // test if purge command is allowed
+  this.purge_mailbox_test = function()
+  {
+    return (this.env.messagecount && (this.env.mailbox == this.env.trash_mailbox || this.env.mailbox == this.env.junk_mailbox 
+      || this.env.mailbox.match('^' + RegExp.escape(this.env.trash_mailbox) + RegExp.escape(this.env.delimiter)) 
+      || this.env.mailbox.match('^' + RegExp.escape(this.env.junk_mailbox) + RegExp.escape(this.env.delimiter))));
+  };
+
+
   // move selected messages to the specified mailbox
   this.move_messages = function(mbox)
     {
@@ -1900,7 +1918,8 @@
         }
       }
   };
-
+  
+  
   /*********************************************************/
   /*********           login form methods          *********/
   /*********************************************************/
@@ -3599,6 +3618,7 @@
       }
     
     this.contact_list.insert_row(row);
+    this.enable_command('export', (this.contact_list.rowcount > 0));
     };
 
 
@@ -3739,53 +3759,46 @@
       eval(request_obj.get_text());
 
     // process the response data according to the sent action
-    switch (request_obj.__action)
-      {
-
+    switch (request_obj.__action) {
       case 'delete':
-	if (this.task == 'addressbook')
-	  {
-	    var uid = this.contact_list.get_selection();
-	    this.enable_command('compose', (uid && this.contact_list.rows[uid]));
-	    this.enable_command('delete', 'edit', (uid && this.contact_list.rows[uid] && this.env.address_sources && !this.env.address_sources[this.env.source].readonly));
-	    break;  
-	  }
+        if (this.task == 'addressbook') {
+          var uid = this.contact_list.get_selection();
+          this.enable_command('compose', (uid && this.contact_list.rows[uid]));
+          this.enable_command('delete', 'edit', (uid && this.contact_list.rows[uid] && this.env.address_sources && !this.env.address_sources[this.env.source].readonly));
+          this.enable_command('export', (this.contact_list && this.contact_list.rowcount > 0));
+        }
+      
       case 'moveto':
-        if (this.env.action=='show')
+        if (this.env.action == 'show')
           this.command('list');
         else if (this.message_list)
           this.message_list.init();
-	  
+        break;
+        
       case 'purge':
       case 'expunge':      
-	if (!this.env.messagecount && this.task == 'mail')
-    	  {
-	    // clear preview pane content
-	    if (this.env.contentframe)
-	      this.show_contentframe(false);
-	    // disable commands useless when mailbox is empty
-	    this.enable_command('show', 'reply', 'reply-all', 'forward', 'moveto', 'delete', 'mark', 'viewsource',
-	      'print', 'load-attachment', 'purge', 'expunge', 'select-all', 'select-none', 'sort', false);
-	  }
-	break;
-
-      case 'list':
-	this.msglist_select(this.message_list);
+        if (!this.env.messagecount && this.task == 'mail') {
+          // clear preview pane content
+          if (this.env.contentframe)
+            this.show_contentframe(false);
+          // disable commands useless when mailbox is empty
+          this.enable_command('show', 'reply', 'reply-all', 'forward', 'moveto', 'delete', 'mark', 'viewsource',
+            'print', 'load-attachment', 'purge', 'expunge', 'select-all', 'select-none', 'sort', false);
+        }
+        break;
 
       case 'check-recent':
       case 'getunread':
-	if (this.task == 'mail')
-	{
-	  this.enable_command('show', 'expunge', 'select-all', 'select-none', 'sort', (this.env.messagecount > 0));
-	  var mailboxtest = (this.env.mailbox == this.env.trash_mailbox || this.env.mailbox == this.env.junk_mailbox 
-	    || this.env.mailbox.match('^' + RegExp.escape(this.env.trash_mailbox) + RegExp.escape(this.env.delimiter)) 
-	    || this.env.mailbox.match('^' + RegExp.escape(this.env.junk_mailbox) + RegExp.escape(this.env.delimiter))) ? true : false;
-	
-	  this.enable_command('purge', (this.env.messagecount && mailboxtest));
-	}
-	break;
-
-      }
+      case 'list':
+        if (this.task == 'mail') {
+          this.msglist_select(this.message_list);
+          this.enable_command('show', 'expunge', 'select-all', 'select-none', 'sort', (this.env.messagecount > 0));
+          this.enable_command('purge', this.purge_mailbox_test());
+        }
+        else if (this.task == 'addressbook')
+          this.enable_command('export', (this.contact_list && this.contact_list.rowcount > 0));
+        break;
+    }
 
     request_obj.reset();
     };
diff --git a/program/js/list.js b/program/js/list.js
index 1986e8b..97549f7 100644
--- a/program/js/list.js
+++ b/program/js/list.js
@@ -31,6 +31,7 @@
   this.frame = null;
   this.rows = [];
   this.selection = [];
+  this.rowcount = 0;
   
   this.subject_col = -1;
   this.shiftkey = false;
@@ -69,6 +70,7 @@
   if (this.list && this.list.tBodies[0])
   {
     this.rows = new Array();
+    this.rowcount = 0;
 
     var row;
     for(var r=0; r<this.list.tBodies[0].childNodes.length; r++)
@@ -81,6 +83,7 @@
       }
 
       this.init_row(row);
+      this.rowcount++;
     }
 
     this.frame = this.list.parentNode;
@@ -128,6 +131,7 @@
   this.list.insertBefore(tbody, this.list.tBodies[0]);
   this.list.removeChild(this.list.tBodies[1]);
   this.rows = new Array();
+  this.rowcount = 0;
   
   if (sel) this.clear_selection();
 },
@@ -145,6 +149,7 @@
     this.select_next();
 
   this.rows[uid] = null;
+  this.rowcount--;
 },
 
 
@@ -161,6 +166,7 @@
     tbody.appendChild(row);
 
   this.init_row(row);
+  this.rowcount++;
 },
 
 
diff --git a/program/steps/addressbook/export.inc b/program/steps/addressbook/export.inc
new file mode 100644
index 0000000..bfffac1
--- /dev/null
+++ b/program/steps/addressbook/export.inc
@@ -0,0 +1,43 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/addressbook/export.inc                                  |
+ |                                                                       |
+ | This file is part of the RoundCube Webmail client                     |
+ | Copyright (C) 2008, RoundCube Dev. - Switzerland                      |
+ | Licensed under the GNU GPL                                            |
+ |                                                                       |
+ | PURPOSE:                                                              |
+ |   Export the selected address book as vCard file                      |
+ |                                                                       |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com>                        |
+ +-----------------------------------------------------------------------+
+
+ $Id:  $
+
+*/
+
+// get contacts for this user
+$CONTACTS->set_pagesize(999);
+$result = $CONTACTS->list_records();
+
+// send downlaod headers
+send_nocacheing_headers();
+header('Content-Type: text/x-vcard; charset=UTF-8');
+header('Content-Disposition: attachment; filename="rcube_contacts.vcf"');
+
+while ($result && ($row = $result->next())) {
+  $vcard = new rcube_vcard($row['vcard']);
+  $vcard->set('displayname', $row['name']);
+  $vcard->set('firstname', $row['firstname']);
+  $vcard->set('surname', $row['surname']);
+  $vcard->set('email', $row['email']);
+  
+  echo $vcard->export();
+}
+
+exit;
+
+?>
diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc
index b95784f..fc3ee83 100644
--- a/program/steps/mail/get.inc
+++ b/program/steps/mail/get.inc
@@ -57,26 +57,20 @@
     
     $browser = new rcube_browser;
 
-    header("Expires: 0");
-    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
-    header("Cache-Control: private", false);
-    header("Content-Transfer-Encoding: binary");
-
+    send_nocacheing_headers();
+    
     // send download headers
     if ($_GET['_download']) {
       header("Content-Type: application/octet-stream");
       if ($browser->ie)
         header("Content-Type: application/force-download");
     }
-    else if ($ctype_primary == 'text')
+    else if ($ctype_primary == 'text') {
       header("Content-Type: text/$ctype_secondary; charset=" . ($part->charset ? $part->charset : RCMAIL_CHARSET));
-    else
+    }
+    else {
       header("Content-Type: $mimetype");
-
-    // We need to set the following headers to make downloads work using IE in HTTPS mode.
-    if (isset($_SERVER['HTTPS'])) {
-      header('Pragma: ');
-      header('Cache-Control: ');
+      header("Content-Transfer-Encoding: binary");
     }
 
     // deliver part content
diff --git a/skins/default/templates/addcontact.html b/skins/default/templates/addcontact.html
index 70a796a..10a121a 100644
--- a/skins/default/templates/addcontact.html
+++ b/skins/default/templates/addcontact.html
@@ -14,7 +14,7 @@
 
 <p><br />
 <input type="button" value="<roundcube:label name="cancel" />" class="button" onclick="history.back()" />&nbsp;
-<roundcube:button command="save" type="input" class="button" label="save mainaction" />
+<roundcube:button command="save" type="input" class="button mainaction" label="save" />
 </p>
 
 </form>

--
Gitblit v1.9.1