thomascube
2008-09-04 0dbac3218130dfe418d6c7dc162f819c746bec2d
Enable export of contacts as vCard + DRY

7 files modified
1 files added
193 ■■■■■ changed files
CHANGELOG 4 ●●●● patch | view | raw | blame | history
program/include/rcube_shared.inc 8 ●●●● patch | view | raw | blame | history
program/include/rcube_vcard.php 11 ●●●●● patch | view | raw | blame | history
program/js/app.js 101 ●●●●● patch | view | raw | blame | history
program/js/list.js 6 ●●●●● patch | view | raw | blame | history
program/steps/addressbook/export.inc 43 ●●●●● patch | view | raw | blame | history
program/steps/mail/get.inc 18 ●●●●● patch | view | raw | blame | history
skins/default/templates/addcontact.html 2 ●●● patch | view | raw | blame | history
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
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: ');
  }
}
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";
  }
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();
    };
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++;
},
program/steps/addressbook/export.inc
New file
@@ -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;
?>
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
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>