From f7af22c7801afcc248b004c84d0f1fb45e1a1632 Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Tue, 17 Feb 2015 05:54:04 -0500
Subject: [PATCH] Add possibility to print contact information (of a single contact)

---
 skins/classic/templates/addressbook.html  |    1 
 skins/larry/templates/contactprint.html   |   20 ++
 CHANGELOG                                 |    1 
 skins/larry/templates/addressbook.html    |    1 
 skins/larry/print.css                     |   63 +++++++
 program/steps/addressbook/func.inc        |   75 ++++++---
 skins/classic/print.css                   |   64 ++++++++
 skins/classic/templates/contactprint.html |   20 ++
 skins/classic/addressbook.css             |   10 +
 program/steps/addressbook/print.inc       |  138 +++++++++++++++++
 program/localization/en_US/labels.inc     |    1 
 program/steps/addressbook/show.inc        |   15 +
 program/js/app.js                         |   46 ++++-
 13 files changed, 412 insertions(+), 43 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 1b98ed2..ec400f2 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 CHANGELOG Roundcube Webmail
 ===========================
 
+- Add possibility to print contact information (of a single contact)
 - Fix refreshing of drafts list when sending a message which was saved in meantime (#1490238)
 
 RELEASE 1.1.0
diff --git a/program/js/app.js b/program/js/app.js
index 227ba32..e818955 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -326,10 +326,7 @@
           this.enable_command('download', 'print', true);
         // show printing dialog
         else if (this.env.action == 'print' && this.env.uid) {
-          if (bw.safari)
-            setTimeout('window.print()', 10);
-          else
-            window.print();
+          this.print_dialog();
         }
 
         // get unread count for each mailbox
@@ -439,6 +436,9 @@
           this.enable_command('save', true);
           if (this.env.action == 'add' || this.env.action == 'edit' || this.env.action == 'search')
               this.init_contact_form();
+        }
+        else if (this.env.action == 'print') {
+          this.print_dialog();
         }
 
         break;
@@ -1176,7 +1176,15 @@
         break;
 
       case 'print':
-        if (this.env.action == 'get') {
+        if (this.task == 'addressbook') {
+          if (uid = this.contact_list.get_single_selection()) {
+            url = '&_action=print&_cid=' + uid;
+            if (this.env.source)
+              url += '&_source=' + urlencode(this.env.source);
+            this.open_window(this.env.comm_path + url, true, true);
+          }
+        }
+        else if (this.env.action == 'get') {
           this.gui_objects.messagepartframe.contentWindow.print();
         }
         else if (uid = this.get_single_uid()) {
@@ -4721,6 +4729,7 @@
       clearTimeout(this.preview_timer);
 
     var n, id, sid, contact, writable = false,
+      selected = list.selection.length,
       source = this.env.source ? this.env.address_sources[this.env.source] : null;
 
     // we don't have dblclick handler here, so use 200 instead of this.dblclick_time
@@ -4729,7 +4738,7 @@
     else if (this.env.contentframe)
       this.show_contentframe(false);
 
-    if (list.selection.length) {
+    if (selected) {
       list.draggable = false;
 
       // no source = search result, we'll need to detect if any of
@@ -4764,11 +4773,12 @@
 
     // if a group is currently selected, and there is at least one contact selected
     // thend we can enable the group-remove-selected command
-    this.enable_command('group-remove-selected', this.env.group && list.selection.length > 0 && writable);
-    this.enable_command('compose', this.env.group || list.selection.length > 0);
-    this.enable_command('export-selected', 'copy', list.selection.length > 0);
+    this.enable_command('group-remove-selected', this.env.group && selected && writable);
+    this.enable_command('compose', this.env.group || selected);
+    this.enable_command('print', selected == 1);
+    this.enable_command('export-selected', 'copy', selected > 0);
     this.enable_command('edit', id && writable);
-    this.enable_command('delete', 'move', list.selection.length > 0 && writable);
+    this.enable_command('delete', 'move', selected && writable);
 
     return false;
   };
@@ -4881,8 +4891,8 @@
     this.contact_list.data = {};
     this.contact_list.clear(true);
     this.show_contentframe(false);
-    this.enable_command('delete', 'move', 'copy', false);
-    this.enable_command('compose', this.env.group ? true : false);
+    this.enable_command('delete', 'move', 'copy', 'print', false);
+    this.enable_command('compose', this.env.group);
   };
 
   this.set_group_prop = function(prop)
@@ -4922,7 +4932,7 @@
         this.contact_list.clear_selection();
 
       this.enable_command('compose', rec && rec.email);
-      this.enable_command('export-selected', rec && rec._type != 'group');
+      this.enable_command('export-selected', 'print', rec && rec._type != 'group');
     }
     else if (framed)
       return false;
@@ -7365,7 +7375,7 @@
           this.enable_command('compose', (uid && this.contact_list.rows[uid]));
           this.enable_command('delete', 'edit', writable);
           this.enable_command('export', (this.contact_list && this.contact_list.rowcount > 0));
-          this.enable_command('export-selected', false);
+          this.enable_command('export-selected', 'print', false);
         }
 
       case 'move':
@@ -8198,6 +8208,14 @@
       return false;
     }
   };
+
+  this.print_dialog = function()
+  {
+    if (bw.safari)
+      setTimeout('window.print()', 10);
+    else
+      window.print();
+  };
 }  // end object rcube_webmail
 
 
diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc
index 63b6616..fac52ba 100644
--- a/program/localization/en_US/labels.inc
+++ b/program/localization/en_US/labels.inc
@@ -343,6 +343,7 @@
 $labels['advsearch'] = 'Advanced Search';
 $labels['advanced'] = 'Advanced';
 $labels['other'] = 'Other';
+$labels['printcontact'] = 'Print contact';
 
 $labels['typehome']   = 'Home';
 $labels['typework']   = 'Work';
diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc
index c40b517..594c2a6 100644
--- a/program/steps/addressbook/func.inc
+++ b/program/steps/addressbook/func.inc
@@ -518,12 +518,13 @@
     $plugin = $RCMAIL->plugins->exec_hook('contact_form', array(
         'form' => $form, 'record' => $record));
 
-    $form = $plugin['form'];
-    $record = $plugin['record'];
-    $edit_mode = $RCMAIL->action != 'show';
+    $form       = $plugin['form'];
+    $record     = $plugin['record'];
+    $edit_mode  = $RCMAIL->action != 'show' && $RCMAIL->action != 'print';
     $del_button = $attrib['deleteicon'] ? html::img(array('src' => $RCMAIL->output->get_skin_file($attrib['deleteicon']), 'alt' => $RCMAIL->gettext('delete'))) : $RCMAIL->gettext('delete');
+    $out        = '';
+
     unset($attrib['deleteicon']);
-    $out = '';
 
     // get default coltypes
     $coltypes = $GLOBALS['CONTACT_COLTYPES'];
@@ -544,8 +545,9 @@
 
     foreach ($form as $section => $fieldset) {
         // skip empty sections
-        if (empty($fieldset['content']))
+        if (empty($fieldset['content'])) {
             continue;
+        }
 
         $select_add = new html_select(array('class' => 'addfieldmenu', 'rel' => $section));
         $select_add->add($RCMAIL->gettext('addfield'), '');
@@ -555,18 +557,20 @@
             $content = '';
 
             // unset display name if it is composed from name parts
-            if ($record['name'] == rcube_addressbook::compose_display_name(array('name' => '') + (array)$record))
-              unset($record['name']);
+            if ($record['name'] == rcube_addressbook::compose_display_name(array('name' => '') + (array)$record)) {
+                unset($record['name']);
+            }
 
             // group fields
             $field_blocks = array(
-                'names'    => array('prefix','firstname','middlename','surname','suffix'),
-                'displayname' => array('name'),
-                'nickname' => array('nickname'),
+                'names'        => array('prefix','firstname','middlename','surname','suffix'),
+                'displayname'  => array('name'),
+                'nickname'     => array('nickname'),
                 'organization' => array('organization'),
-                'department' => array('department'),
-                'jobtitle' => array('jobtitle'),
+                'department'   => array('department'),
+                'jobtitle'     => array('jobtitle'),
             );
+
             foreach ($field_blocks as $blockname => $colnames) {
                 $fields = '';
                 foreach ($colnames as $col) {
@@ -574,11 +578,16 @@
                     if (!$coltypes[$col])
                         continue;
 
+                    // skip cols not listed in the form definition
+                    if (is_array($fieldset['content']) && !in_array($col, array_keys($fieldset['content']))) {
+                        continue;
+                    }
+
                     // only string values are expected here
                     if (is_array($record[$col]))
                         $record[$col] = join(' ', $record[$col]);
 
-                    if ($RCMAIL->action == 'show') {
+                    if (!$edit_mode) {
                         if (!empty($record[$col]))
                             $fields .= html::span('namefield ' . $col, rcube::Q($record[$col])) . " ";
                     }
@@ -611,11 +620,15 @@
                 $fullkey = $col.':'.$subtype;
 
                 // skip cols unknown to the backend
-                if (!$coltypes[$field])
+                if (!$coltypes[$field] && empty($colprop['value'])) {
                     continue;
+                }
 
                 // merge colprop with global coltype configuration
-                $colprop += $coltypes[$field];
+                if ($coltypes[$field]) {
+                    $colprop += $coltypes[$field];
+                }
+
                 $label = isset($colprop['label']) ? $colprop['label'] : $RCMAIL->gettext($col);
 
                 // prepare subtype selector in edit mode
@@ -624,8 +637,9 @@
                     $select_subtype = new html_select(array('name' => '_subtype_'.$col.'[]', 'class' => 'contactselectsubtype', 'title' => $colprop['label'] . ' ' . $RCMAIL->gettext('type')));
                     $select_subtype->add($subtype_names, $colprop['subtypes']);
                 }
-                else
+                else {
                     $select_subtype = null;
+                }
 
                 if (!empty($colprop['value'])) {
                     $values = (array)$colprop['value'];
@@ -729,12 +743,21 @@
 
                     // display row with label
                     if ($label) {
+                        if ($RCMAIL->action == 'print') {
+                            $_label = rcube::Q($colprop['label'] . ($label != $colprop['label'] ? ' (' . $label . ')' : ''));
+                        }
+                        else {
+                            $_label = $select_subtype ? $select_subtype->show($subtype) : html::label($colprop['id'], rcube::Q($label));
+                        }
+
                         $rows .= html::div('row',
-                            html::div('contactfieldlabel label', $select_subtype ? $select_subtype->show($subtype) : html::label($colprop['id'], rcube::Q($label))) .
+                            html::div('contactfieldlabel label', $_label) .
                             html::div('contactfieldcontent '.$colprop['type'], $val));
                     }
-                    else   // row without label
+                    // row without label
+                    else {
                         $rows .= html::div('row', html::div('contactfield', $val));
+                    }
                 }
 
                 // add option to the add-field menu
@@ -745,9 +768,13 @@
 
                 // wrap rows in fieldgroup container
                 if ($rows) {
-                    $content .= html::tag('fieldset', array('class' => 'contactfieldgroup ' . ($colprop['subtypes'] ? 'contactfieldgroupmulti ' : '') . 'contactcontroller' . $col, 'style' => ($rows ? null : 'display:none')),
-                      ($colprop['subtypes'] ? html::tag('legend', null, rcube::Q($colprop['label'])) : ' ') .
-                      $rows);
+                    $c_class    = 'contactfieldgroup ' . ($colprop['subtypes'] ? 'contactfieldgroupmulti ' : '') . 'contactcontroller' . $col;
+                    $with_label = $colprop['subtypes'] && $RCMAIL->action != 'print';
+                    $content   .= html::tag(
+                        'fieldset',
+                        array('class' => $c_class, 'style' => ($rows ? null : 'display:none')),
+                        ($with_label ? html::tag('legend', null, rcube::Q($colprop['label'])) : ' ') . $rows
+                    );
                 }
             }
 
@@ -769,9 +796,9 @@
     }
 
     if ($edit_mode) {
-      $RCMAIL->output->set_env('coltypes', $coltypes + $coltype_labels);
-      $RCMAIL->output->set_env('delbutton', $del_button);
-      $RCMAIL->output->add_label('delete');
+        $RCMAIL->output->set_env('coltypes', $coltypes + $coltype_labels);
+        $RCMAIL->output->set_env('delbutton', $del_button);
+        $RCMAIL->output->add_label('delete');
     }
 
     return $out;
diff --git a/program/steps/addressbook/print.inc b/program/steps/addressbook/print.inc
new file mode 100644
index 0000000..c9460d4
--- /dev/null
+++ b/program/steps/addressbook/print.inc
@@ -0,0 +1,138 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/addressbook/print.inc                                   |
+ |                                                                       |
+ | This file is part of the Roundcube Webmail client                     |
+ | Copyright (C) 2005-2015, The Roundcube Dev Team                       |
+ | Copyright (C) 2011-2015, Kolab Systems AG                             |
+ |                                                                       |
+ | Licensed under the GNU General Public License version 3 or            |
+ | any later version with exceptions for skins & plugins.                |
+ | See the README file for a full license statement.                     |
+ |                                                                       |
+ | PURPOSE:                                                              |
+ |   Print contact details                                               |
+ |                                                                       |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com>                        |
+ | Author: Aleksander Machniak <machniak@kolabsys.com>                   |
+ +-----------------------------------------------------------------------+
+*/
+
+// Get contact ID and source ID from request
+$cids   = rcmail_get_cids();
+$source = key($cids);
+$cid    = $cids ? array_shift($cids[$source]) : null;
+
+// Initialize addressbook source
+$CONTACTS  = rcmail_contact_source($source, true);
+$SOURCE_ID = $source;
+
+// read contact record
+if ($cid && $CONTACTS) {
+    $record = $CONTACTS->get_record($cid, true);
+}
+
+$OUTPUT->add_handlers(array(
+    'contacthead'    => 'rcmail_contact_head',
+    'contactdetails' => 'rcmail_contact_details',
+    'contactphoto'   => 'rcmail_contact_photo',
+));
+
+$OUTPUT->send('contactprint');
+
+
+
+function rcmail_contact_head($attrib)
+{
+    global $CONTACTS, $RCMAIL;
+
+    // check if we have a valid result
+    if (!(($result = $CONTACTS->get_result()) && ($record = $result->first()))) {
+        $RCMAIL->output->show_message('contactnotfound', 'error');
+        return false;
+    }
+
+    $form = array(
+        'head' => array(  // section 'head' is magic!
+            'name' => $RCMAIL->gettext('contactnameandorg'),
+            'content' => array(
+                'prefix'     => array(),
+                'name'       => array(),
+                'firstname'  => array(),
+                'middlename' => array(),
+                'surname'    => array(),
+                'suffix'     => array(),
+            ),
+        ),
+    );
+
+    unset($attrib['name']);
+    return rcmail_contact_form($form, $record, $attrib);
+}
+
+
+function rcmail_contact_details($attrib)
+{
+    global $CONTACTS, $RCMAIL, $CONTACT_COLTYPES;
+
+    // check if we have a valid result
+    if (!(($result = $CONTACTS->get_result()) && ($record = $result->first()))) {
+        return false;
+    }
+
+    $i_size = !empty($attrib['size']) ? $attrib['size'] : 40;
+
+    $form = array(
+        'contact' => array(
+            'name'    => $RCMAIL->gettext('properties'),
+            'content' => array(
+                'organization' => array(),
+                'department'   => array(),
+                'jobtitle'     => array(),
+                'email'        => array(),
+                'phone'        => array(),
+                'address'      => array(),
+                'website'      => array(),
+                'im'           => array(),
+                'groups' => array('value' => 'sdfsdfs', 'label' => $RCMAIL->gettext('groups')),
+            ),
+        ),
+        'personal' => array(
+            'name'    => $RCMAIL->gettext('personalinfo'),
+            'content' => array(
+                'nickname'    => array(),
+                'gender'      => array(),
+                'maidenname'  => array(),
+                'birthday'    => array(),
+                'anniversary' => array(),
+                'manager'     => array(),
+                'assistant'   => array(),
+                'spouse'      => array(),
+            ),
+        ),
+    );
+
+    if (isset($CONTACT_COLTYPES['notes'])) {
+        $form['notes'] = array(
+            'name'    => $RCMAIL->gettext('notes'),
+            'content' => array(
+                'notes' => array('type' => 'textarea', 'label' => false),
+            ),
+        );
+    }
+
+    if ($CONTACTS->groups) {
+        $groups = $CONTACTS->get_record_groups($record['ID']);
+        if (!empty($groups)) {
+            $form['contact']['content']['groups'] = array(
+                'value' => rcube::Q(implode(', ', $groups)),
+                'label' => $RCMAIL->gettext('groups')
+            );
+        }
+    }
+
+    return rcmail_contact_form($form, $record);
+}
diff --git a/program/steps/addressbook/show.inc b/program/steps/addressbook/show.inc
index 5835ce7..d1bc859 100644
--- a/program/steps/addressbook/show.inc
+++ b/program/steps/addressbook/show.inc
@@ -66,11 +66,16 @@
         'head' => array(  // section 'head' is magic!
             'name' => $RCMAIL->gettext('contactnameandorg'),
             'content' => array(
-                'prefix' => array('type' => 'text'),
-                'firstname' => array('type' => 'text'),
-                'middlename' => array('type' => 'text'),
-                'surname' => array('type' => 'text'),
-                'suffix' => array('type' => 'text'),
+                'prefix'       => array('type' => 'text'),
+                'firstname'    => array('type' => 'text'),
+                'middlename'   => array('type' => 'text'),
+                'surname'      => array('type' => 'text'),
+                'suffix'       => array('type' => 'text'),
+                'name'         => array('type' => 'text'),
+                'nickname'     => array('type' => 'text'),
+                'organization' => array('type' => 'text'),
+                'department'   => array('type' => 'text'),
+                'jobtitle'     => array('type' => 'text'),
             ),
         ),
     );
diff --git a/skins/classic/addressbook.css b/skins/classic/addressbook.css
index 90438aa..b54f057 100644
--- a/skins/classic/addressbook.css
+++ b/skins/classic/addressbook.css
@@ -43,6 +43,16 @@
   background-position: -32px -32px;
 }
 
+#abooktoolbar a.print {
+  background: url(images/mail_toolbar.png) 0 0 no-repeat transparent;
+  background-position: -256px 0;
+}
+
+#abooktoolbar a.printSel {
+  background: url(images/mail_toolbar.png) 0 0 no-repeat transparent;
+  background-position: -256px -32px;
+}
+
 #abooktoolbar a.delete {
   background-position: -64px 0;
 }
diff --git a/skins/classic/print.css b/skins/classic/print.css
index 349b224..4d7fb24 100644
--- a/skins/classic/print.css
+++ b/skins/classic/print.css
@@ -159,3 +159,67 @@
 {
   display: none;
 }
+
+/* contact print */
+#contact-details fieldset {
+	color: #666;
+	border: 1px solid #999;
+	margin-top: 5px;
+}
+
+#contact-details fieldset.contactfieldgroup {
+	border: 0;
+	padding: 0;
+	margin: 0;
+}
+
+#contact-details div.row {
+	padding: 2px 0;
+}
+
+#contact-details .contactfieldlabel {
+	display: inline-block;
+	vertical-align: top;
+	width: 150px;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+
+#contact-details .contactfieldcontent {
+	display: inline-block;
+	vertical-align: top;
+	font-weight: bold;
+}
+
+#contact-details #contactphoto {
+	float: left;
+	margin: 5px 15px 5px 3px;
+	width: 112px;
+	border: 0;
+	padding: 0;
+}
+
+#contact-details #contactpic {
+	width: 112px;
+	background: white;
+}
+
+#contact-details #contactpic img {
+	max-width: 112px;
+	visibility: inherit;
+}
+
+#contact-details #contacthead {
+	border: 0;
+	margin: 0 16em 0 0;
+	padding: 0;
+}
+
+#contact-details #contacthead > legend {
+	display: none;
+}
+
+#contact-details #contacthead .names span.namefield {
+	font-size: 140%;
+	font-weight: bold;
+}
diff --git a/skins/classic/templates/addressbook.html b/skins/classic/templates/addressbook.html
index 0af5e43..6fb8bf5 100644
--- a/skins/classic/templates/addressbook.html
+++ b/skins/classic/templates/addressbook.html
@@ -21,6 +21,7 @@
 <div id="abooktoolbar">
 <roundcube:button command="add" type="link" class="buttonPas addcontact" classAct="button addcontact" classSel="button addcontactSel" title="newcontact" content=" " />
 <roundcube:button command="compose" type="link" class="buttonPas compose" classAct="button compose" classSel="button composeSel" title="composeto" content=" " />
+<roundcube:button command="print" type="link" class="buttonPas print" classAct="button print" classSel="button printSel" label="print" title="printcontact" content=" " />
 <roundcube:button command="delete" type="link" class="buttonPas delete" classAct="button delete" classSel="button deleteSel" title="deletecontact" content=" " />
 <span class="separator">&nbsp;</span>
 <roundcube:button command="import" type="link" class="buttonPas import" classAct="button import" classSel="button importSel" title="importcontacts" content=" " />
diff --git a/skins/classic/templates/contactprint.html b/skins/classic/templates/contactprint.html
new file mode 100644
index 0000000..81794f5
--- /dev/null
+++ b/skins/classic/templates/contactprint.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<link rel="shortcut icon" href="/images/favicon.ico"/>
+<link rel="stylesheet" type="text/css" href="/print.css" />
+</head>
+<body>
+
+<roundcube:object name="logo" src="/images/roundcube_logo.png" id="logo" border="0" />
+
+<div id="contact-details">
+  <div id="contactphoto"><roundcube:object name="contactphoto" id="contactpic" placeholder="/images/contactpic.png" placeholderGroup="/images/contactgroup.png" /></div>
+  <roundcube:object name="contacthead" id="contacthead" />
+  <div style="clear:both"></div>
+  <roundcube:object name="contactdetails" />
+</div>
+
+</body>
+</html>
diff --git a/skins/larry/print.css b/skins/larry/print.css
index d3a6cd8..4b67bcc 100644
--- a/skins/larry/print.css
+++ b/skins/larry/print.css
@@ -143,3 +143,66 @@
 	display: none;
 }
 
+/* contact print */
+#contact-details fieldset {
+	color: #666;
+	border: 1px solid #999;
+	margin-top: 5px;
+}
+
+#contact-details fieldset.contactfieldgroup {
+	border: 0;
+	padding: 0;
+	margin: 0;
+}
+
+#contact-details div.row {
+	padding: 2px 0;
+}
+
+#contact-details .contactfieldlabel {
+	display: inline-block;
+	vertical-align: top;
+	width: 150px;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+
+#contact-details .contactfieldcontent {
+	display: inline-block;
+	vertical-align: top;
+	font-weight: bold;
+}
+
+#contact-details #contactphoto {
+	float: left;
+	margin: 5px 15px 5px 3px;
+	width: 112px;
+	border: 0;
+	padding: 0;
+}
+
+#contact-details #contactpic {
+	width: 112px;
+	background: white;
+}
+
+#contact-details #contactpic img {
+	max-width: 112px;
+	visibility: inherit;
+}
+
+#contact-details #contacthead {
+	border: 0;
+	margin: 0 16em 0 0;
+	padding: 0;
+}
+
+#contact-details #contacthead > legend {
+	display: none;
+}
+
+#contact-details #contacthead .names span.namefield {
+	font-size: 140%;
+	font-weight: bold;
+}
diff --git a/skins/larry/templates/addressbook.html b/skins/larry/templates/addressbook.html
index 62bca3c..7f358f5 100644
--- a/skins/larry/templates/addressbook.html
+++ b/skins/larry/templates/addressbook.html
@@ -23,6 +23,7 @@
 
 	<span class="spacer"></span>
 	<roundcube:button command="compose" type="link" class="button compose disabled" classAct="button compose" classSel="button compose pressed" label="compose" title="writenewmessage" />
+	<roundcube:button command="print" type="link" class="button print disabled" classAct="button print" classSel="button print pressed" label="print" title="printcontact" />
 	<roundcube:button command="advanced-search" type="link" class="button search disabled" classAct="button search" classSel="button search pressed" label="advanced" title="advsearch" />
 	<roundcube:container name="toolbar" id="addressbooktoolbar" />
 
diff --git a/skins/larry/templates/contactprint.html b/skins/larry/templates/contactprint.html
new file mode 100644
index 0000000..f3f169e
--- /dev/null
+++ b/skins/larry/templates/contactprint.html
@@ -0,0 +1,20 @@
+<roundcube:object name="doctype" value="html5" />
+<html>
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<link rel="shortcut icon" href="/images/favicon.ico"/>
+<link rel="stylesheet" type="text/css" href="/print.css" />
+</head>
+<body>
+
+<div id="header"><roundcube:object name="logo" src="/images/roundcube_logo.png" id="toplogo" border="0" alt="Logo" /></div>
+
+<div id="contact-details" class="boxcontent">
+	<div id="contactphoto"><roundcube:object name="contactphoto" id="contactpic" placeholder="/images/contactpic.png" placeholderGroup="/images/contactgroup.png" /></div>
+	<roundcube:object name="contacthead" id="contacthead" />
+	<br style="clear:both" />
+	<roundcube:object name="contactdetails" />
+</div>
+
+</body>
+</html>

--
Gitblit v1.9.1