Changeset 4314 in subversion


Ignore:
Timestamp:
Dec 7, 2010 5:07:39 PM (2 years ago)
Author:
thomasb
Message:

Allow to upload photos for contacts; images are identified and resized using imagemagick

Location:
branches/devel-addressbook
Files:
20 edited

Legend:

Unmodified
Added
Removed
  • branches/devel-addressbook/config/main.inc.php.dist

    r4239 r4314  
    286286// mime magic database 
    287287$rcmail_config['mime_magic'] = '/usr/share/misc/magic'; 
     288 
     289// path to imagemagick identify binary 
     290$rcmail_config['im_identify_path'] = null; 
     291 
     292// path to imagemagick convert binary 
     293$rcmail_config['im_convert_path'] = null; 
     294 
     295// maximum size of uploaded contact photos 
     296$rcmail_config['contact_photo_size'] = 160; 
    288297 
    289298// Enable DNS checking for e-mail address validation 
  • branches/devel-addressbook/index.php

    r4297 r4314  
    214214    'add' => 'edit.inc', 
    215215    'photo' => 'show.inc', 
     216    'upload-photo' => 'save.inc', 
    216217    'group-create' => 'groups.inc', 
    217218    'group-rename' => 'groups.inc', 
  • branches/devel-addressbook/program/include/rcmail.php

    r4283 r4314  
    12941294 
    12951295  /** 
     1296   * Use imagemagick or GD lib to read image properties 
     1297   * 
     1298   * @param string Absolute file path 
     1299   * @return mixed Hash array with image props like type, width, height or False on error 
     1300   */ 
     1301  public static function imageprops($filepath) 
     1302  { 
     1303    $rcmail = rcmail::get_instance(); 
     1304    if ($cmd = $rcmail->config->get('im_identify_path', false)) { 
     1305      list(, $type, $size) = explode(' ', strtolower(rcmail::exec($cmd. ' 2>/dev/null {in}', array('in' => $filepath)))); 
     1306      if ($size) 
     1307        list($width, $height) = explode('x', $size); 
     1308    } 
     1309    else if (function_exists('getimagesize')) { 
     1310      $imsize = @getimagesize($filepath); 
     1311      $width = $imsize[0]; 
     1312      $height = $imsize[1]; 
     1313      $type = preg_replace('!image/!', '', $imsize['mime']); 
     1314    } 
     1315 
     1316    return $type ? array('type' => $type, 'width' => $width, 'height' => $height) : false; 
     1317  } 
     1318 
     1319 
     1320  /** 
     1321   * Convert an image to a given size and type using imagemagick (ensures input is an image) 
     1322   * 
     1323   * @param $p['in']  Input filename (mandatory) 
     1324   * @param $p['out'] Output filename (mandatory) 
     1325   * @param $p['size']  Width x height of resulting image, e.g. "160x60" 
     1326   * @param $p['type']  Output file type, e.g. "jpg" 
     1327   * @param $p['-opts'] Custom command line options to ImageMagick convert 
     1328   * @return Success of convert as true/false 
     1329   */ 
     1330  public static function imageconvert($p) 
     1331  { 
     1332    $result = false; 
     1333    $rcmail = rcmail::get_instance(); 
     1334    $convert  = $rcmail->config->get('im_convert_path', false); 
     1335    $identify = $rcmail->config->get('im_identify_path', false); 
     1336     
     1337    // imagemagick is required for this 
     1338    if (!$convert) 
     1339        return false; 
     1340 
     1341    if (!(($imagetype = @exif_imagetype($p['in'])) && ($type = image_type_to_extension($imagetype, false)))) 
     1342      list(, $type) = explode(' ', strtolower(rcmail::exec($identify . ' 2>/dev/null {in}', $p))); # for things like eps 
     1343 
     1344    $type = strtr($type, array("jpeg" => "jpg", "tiff" => "tif", "ps" => "eps", "ept" => "eps")); 
     1345    $p += array('type' => $type, 'types' => "bmp,eps,gif,jp2,jpg,png,svg,tif", 'quality' => 75); 
     1346    $p['-opts'] = array('-resize' => $p['size'].'>') + (array)$p['-opts']; 
     1347 
     1348    if (in_array($type, explode(',', $p['types']))) # Valid type? 
     1349      $result = rcmail::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace RGB -quality {quality} {-opts} {in} {type}:{out}', $p) === ""; 
     1350 
     1351    return $result; 
     1352  } 
     1353 
     1354 
     1355  /** 
     1356   * Construct shell command, execute it and return output as string. 
     1357   * Keywords {keyword} are replaced with arguments 
     1358   * 
     1359   * @param $cmd Format string with {keywords} to be replaced 
     1360   * @param $values (zero, one or more arrays can be passed) 
     1361   * @return output of command. shell errors not detectable, see error_log in /www/server/logs 
     1362   */ 
     1363  public static function exec(/* $cmd, $values1 = array(), ... */) 
     1364  { 
     1365    $args = func_get_args(); 
     1366    $cmd = array_shift($args); 
     1367    $values = $replacements = array(); 
     1368 
     1369    // merge values into one array 
     1370    foreach ($args as $arg) 
     1371      $values += (array)$arg; 
     1372 
     1373    preg_match_all('/({(-?)([a-z]\w*)})/', $cmd, $matches, PREG_SET_ORDER); 
     1374    foreach ($matches as $tags) { 
     1375      list(, $tag, $option, $key) = $tags; 
     1376      $parts = array(); 
     1377 
     1378      if ($option) { 
     1379        foreach ((array)$values["-$key"] as $key => $value) { 
     1380          if ($value === true || $value === false || $value === null) 
     1381            $parts[] = $value ? $key : ""; 
     1382          else foreach ((array)$value as $val) 
     1383            $parts[] = "$key " . escapeshellarg($val); 
     1384        } 
     1385      } 
     1386      else { 
     1387        foreach ((array)$values[$key] as $value) 
     1388          $parts[] = escapeshellarg($value); 
     1389      } 
     1390 
     1391      $replacements[$tag] = join(" ", $parts); 
     1392    } 
     1393 
     1394    // use strtr behaviour of going through source string once 
     1395    $cmd = strtr($cmd, $replacements); 
     1396     
     1397    return (string)shell_exec($cmd); 
     1398  } 
     1399 
     1400 
     1401  /** 
    12961402   * Helper method to set a cookie with the current path and host settings 
    12971403   * 
  • branches/devel-addressbook/program/include/rcube_contacts.php

    r4297 r4314  
    502502            list($field, $section) = explode(':', $key); 
    503503            foreach ((array)$values as $value) { 
    504                 if (is_array($value) || strlen($value)) 
     504                if (isset($value)) 
    505505                    $vcard->set($field, $value, $section); 
    506506            } 
  • branches/devel-addressbook/program/include/rcube_vcard.php

    r4298 r4314  
    3333    'N' => array(array('','','','','')), 
    3434  ); 
    35   private $fieldmap = array('photo' => 'PHOTO', 'phone' => 'TEL', 'birthday' => 'BDAY', 'website' => 'URL', 'notes' => 'NOTE', 'email' => 'EMAIL', 'address' => 'ADR', 'gender' => 'X-GENDER', 'maidenname' => 'X-MAIDENNAME', 'gender' => 'X-GENDER'); 
     35  private $fieldmap = array('phone' => 'TEL', 'birthday' => 'BDAY', 'website' => 'URL', 'notes' => 'NOTE', 'email' => 'EMAIL', 'address' => 'ADR', 'gender' => 'X-GENDER', 'maidenname' => 'X-MAIDENNAME', 'gender' => 'X-GENDER'); 
    3636  private $typemap = array('iPhone' => 'mobile', 'CELL' => 'mobile'); 
    3737  private $phonetypemap = array('HOME1' => 'HOME', 'BUSINESS1' => 'WORK', 'BUSINESS2' => 'WORK2', 'WORKFAX' => 'BUSINESSFAX'); 
     
    6868  { 
    6969    $this->raw = self::vcard_decode($vcard); 
    70      
     70 
    7171    // resolve charset parameters 
    7272    if ($charset == null) 
     
    152152    } 
    153153     
     154    // copy photo data 
     155    if ($this->raw['PHOTO']) 
     156      $out['photo'] = $this->raw['PHOTO'][0][0]; 
     157     
    154158    return $out; 
    155159  } 
     
    235239         
    236240      case 'photo': 
    237         $encoded = base64_decode($value, true) ? true : false; 
    238         $this->raw['PHOTO'][] = array(0 => $encoded ? $value : base64_encode($value), array('BASE64')); 
     241        $encoded = !preg_match('![^a-z0-9/=+-]!i', $value); 
     242        $this->raw['PHOTO'][0] = array(0 => $encoded ? $value : base64_encode($value), 'BASE64' => true); 
    239243        break; 
    240244         
     
    270274          $type = $this->phonetypemap[$type]; 
    271275 
    272         if ($tag = $this->fieldmap[$field]) { 
     276        if (($tag = $this->fieldmap[$field]) && (is_array($value) || strlen($value))) { 
    273277          $index = count($this->raw[$tag]); 
    274278          $this->raw[$tag][$index] = (array)$value; 
  • branches/devel-addressbook/program/js/app.js

    r4297 r4314  
    320320        if ((this.env.action=='add' || this.env.action=='edit') && this.gui_objects.editform) { 
    321321          this.enable_command('save', true); 
     322          this.enable_command('upload-photo', this.env.coltypes.photo ? true : false); 
     323          this.enable_command('delete-photo', this.env.coltypes.photo && this.env.action == 'edit'); 
    322324          $("input[type='text']").first().select(); 
    323325 
    324326          for (var col in this.env.coltypes) 
    325327            this.init_edit_field(col, null); 
    326              
    327           $('#contactpicframe').click(function(){ ref.upload_contact_photo(this); return false; }); 
    328328 
    329329          $('.contactfieldgroup .row a.deletebutton').click(function(){ ref.delete_edit_field(this); return false }); 
     
    447447    if (!this.commands[command]) { 
    448448      // pass command to parent window 
    449       if (this.env.framed && parent.rcmail && parent.rcmail.command) 
     449      if (this.env.framed && parent.rcmail && parent.rcmail != this && parent.rcmail.command) 
    450450        parent.rcmail.command(command, props); 
    451451 
     
    10111011        } 
    10121012        break; 
     1013         
     1014      case 'upload-photo': 
     1015        this.upload_contact_photo(props); 
     1016        break; 
     1017 
     1018      case 'delete-photo': 
     1019        this.replace_contact_photo('-del-'); 
     1020        break; 
    10131021 
    10141022      // user settings commands 
     
    31823190    // create hidden iframe and post upload form 
    31833191    if (send) { 
    3184       var ts = new Date().getTime(); 
    3185       var frame_name = 'rcmupload'+ts; 
    3186  
    3187       // have to do it this way for IE 
    3188       // otherwise the form will be posted to a new window 
    3189       if (document.all) { 
    3190         var html = '<iframe name="'+frame_name+'" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>'; 
    3191         document.body.insertAdjacentHTML('BeforeEnd',html); 
    3192       } 
    3193       else { // for standards-compilant browsers 
    3194         var frame = document.createElement('iframe'); 
    3195         frame.name = frame_name; 
    3196         frame.style.border = 'none'; 
    3197         frame.style.width = 0; 
    3198         frame.style.height = 0; 
    3199         frame.style.visibility = 'hidden'; 
    3200         document.body.appendChild(frame); 
    3201       } 
    3202  
    3203       // handle upload errors, parsing iframe content in onload 
    3204       $(frame_name).bind('load', {ts:ts}, function(e) { 
     3192      this.async_upload_form(form, 'upload', function(e) { 
    32053193        var d, content = ''; 
    32063194        try { 
     
    32223210          rcmail.env.uploadframe = e.data.ts; 
    32233211      }); 
    3224  
    3225       form.target = frame_name; 
    3226       form.action = this.env.comm_path+'&_action=upload&_uploadid='+ts; 
    3227       form.setAttribute('enctype', 'multipart/form-data'); 
    3228       form.submit(); 
    32293212 
    32303213      // display upload indicator and cancel button 
     
    40844067    var col = $(elem).attr('rel'), 
    40854068      colprop = this.env.coltypes[col], 
    4086       addmenu = $(elem).parents('div.contactfieldgroup').parent().find('select.addfieldmenu'); 
     4069      fieldset = $(elem).parents('fieldset.contactfieldgroup'), 
     4070      addmenu = fieldset.parent().find('select.addfieldmenu'); 
    40874071     
    40884072    // just clear input but don't hide the last field 
    40894073    if (--colprop.count <= 0 && colprop.visible) 
    40904074      $(elem).parent().children('input').val('').blur(); 
    4091     else 
     4075    else { 
    40924076      $(elem).parents('div.row').remove(); 
     4077      // hide entire fieldset if no more rows 
     4078      if (!fieldset.children('div.row').length) 
     4079        fieldset.hide(); 
     4080    } 
    40934081     
    4094     // TODO: enable option in add-field selector or insert it if necessary 
     4082    // enable option in add-field selector or insert it if necessary 
    40954083    if (addmenu.length) { 
    40964084      var option = addmenu.children('option[value="'+col+'"]'); 
     
    41014089    } 
    41024090    else 
    4103     alert('addmennu not found') 
    4104   }; 
    4105  
    4106  
    4107   this.upload_contact_photo = function(frame) 
    4108   { 
    4109     // TODO: implement this 
     4091      alert('addmennu not found') 
     4092  }; 
     4093 
     4094 
     4095  this.upload_contact_photo = function(form) 
     4096  { 
     4097    if (form && form.elements._photo.value) { 
     4098      this.async_upload_form(form, 'upload-photo', function(e) { 
     4099        rcmail.set_busy(false, null, rcmail.photo_upload_id); 
     4100      }); 
     4101 
     4102      // display upload indicator 
     4103      this.photo_upload_id = this.set_busy(true, 'uploading'); 
     4104    } 
     4105  }; 
     4106   
     4107  this.replace_contact_photo = function(id) 
     4108  { 
     4109    $('#ff_photo').val(id); 
     4110     
     4111    var buttons = this.buttons['upload-photo']; 
     4112    for (var n=0; n < buttons.length; n++) 
     4113      $('#'+buttons[n].id).html(this.get_label(id == '-del-' ? 'addphoto' : 'replacephoto')); 
     4114     
     4115    var img_src = id == '-del-' ? this.env.photo_placeholder : 
     4116      this.env.comm_path + '&_action=photo&_source=' + this.env.source + '&_cid=' + this.env.cid + '&_photo=' + id; 
     4117    $(this.gui_objects.contactphoto).children('img').attr('src', img_src); 
     4118     
     4119    this.enable_command('delete-photo', id != '-del-'); 
     4120  }; 
     4121   
     4122  this.photo_upload_end = function() 
     4123  { 
     4124    this.set_busy(false, null, this.photo_upload_id); 
     4125    delete this.photo_upload_id; 
    41104126  }; 
    41114127 
     
    53765392  }; 
    53775393 
     5394  // post the given form to a hidden iframe 
     5395  this.async_upload_form = function(form, action, onload) 
     5396  { 
     5397    var ts = new Date().getTime(); 
     5398    var frame_name = 'rcmupload'+ts; 
     5399 
     5400    // have to do it this way for IE 
     5401    // otherwise the form will be posted to a new window 
     5402    if (document.all) { 
     5403      var html = '<iframe name="'+frame_name+'" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>'; 
     5404      document.body.insertAdjacentHTML('BeforeEnd', html); 
     5405    } 
     5406    else { // for standards-compilant browsers 
     5407      var frame = document.createElement('iframe'); 
     5408      frame.name = frame_name; 
     5409      frame.style.border = 'none'; 
     5410      frame.style.width = 0; 
     5411      frame.style.height = 0; 
     5412      frame.style.visibility = 'hidden'; 
     5413      document.body.appendChild(frame); 
     5414    } 
     5415 
     5416    // handle upload errors, parsing iframe content in onload 
     5417    $(frame_name).bind('load', {ts:ts}, onload); 
     5418 
     5419    form.target = frame_name; 
     5420    form.action = this.env.comm_path + '&_action=' + action + '&_uploadid=' + ts; 
     5421    form.setAttribute('enctype', 'multipart/form-data'); 
     5422    form.submit(); 
     5423  }; 
     5424   
    53785425  // starts interval for keep-alive/check-recent signal 
    53795426  this.start_keepalive = function() 
  • branches/devel-addressbook/program/localization/en_US/labels.inc

    r4285 r4314  
    289289$labels['save']   = 'Save'; 
    290290$labels['delete'] = 'Delete'; 
     291$labels['addphoto'] = 'Add'; 
     292$labels['replacephoto'] = 'Replace'; 
    291293 
    292294$labels['newcontact']     = 'Create new contact card'; 
  • branches/devel-addressbook/program/localization/en_US/messages.inc

    r4190 r4314  
    129129$messages['messagemarked'] = 'Message(s) marked successfully'; 
    130130$messages['autocompletechars'] = 'Enter at least $min characters for autocompletion'; 
     131$messages['invalidimageformat'] = 'Not a valid image format'; 
    131132 
    132133?> 
  • branches/devel-addressbook/program/steps/addressbook/edit.inc

    r4242 r4314  
    125125 
    126126 
     127function rcmail_upload_photo_form($attrib) 
     128{ 
     129  global $OUTPUT; 
     130 
     131  // add ID if not given 
     132  if (!$attrib['id']) 
     133    $attrib['id'] = 'rcmUploadbox'; 
     134 
     135  // find max filesize value 
     136  $max_filesize = parse_bytes(ini_get('upload_max_filesize')); 
     137  $max_postsize = parse_bytes(ini_get('post_max_size')); 
     138  if ($max_postsize && $max_postsize < $max_filesize) 
     139    $max_filesize = $max_postsize; 
     140  $max_filesize = show_bytes($max_filesize); 
     141   
     142  $hidden = new html_hiddenfield(array('name' => '_cid', 'value' => $GLOBALS['cid'])); 
     143  $input = new html_inputfield(array('type' => 'file', 'name' => '_photo', 'size' => $attrib['size'])); 
     144  $button = new html_inputfield(array('type' => 'button')); 
     145   
     146  $out = html::div($attrib, 
     147    $OUTPUT->form_tag(array('name' => 'uploadform', 'method' => 'post', 'enctype' => 'multipart/form-data'), 
     148      $hidden->show() . 
     149      html::div(null, $input->show()) . 
     150      html::div('hint', rcube_label(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize)))) . 
     151      html::div('buttons', 
     152        $button->show(rcube_label('close'), array('class' => 'button', 'onclick' => "$('#$attrib[id]').hide()")) . ' ' . 
     153        $button->show(rcube_label('upload'), array('class' => 'button mainaction', 'onclick' => JS_OBJECT_NAME . ".command('upload-photo', this.form)")) 
     154      ) 
     155    ) 
     156  ); 
     157   
     158  $OUTPUT->add_label('addphoto','replacephoto'); 
     159  $OUTPUT->add_gui_object('uploadbox', $attrib['id']); 
     160  return $out; 
     161} 
     162 
     163 
    127164// similar function as in /steps/settings/edit_identity.inc 
    128165function get_form_tags($attrib) 
     
    157194$OUTPUT->add_handler('contactedithead', 'rcmail_contact_edithead'); 
    158195$OUTPUT->add_handler('contacteditform', 'rcmail_contact_editform'); 
     196$OUTPUT->add_handler('contactphoto',    'rcmail_contact_photo'); 
     197$OUTPUT->add_handler('photouploadform', 'rcmail_upload_photo_form'); 
    159198 
    160199if (!$CONTACTS->get_result() && $OUTPUT->template_exists('contactadd')) 
  • branches/devel-addressbook/program/steps/addressbook/func.inc

    r4298 r4314  
    100100} 
    101101 
     102$OUTPUT->set_env('photocol', is_array($CONTACT_COLTYPES['photo'])); 
     103 
    102104 
    103105function rcmail_directory_list($attrib) 
     
    288290    $form = $plugin['form']; 
    289291    $record = $plugin['record']; 
    290     $formdata = array(); 
    291292    $edit_mode = $RCMAIL->action != 'show'; 
    292     $pic_img = $attrib['picplaceholder'] ? $CONFIG['skin_path'] . $attrib['picplaceholder'] : 'program/blank.gif'; 
    293293    $del_button = $attrib['deleteicon'] ? html::img(array('src' => $CONFIG['skin_path'] . $attrib['deleteicon'], 'alt' => rcube_label('delete'))) : rcube_label('delete'); 
    294     unset($attrib['picplaceholder'], $attrib['deleteicon']); 
     294    unset($attrib['deleteicon']); 
    295295    $out = ''; 
    296296     
     
    322322        if ($section == 'head') { 
    323323            $content = ''; 
    324              
    325             if ($coltypes['photo']) { 
    326                 if ($record['photo']) 
    327                     $pic_img = $RCMAIL->url(array('_action' => 'photo', '_cid' => $record['ID'], '_source' => $_REQUEST['_source'])); 
    328                 $img = html::img(array('src' => $pic_img, 'border' => 1, 'alt' => '')); 
    329                 if ($edit_mode) { 
    330                     $label = $record['photo'] ? rcube_label('replacephoto') : rcube_label('addphoto'); 
    331                     $img = html::a(array('href' => '#', 'id' => 'contactpicframe', 'title' => $label), $img); 
    332                 } 
    333                 $content .= html::div('contactpic', $img); 
    334             } 
    335324             
    336325            $names_arr = array($record['prefix'], $record['firstname'], $record['middlename'], $record['surname'], $record['suffix']); 
     
    466455 
    467456                        $coltypes[$field] = (array)$colprop + $coltypes[$field]; 
    468                         $formdata[$field][] = array('subtype' => $subtype, 'value' => $val); 
    469457 
    470458                        if ($colprop['subtypes'] || $colprop['limit'] != 1) 
     
    525513 
    526514    if ($edit_mode) { 
    527       $RCMAIL->output->set_env('contactdata', $formdata); 
    528515      $RCMAIL->output->set_env('coltypes', $coltypes + $coltype_lables); 
    529516      $RCMAIL->output->set_env('delbutton', $del_button); 
     
    532519 
    533520    return $out; 
     521} 
     522 
     523 
     524function rcmail_contact_photo($attrib) 
     525{ 
     526    global $CONTACTS, $CONTACT_COLTYPES, $RCMAIL, $CONFIG; 
     527     
     528    if ($result = $CONTACTS->get_result()) 
     529        $record = $result->first(); 
     530     
     531    $photo_img = $attrib['placeholder'] ? $CONFIG['skin_path'] . $attrib['placeholder'] : 'program/blank.gif'; 
     532    unset($attrib['placeholder']); 
     533     
     534    if ($CONTACT_COLTYPES['photo']) { 
     535        $RCMAIL->output->set_env('photo_placeholder', $photo_img); 
     536         
     537        if ($record['photo']) 
     538            $photo_img = $RCMAIL->url(array('_action' => 'photo', '_cid' => $record['ID'], '_source' => $_REQUEST['_source'])); 
     539        $img = html::img(array('src' => $photo_img, 'border' => 1, 'alt' => '')); 
     540        $content = html::div($attrib, $img); 
     541       
     542        if ($RCMAIL->action == 'edit' || $RCMAIL->action == 'add') { 
     543            $RCMAIL->output->add_gui_object('contactphoto', $attrib['id']); 
     544            $hidden = new html_hiddenfield(array('name' => '_photo', 'id' => 'ff_photo')); 
     545            $content .= $hidden->show(); 
     546        } 
     547  } 
     548   
     549  return $content; 
    534550} 
    535551 
  • branches/devel-addressbook/program/steps/addressbook/save.inc

    r4289 r4314  
    3131 
    3232 
     33// handle photo upload for contacts 
     34if ($RCMAIL->action == 'upload-photo') { 
     35    // clear all stored output properties (like scripts and env vars) 
     36    $OUTPUT->reset(); 
     37 
     38    if ($filepath = $_FILES['_photo']['tmp_name']) { 
     39        // TODO: check file type and resize image 
     40        $imageprop = rcmail::imageprops($_FILES['_photo']['tmp_name']); 
     41         
     42        if ($imageprop['width'] && $imageprop['height']) { 
     43            $maxsize = $RCMAIL->config->get('contact_photo_size', 160); 
     44            $tmpfname = tempnam($RCMAIL->config->get('temp_dir'), 'rcmImgConvert'); 
     45            $save_hook = 'attachment_upload'; 
     46             
     47            // scale image to a maximum size 
     48            if (($imageprop['width'] > $maxsize || $imageprop['height'] > $maxsize) && 
     49                  (rcmail::imageconvert(array('in' => $filepath, 'out' => $tmpfname, 'size' => $maxsize.'x'.$maxsize, 'type' => $imageprop['type'])) !== false)) { 
     50                $filepath = $tmpfname; 
     51                $save_hook = 'attachment_save'; 
     52            } 
     53             
     54            // save uploaded file in storage backend 
     55            $attachment = $RCMAIL->plugins->exec_hook($save_hook, array( 
     56                'path' => $filepath, 
     57                'size' => $_FILES['_photo']['size'], 
     58                'name' => $_FILES['_photo']['name'], 
     59                'mimetype' => rc_mime_content_type($filepath, $_FILES['_photo']['name'], 'image/' . $imageprop['type']) 
     60            )); 
     61        } 
     62        else 
     63            $attachment['error'] = rcube_label('invalidimageformat'); 
     64 
     65        if ($attachment['status'] && !$attachment['abort']) { 
     66            $file_id = $attachment['id']; 
     67            $_SESSION['contacts']['files'][$file_id] = $attachment; 
     68            $OUTPUT->command('replace_contact_photo', $file_id); 
     69        } 
     70        else {  // upload failed 
     71            $err = $_FILES['_photo']['error']; 
     72            if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) 
     73                $msg = rcube_label(array('name' => 'filesizeerror', 'vars' => array('size' => show_bytes(parse_bytes(ini_get('upload_max_filesize')))))); 
     74            else if ($attachment['error']) 
     75                $msg = $attachment['error']; 
     76            else 
     77                $msg = rcube_label('fileuploaderror'); 
     78             
     79            $OUTPUT->command('display_message', $msg, 'error'); 
     80        } 
     81    } 
     82    else if ($_SERVER['REQUEST_METHOD'] == 'POST') { 
     83        // if filesize exceeds post_max_size then $_FILES array is empty, 
     84        // show filesizeerror instead of fileuploaderror 
     85        if ($maxsize = ini_get('post_max_size')) 
     86            $msg = rcube_label(array('name' => 'filesizeerror', 'vars' => array('size' => show_bytes(parse_bytes($maxsize))))); 
     87        else 
     88            $msg = rcube_label('fileuploaderror'); 
     89             
     90        $OUTPUT->command('display_message', $msg, 'error'); 
     91    } 
     92     
     93    $OUTPUT->command('photo_upload_end'); 
     94    $OUTPUT->send('iframe'); 
     95} 
     96 
     97 
    3398// read POST values into hash array 
    3499$a_record = array(); 
     
    88153} 
    89154 
     155// get raw photo data if changed 
     156if (isset($a_record['photo'])) { 
     157    if ($a_record['photo'] == '-del-') { 
     158        $a_record['photo'] = ''; 
     159    } 
     160    else if ($tempfile = $_SESSION['contacts']['files'][$a_record['photo']]) { 
     161        $tempfile = $RCMAIL->plugins->exec_hook('attachment_get', $tempfile); 
     162        if ($tempfile['status']) 
     163            $a_record['photo'] = $tempfile['data'] ? $tempfile['data'] : @file_get_contents($tempfile['path']); 
     164    } 
     165    else 
     166        unset($a_record['photo']); 
     167     
     168    // cleanup session data 
     169    $RCMAIL->plugins->exec_hook('attachments_cleanup', array()); 
     170    $RCMAIL->session->remove('contacts'); 
     171} 
     172 
    90173// update an existing contact 
    91174if (!empty($cid)) 
  • branches/devel-addressbook/program/steps/addressbook/show.inc

    r4300 r4314  
    2828// return raw photo of the given contact 
    2929if ($RCMAIL->action == 'photo') { 
    30     if ($record['photo']) { 
     30    if (($file_id = get_input_value('_photo', RCUBE_INPUT_GPC)) && ($tempfile = $_SESSION['contacts']['files'][$file_id])) { 
     31        $tempfile = $RCMAIL->plugins->exec_hook('attachment_display', $tempfile); 
     32        if ($tempfile['status']) { 
     33            if ($tempfile['data']) 
     34                $data = $tempfile['data']; 
     35            else if ($tempfile['path']) 
     36                $data = file_get_contents($tempfile['path']); 
     37        } 
     38    } 
     39    else if ($record['photo']) { 
    3140        $data = is_array($record['photo']) ? $record['photo'][0] : $record['photo']; 
    32         if (!preg_match('![^a-z0-9/+-]!i', $data)) 
     41        if (!preg_match('![^a-z0-9/=+-]!i', $data)) 
    3342            $data = base64_decode($data, true); 
    3443    } 
     
    181190$OUTPUT->add_handler('contacthead', 'rcmail_contact_head'); 
    182191$OUTPUT->add_handler('contactdetails', 'rcmail_contact_details'); 
     192$OUTPUT->add_handler('contactphoto', 'rcmail_contact_photo'); 
    183193 
    184194$OUTPUT->send('contact'); 
  • branches/devel-addressbook/program/steps/mail/compose.inc

    r4190 r4314  
    11301130  $out = html::div($attrib, 
    11311131    $OUTPUT->form_tag(array('name' => 'uploadform', 'method' => 'post', 'enctype' => 'multipart/form-data'), 
    1132       html::div(null, rcmail_compose_attachment_field(array('size' => $attrib[attachmentfieldsize]))) . 
     1132      html::div(null, rcmail_compose_attachment_field(array('size' => $attrib['attachmentfieldsize']))) . 
    11331133      html::div('hint', rcube_label(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize)))) . 
    11341134      html::div('buttons', 
  • branches/devel-addressbook/skins/default/addressbook.css

    r4300 r4314  
    275275} 
    276276 
    277 #contacthead .contactpic 
     277#contactphoto 
    278278{ 
    279279        float: right; 
    280         width: 50px; 
    281         min-height: 50px; 
     280        width: 60px; 
     281        margin-left: 3em; 
     282        margin-right: 4px; 
     283} 
     284 
     285#contactpic 
     286{ 
     287        width: 60px; 
     288        min-height: 60px; 
    282289        border: 1px solid #ccc; 
    283290        background: white; 
    284         margin-left: 3em; 
    285 } 
    286  
    287 #contacthead .contactpic img { 
    288         width: 50px; 
     291} 
     292 
     293#contactpic img { 
     294        width: 60px; 
     295} 
     296 
     297#contactphoto .formlinks 
     298{ 
     299        margin-top: 0.5em; 
     300        text-align: center; 
    289301} 
    290302 
     
    354366} 
    355367 
     368#upload-form 
     369{ 
     370        padding: 6px; 
     371} 
     372 
     373#upload-form div 
     374{ 
     375        padding: 2px; 
     376} 
  • branches/devel-addressbook/skins/default/common.css

    r4227 r4314  
    118118  color: #666; 
    119119  font-size: 11px; 
     120} 
     121 
     122.formlinks a, 
     123.formlinks a:visited 
     124{ 
     125  color: #CC0000; 
     126  font-size: 11px; 
     127  text-decoration: none; 
     128} 
     129 
     130.formlinks a.disabled, 
     131.formlinks a.disabled:visited 
     132{ 
     133  color: #999999; 
    120134} 
    121135 
  • branches/devel-addressbook/skins/default/functions.js

    r4297 r4314  
    8888    composemenu:    {id:'composeoptionsmenu', editable:1}, 
    8989    // toggle: #1486823, #1486930 
    90     uploadmenu:     {id:'attachment-form', editable:1, above:1, toggle:!bw.ie&&!bw.linux } 
     90    uploadmenu:     {id:'attachment-form', editable:1, above:1, toggle:!bw.ie&&!bw.linux }, 
     91    uploadform:     {id:'upload-form', editable:1, toggle:true} 
    9192  }; 
    9293 
     
    129130    if (!above && pos.top + ref.offsetHeight + obj.height() > window.innerHeight) 
    130131      above = true; 
     132 
     133    if (pos.left + obj.width() > window.innerWidth) 
     134      pos.left = window.innerWidth - obj.width() - 30; 
    131135 
    132136    obj.css({ left:pos.left, top:(pos.top + (above ? -obj.height() : ref.offsetHeight)) }); 
     
    494498      rcmail_ui.init_compose_form(); 
    495499  } 
     500  else if (rcmail.env.task == 'addressbook') { 
     501    rcmail.addEventListener('afterupload-photo', function(){ rcmail_ui.show_popup('uploadform', false); }); 
     502  } 
    496503} 
    497504 
  • branches/devel-addressbook/skins/default/mail.css

    r4184 r4314  
    13391339} 
    13401340 
    1341 .formlinks a, 
    1342 .formlinks a:visited 
    1343 { 
    1344   color: #999999; 
    1345   font-size: 11px; 
    1346   text-decoration: none; 
    1347 } 
    1348  
    1349 .formlinks a, 
    1350 .formlinks a:visited 
    1351 { 
    1352   color: #CC0000; 
    1353 } 
    1354  
    13551341#compose-editorfooter 
    13561342{ 
  • branches/devel-addressbook/skins/default/templates/contact.html

    r4297 r4314  
    1010<div id="contact-title" class="boxtitle"><roundcube:label name="contactproperties" /></div> 
    1111<div id="contact-details" class="boxcontent"> 
    12   <roundcube:object name="contacthead" id="contacthead" picPlaceholder="/images/contactpic.png" /> 
     12  <div id="contactphoto"><roundcube:object name="contactphoto" id="contactpic" placeholder="/images/contactpic.png" /></div> 
     13  <roundcube:object name="contacthead" id="contacthead" photoplaceholder="/images/contactpic.png" /> 
     14  <div style="clear:both"></div> 
    1315  <div id="contacttabs"> 
    1416    <roundcube:object name="contactdetails" /> 
  • branches/devel-addressbook/skins/default/templates/contactadd.html

    r4297 r4314  
    66<script type="text/javascript" src="/functions.js"></script> 
    77</head> 
    8 <body class="iframe"> 
     8<body class="iframe" onload="rcube_init_mail_ui()"> 
    99 
    1010<div id="contact-title" class="boxtitle"><roundcube:label name="addcontact" /></div> 
    1111<div id="contact-details" class="boxcontent"> 
    1212<form name="editform" method="post" action="./"> 
    13   <roundcube:object name="contactedithead" id="contacthead" size="16" form="editform" picPlaceholder="/images/contactpic.png" /> 
     13  <div id="contactphoto"> 
     14    <roundcube:object name="contactphoto" id="contactpic" placeholder="/images/contactpic.png" /> 
     15    <div class="formlinks"> 
     16      <roundcube:button command="upload-photo" id="uploadformlink" type="link" label="addphoto" class="disabled" classAct="active" onclick="rcmail_ui.show_popup('uploadform', true);return false" condition="env:photocol" /><br/> 
     17      <roundcube:button command="delete-photo" type="link" label="delete" class="disabled" classAct="active" condition="env:photocol" /> 
     18    </div> 
     19  </div> 
     20  <roundcube:object name="contactedithead" id="contacthead" size="16" form="editform" /> 
     21  <div style="clear:both"></div> 
     22   
    1423  <div id="contacttabs"> 
    1524    <roundcube:object name="contacteditform" size="40" textareacols="60" deleteIcon="/images/icons/delete.png" form="editform" /> 
     
    2130</form> 
    2231</div> 
     32 
     33<roundcube:object name="photoUploadForm" id="upload-form" size="30" class="popupmenu" /> 
     34 
    2335<script type="text/javascript">rcube_init_tabs('contacttabs')</script> 
    2436 
  • branches/devel-addressbook/skins/default/templates/contactedit.html

    r4297 r4314  
    66<script type="text/javascript" src="/functions.js"></script> 
    77</head> 
    8 <body class="iframe"> 
     8<body class="iframe" onload="rcube_init_mail_ui()"> 
    99 
    1010<div id="contact-title" class="boxtitle"><roundcube:label name="editcontact" /></div> 
    1111<div id="contact-details" class="boxcontent"> 
    1212<form name="editform" method="post" action="./"> 
    13   <roundcube:object name="contactedithead" id="contacthead" size="16" form="editform" picPlaceholder="/images/contactpic.png" /> 
     13  <div id="contactphoto"> 
     14    <roundcube:object name="contactphoto" id="contactpic" placeholder="/images/contactpic.png" /> 
     15    <div class="formlinks"> 
     16      <roundcube:button command="upload-photo" id="uploadformlink" type="link" label="replacephoto" class="disabled" classAct="active" onclick="rcmail_ui.show_popup('uploadform', true);return false" condition="env:photocol" /><br/> 
     17      <roundcube:button command="delete-photo" type="link" label="delete" class="disabled" classAct="active" condition="env:photocol" /> 
     18    </div> 
     19  </div> 
     20  <roundcube:object name="contactedithead" id="contacthead" size="16" form="editform" /> 
     21  <div style="clear:both"></div> 
     22   
    1423  <div id="contacttabs"> 
    1524    <roundcube:object name="contacteditform" size="40" textareacols="60" deleteIcon="/images/icons/delete.png" form="editform" /> 
     
    2130</form> 
    2231</div> 
     32 
     33<roundcube:object name="photoUploadForm" id="upload-form" size="30" class="popupmenu" /> 
     34 
    2335<script type="text/javascript">rcube_init_tabs('contacttabs')</script> 
    2436 
Note: See TracChangeset for help on using the changeset viewer.