Changeset 0501b63 in github
- Timestamp:
- Jan 18, 2011 1:00:57 PM (2 years ago)
- Branches:
- master, HEAD, courier-fix, dev-browser-capabilities, pdo, release-0.6, release-0.7, release-0.8
- Children:
- 1bcb2f3
- Parents:
- e81a307
- Files:
-
- 1 added
- 38 edited
-
config/main.inc.php.dist (modified) (5 diffs)
-
program/include/html.php (modified) (3 diffs)
-
program/include/main.inc (modified) (2 diffs)
-
program/include/rcmail.php (modified) (7 diffs)
-
program/include/rcube_addressbook.php (modified) (9 diffs)
-
program/include/rcube_browser.php (modified) (1 diff)
-
program/include/rcube_contacts.php (modified) (9 diffs)
-
program/include/rcube_ldap.php (modified) (15 diffs)
-
program/include/rcube_plugin.php (modified) (1 diff)
-
program/include/rcube_plugin_api.php (modified) (3 diffs)
-
program/include/rcube_shared.inc (modified) (1 diff)
-
program/include/rcube_template.php (modified) (1 diff)
-
program/include/rcube_vcard.php (modified) (7 diffs)
-
program/js/app.js (modified) (14 diffs)
-
program/localization/en_US/labels.inc (modified) (2 diffs)
-
program/localization/en_US/messages.inc (modified) (2 diffs)
-
program/steps/addressbook/copy.inc (modified) (1 diff)
-
program/steps/addressbook/delete.inc (modified) (1 diff)
-
program/steps/addressbook/edit.inc (modified) (5 diffs)
-
program/steps/addressbook/export.inc (modified) (1 diff)
-
program/steps/addressbook/func.inc (modified) (7 diffs)
-
program/steps/addressbook/groups.inc (modified) (1 diff)
-
program/steps/addressbook/import.inc (modified) (1 diff)
-
program/steps/addressbook/list.inc (modified) (1 diff)
-
program/steps/addressbook/mailto.inc (modified) (1 diff)
-
program/steps/addressbook/save.inc (modified) (6 diffs)
-
program/steps/addressbook/search.inc (modified) (2 diffs)
-
program/steps/addressbook/show.inc (modified) (4 diffs)
-
program/steps/mail/autocomplete.inc (modified) (2 diffs)
-
program/steps/mail/compose.inc (modified) (1 diff)
-
skins/default/addressbook.css (modified) (1 diff)
-
skins/default/common.css (modified) (2 diffs)
-
skins/default/functions.js (modified) (6 diffs)
-
skins/default/iehacks.css (modified) (1 diff)
-
skins/default/images/contactpic.png (added)
-
skins/default/mail.css (modified) (1 diff)
-
skins/default/templates/contact.html (modified) (1 diff)
-
skins/default/templates/contactadd.html (modified) (2 diffs)
-
skins/default/templates/contactedit.html (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
config/main.inc.php.dist
rf5e7b353 r0501b63 309 309 $rcmail_config['mime_magic'] = '/usr/share/misc/magic'; 310 310 311 // path to imagemagick identify binary 312 $rcmail_config['im_identify_path'] = null; 313 314 // path to imagemagick convert binary 315 $rcmail_config['im_convert_path'] = null; 316 317 // maximum size of uploaded contact photos in pixel 318 $rcmail_config['contact_photo_size'] = 160; 319 311 320 // Enable DNS checking for e-mail address validation 312 321 $rcmail_config['email_dns_check'] = false; … … 346 355 // use this format for today's date display (date or strftime format) 347 356 $rcmail_config['date_today'] = 'H:i'; 357 358 // use this format for date display without time (date or strftime format) 359 $rcmail_config['date_format'] = 'Y-m-d'; 348 360 349 361 // store draft message is this mailbox … … 459 471 'search_base_dn' => '', 460 472 'search_filter' => '', // e.g. '(&(objectClass=posixAccount)(uid=%u))' 461 462 473 'writable' => false, // Indicates if we can write to the LDAP directory or not. 463 474 // If writable is true then these fields need to be populated: … … 468 479 'ldap_version' => 3, // using LDAPv3 469 480 'search_fields' => array('mail', 'cn'), // fields to search in 470 'name_field' => 'cn', // this field represents the contact's name 471 'email_field' => 'mail', // this field represents the contact's e-mail 472 'surname_field' => 'sn', // this field represents the contact's last name 473 'firstname_field' => 'gn', // this field represents the contact's first name 481 'fieldmap' => array( // mapping of contact fields to directory attributes 482 // Roundcube => LDAP 483 'name' => 'cn', 484 'surname' => 'sn', 485 'firstname' => 'givenName', 486 'email' => 'mail', 487 'phone:home' => 'homePhone', 488 'phone:work' => 'telephoneNumber', 489 'phone:mobile' => 'mobile', 490 'street' => 'street', 491 'zipcode' => 'postalCode', 492 'locality' => 'l', 493 'country' => 'c', 494 'organization' => 'o', 495 ), 474 496 'sort' => 'cn', // The field to sort the listing by. 475 497 'scope' => 'sub', // search mode: sub|base|list … … 490 512 $rcmail_config['autocomplete_min_length'] = 1; 491 513 514 // show address fields in this order 515 // available placeholders: {street}, {locality}, {zipcode}, {country}, {region} 516 $rcmail_config['address_template'] = '{street}<br/>{locality} {zipcode}<br/>{country} {region}'; 517 492 518 // ---------------------------------- 493 519 // USER PREFERENCES -
program/include/html.php
re224b00 r0501b63 72 72 public static function tag($tagname, $attrib = array(), $content = null, $allowed_attrib = null) 73 73 { 74 if (is_string($attrib)) 75 $attrib = array('class' => $attrib); 76 74 77 $inline_tags = array('a','span','img'); 75 78 $suffix = $attrib['nl'] || ($content && $attrib['nl'] !== false && !in_array($tagname, $inline_tags)) ? "\n" : ''; … … 148 151 } 149 152 return self::tag('a', $attr, $cont, array_merge(self::$common_attrib, 150 array('href','target','name',' onclick','onmouseover','onmouseout','onmousedown','onmouseup')));153 array('href','target','name','rel','onclick','onmouseover','onmouseout','onmousedown','onmouseup'))); 151 154 } 152 155 … … 502 505 protected $options = array(); 503 506 protected $allowed = array('name','size','tabindex','autocomplete', 504 'multiple','onchange','disabled' );507 'multiple','onchange','disabled','rel'); 505 508 506 509 /** -
program/include/main.inc
ra76cbdd r0501b63 800 800 // format each col 801 801 foreach ($a_show_cols as $col) 802 $table->add($col, Q( $row_data[$col]));802 $table->add($col, Q(is_array($row_data[$col]) ? $row_data[$col][0] : $row_data[$col])); 803 803 804 804 $c++; … … 820 820 */ 821 821 function rcmail_get_edit_field($col, $value, $attrib, $type='text') 822 { 822 { 823 static $colcounts = array(); 824 823 825 $fname = '_'.$col; 824 $attrib['name'] = $fname ;825 826 if ($type=='checkbox')827 {826 $attrib['name'] = $fname . ($attrib['array'] ? '[]' : ''); 827 $attrib['class'] = trim($attrib['class'] . ' ff_' . $col); 828 829 if ($type == 'checkbox') { 828 830 $attrib['value'] = '1'; 829 831 $input = new html_checkbox($attrib); 830 } 831 else if ($type=='textarea') 832 { 832 } 833 else if ($type == 'textarea') { 833 834 $attrib['cols'] = $attrib['size']; 834 835 $input = new html_textarea($attrib); 835 } 836 else 836 } 837 else if ($type == 'select') { 838 $input = new html_select($attrib); 839 $input->add('---', ''); 840 $input->add(array_values($attrib['options']), array_keys($attrib['options'])); 841 } 842 else { 843 if ($attrib['type'] != 'text' && $attrib['type'] != 'hidden') 844 $attrib['type'] = 'text'; 837 845 $input = new html_inputfield($attrib); 846 } 838 847 839 848 // use value from post 840 if (!empty($_POST[$fname])) 841 $value = get_input_value($fname, RCUBE_INPUT_POST, 842 $type == 'textarea' && strpos($attrib['class'], 'mce_editor')!==false ? true : false); 849 if (isset($_POST[$fname])) { 850 $postvalue = get_input_value($fname, RCUBE_INPUT_POST, 851 $type == 'textarea' && strpos($attrib['class'], 'mce_editor')!==false ? true : false); 852 $value = $attrib['array'] ? $postvalue[intval($colcounts[$col]++)] : $postvalue; 853 } 843 854 844 855 $out = $input->show($value); 845 856 846 857 return $out; 847 }858 } 848 859 849 860 -
program/include/rcmail.php
ra76cbdd r0501b63 115 115 116 116 private $texts; 117 private $ books = array();117 private $address_books = array(); 118 118 private $action_map = array(); 119 119 … … 331 331 if ($plugin['instance'] instanceof rcube_addressbook) { 332 332 $contacts = $plugin['instance']; 333 } 334 // use existing instance 335 else if (isset($this->address_books[$id]) && is_a($this->address_books[$id], 'rcube_addressbook') && (!$writeable || !$this->address_books[$id]->readonly)) { 336 $contacts = $this->address_books[$id]; 333 337 } 334 338 else if ($id && $ldap_config[$id]) { … … 352 356 353 357 // add to the 'books' array for shutdown function 354 if (!i n_array($contacts, $this->books))355 $this-> books[] = $contacts;358 if (!isset($this->address_books[$id])) 359 $this->address_books[$id] = $contacts; 356 360 357 361 return $contacts; … … 374 378 // We are using the DB address book 375 379 if ($abook_type != 'ldap') { 376 $contacts = new rcube_contacts($this->db, null); 380 if (!isset($this->address_books['0'])) 381 $this->address_books['0'] = new rcube_contacts($this->db, $this->user->ID); 377 382 $list['0'] = array( 378 'id' => 0,383 'id' => '0', 379 384 'name' => rcube_label('personaladrbook'), 380 'groups' => $ contacts->groups,385 'groups' => $this->address_books['0']->groups, 381 386 'readonly' => false, 382 387 'autocomplete' => in_array('sql', $autocomplete) … … 399 404 $list = $plugin['sources']; 400 405 401 if ($writeable && !empty($list)) { 402 foreach ($list as $idx => $item) { 403 if ($item['readonly']) { 406 foreach ($list as $idx => $item) { 407 // register source for shutdown function 408 if (!is_object($this->address_books[$item['id']])) 409 $this->address_books[$item['id']] = $item; 410 // remove from list if not writeable as requested 411 if ($writeable && $item['readonly']) 404 412 unset($list[$idx]); 405 } 406 } 407 } 408 413 } 414 409 415 return $list; 410 416 } … … 1079 1085 $this->smtp->disconnect(); 1080 1086 1081 foreach ($this->books as $book) 1082 if (is_object($book)) 1087 foreach ($this->address_books as $book) { 1088 if (!is_object($book)) // maybe an address book instance wasn't fetched using get_address_book() yet 1089 $book = $this->get_address_book($book['id']); 1090 if (is_a($book, 'rcube_addressbook')) 1083 1091 $book->close(); 1092 } 1084 1093 1085 1094 // before closing the database connection, write session data … … 1308 1317 1309 1318 /** 1319 * Use imagemagick or GD lib to read image properties 1320 * 1321 * @param string Absolute file path 1322 * @return mixed Hash array with image props like type, width, height or False on error 1323 */ 1324 public static function imageprops($filepath) 1325 { 1326 $rcmail = rcmail::get_instance(); 1327 if ($cmd = $rcmail->config->get('im_identify_path', false)) { 1328 list(, $type, $size) = explode(' ', strtolower(rcmail::exec($cmd. ' 2>/dev/null {in}', array('in' => $filepath)))); 1329 if ($size) 1330 list($width, $height) = explode('x', $size); 1331 } 1332 else if (function_exists('getimagesize')) { 1333 $imsize = @getimagesize($filepath); 1334 $width = $imsize[0]; 1335 $height = $imsize[1]; 1336 $type = preg_replace('!image/!', '', $imsize['mime']); 1337 } 1338 1339 return $type ? array('type' => $type, 'width' => $width, 'height' => $height) : false; 1340 } 1341 1342 1343 /** 1344 * Convert an image to a given size and type using imagemagick (ensures input is an image) 1345 * 1346 * @param $p['in'] Input filename (mandatory) 1347 * @param $p['out'] Output filename (mandatory) 1348 * @param $p['size'] Width x height of resulting image, e.g. "160x60" 1349 * @param $p['type'] Output file type, e.g. "jpg" 1350 * @param $p['-opts'] Custom command line options to ImageMagick convert 1351 * @return Success of convert as true/false 1352 */ 1353 public static function imageconvert($p) 1354 { 1355 $result = false; 1356 $rcmail = rcmail::get_instance(); 1357 $convert = $rcmail->config->get('im_convert_path', false); 1358 $identify = $rcmail->config->get('im_identify_path', false); 1359 1360 // imagemagick is required for this 1361 if (!$convert) 1362 return false; 1363 1364 if (!(($imagetype = @exif_imagetype($p['in'])) && ($type = image_type_to_extension($imagetype, false)))) 1365 list(, $type) = explode(' ', strtolower(rcmail::exec($identify . ' 2>/dev/null {in}', $p))); # for things like eps 1366 1367 $type = strtr($type, array("jpeg" => "jpg", "tiff" => "tif", "ps" => "eps", "ept" => "eps")); 1368 $p += array('type' => $type, 'types' => "bmp,eps,gif,jp2,jpg,png,svg,tif", 'quality' => 75); 1369 $p['-opts'] = array('-resize' => $p['size'].'>') + (array)$p['-opts']; 1370 1371 if (in_array($type, explode(',', $p['types']))) # Valid type? 1372 $result = rcmail::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace RGB -quality {quality} {-opts} {in} {type}:{out}', $p) === ""; 1373 1374 return $result; 1375 } 1376 1377 1378 /** 1379 * Construct shell command, execute it and return output as string. 1380 * Keywords {keyword} are replaced with arguments 1381 * 1382 * @param $cmd Format string with {keywords} to be replaced 1383 * @param $values (zero, one or more arrays can be passed) 1384 * @return output of command. shell errors not detectable 1385 */ 1386 public static function exec(/* $cmd, $values1 = array(), ... */) 1387 { 1388 $args = func_get_args(); 1389 $cmd = array_shift($args); 1390 $values = $replacements = array(); 1391 1392 // merge values into one array 1393 foreach ($args as $arg) 1394 $values += (array)$arg; 1395 1396 preg_match_all('/({(-?)([a-z]\w*)})/', $cmd, $matches, PREG_SET_ORDER); 1397 foreach ($matches as $tags) { 1398 list(, $tag, $option, $key) = $tags; 1399 $parts = array(); 1400 1401 if ($option) { 1402 foreach ((array)$values["-$key"] as $key => $value) { 1403 if ($value === true || $value === false || $value === null) 1404 $parts[] = $value ? $key : ""; 1405 else foreach ((array)$value as $val) 1406 $parts[] = "$key " . escapeshellarg($val); 1407 } 1408 } 1409 else { 1410 foreach ((array)$values[$key] as $value) 1411 $parts[] = escapeshellarg($value); 1412 } 1413 1414 $replacements[$tag] = join(" ", $parts); 1415 } 1416 1417 // use strtr behaviour of going through source string once 1418 $cmd = strtr($cmd, $replacements); 1419 1420 return (string)shell_exec($cmd); 1421 } 1422 1423 1424 /** 1310 1425 * Helper method to set a cookie with the current path and host settings 1311 1426 * -
program/include/rcube_addressbook.php
rf5e7b353 r0501b63 6 6 | | 7 7 | This file is part of the Roundcube Webmail client | 8 | Copyright (C) 2006-20 09, The Roundcube Dev Team |8 | Copyright (C) 2006-2011, The Roundcube Dev Team | 9 9 | Licensed under the GNU GPL | 10 10 | | … … 28 28 abstract class rcube_addressbook 29 29 { 30 /** public properties */ 31 var $primary_key; 32 var $groups = false; 33 var $readonly = true; 34 var $ready = false; 35 var $list_page = 1; 36 var $page_size = 10; 30 /** constants for error reporting **/ 31 const ERROR_READ_ONLY = 1; 32 const ERROR_NO_CONNECTION = 2; 33 const ERROR_INCOMPLETE = 3; 34 const ERROR_SAVING = 4; 35 36 /** public properties (mandatory) */ 37 public $primary_key; 38 public $groups = false; 39 public $readonly = true; 40 public $ready = false; 41 public $list_page = 1; 42 public $page_size = 10; 43 public $coltypes = array('name' => array('limit'=>1), 'firstname' => array('limit'=>1), 'surname' => array('limit'=>1), 'email' => array('limit'=>1)); 44 45 protected $error; 37 46 38 47 /** … … 54 63 */ 55 64 abstract function reset(); 65 66 /** 67 * Refresh saved search set after data has changed 68 * 69 * @return mixed New search set 70 */ 71 function refresh_search() 72 { 73 return $this->get_search_set(); 74 } 56 75 57 76 /** … … 70 89 * @param string Search value 71 90 * @param boolean True if results are requested, False if count only 72 * @return Indexed list of contact records and 'count' value 73 */ 74 abstract function search($fields, $value, $strict=false, $select=true); 91 * @param boolean True to skip the count query (select only) 92 * @param array List of fields that cannot be empty 93 * @return object rcube_result_set List of contact records and 'count' value 94 */ 95 abstract function search($fields, $value, $strict=false, $select=true, $nocount=false, $required=array()); 75 96 76 97 /** … … 99 120 100 121 /** 122 * Returns the last error occured (e.g. when updating/inserting failed) 123 * 124 * @return array Hash array with the following fields: type, message 125 */ 126 function get_error() 127 { 128 return $this->error; 129 } 130 131 /** 132 * Setter for errors for internal use 133 * 134 * @param int Error type (one of this class' error constants) 135 * @param string Error message (name of a text label) 136 */ 137 protected function set_error($type, $message) 138 { 139 $this->error = array('type' => $type, 'message' => $message); 140 } 141 142 /** 101 143 * Close connection to source 102 144 * Called on script shutdown … … 130 172 * 131 173 * @param array Assoziative array with save data 174 * Keys: Field name with optional section in the form FIELD:SECTION 175 * Values: Field value. Can be either a string or an array of strings for multiple values 132 176 * @param boolean True to check for duplicates first 133 177 * @return mixed The created record ID on success, False on error … … 139 183 140 184 /** 185 * Create new contact records for every item in the record set 186 * 187 * @param object rcube_result_set Recordset to insert 188 * @param boolean True to check for duplicates first 189 * @return array List of created record IDs 190 */ 191 function insertMultiple($recset, $check=false) 192 { 193 $ids = array(); 194 if (is_object($recset) && is_a($recset, rcube_result_set)) { 195 while ($row = $recset->next()) { 196 if ($insert = $this->insert($row, $check)) 197 $ids[] = $insert; 198 } 199 } 200 return $ids; 201 } 202 203 /** 141 204 * Update a specific contact record 142 205 * 143 206 * @param mixed Record identifier 144 207 * @param array Assoziative array with save data 208 * Keys: Field name with optional section in the form FIELD:SECTION 209 * Values: Field value. Can be either a string or an array of strings for multiple values 145 210 * @return boolean True on success, False on error 146 211 */ … … 177 242 * List all active contact groups of this source 178 243 * 244 * @param string Optional search string to match group name 179 245 * @return array Indexed list of contact groups, each a hash array 180 246 */ 181 function list_groups( )247 function list_groups($search = null) 182 248 { 183 249 /* empty for address books don't supporting groups */ … … 261 327 return array(); 262 328 } 329 330 331 /** 332 * Utility function to return all values of a certain data column 333 * either as flat list or grouped by subtype 334 * 335 * @param string Col name 336 * @param array Record data array as used for saving 337 * @param boolean True to return one array with all values, False for hash array with values grouped by type 338 * @return array List of column values 339 */ 340 function get_col_values($col, $data, $flat = false) 341 { 342 $out = array(); 343 foreach ($data as $c => $values) { 344 if (strpos($c, $col) === 0) { 345 if ($flat) { 346 $out = array_merge($out, (array)$values); 347 } 348 else { 349 list($f, $type) = explode(':', $c); 350 $out[$type] = array_merge((array)$out[$type], (array)$values); 351 } 352 } 353 } 354 355 return $out; 356 } 357 263 358 } 264 359 -
program/include/rcube_browser.php
rf5e7b353 r0501b63 69 69 $this->pngalpha = $this->mz || $this->safari || ($this->ie && $this->ver>=5.5) || 70 70 ($this->ie && $this->ver>=5 && $this->mac) || ($this->opera && $this->ver>=7) ? true : false; 71 $this->imgdata = !$this->ie; 71 72 } 72 73 } -
program/include/rcube_contacts.php
rf5e7b353 r0501b63 48 48 49 49 // public properties 50 var $primary_key = 'contact_id'; 51 var $readonly = false; 52 var $groups = true; 53 var $list_page = 1; 54 var $page_size = 10; 55 var $group_id = 0; 56 var $ready = false; 50 public $primary_key = 'contact_id'; 51 public $readonly = false; 52 public $groups = true; 53 public $list_page = 1; 54 public $page_size = 10; 55 public $group_id = 0; 56 public $ready = false; 57 public $coltypes = array('name', 'firstname', 'surname', 'middlename', 'prefix', 'suffix', 'nickname', 58 'jobtitle', 'organization', 'department', 'assistant', 'manager', 59 'gender', 'maidenname', 'spouse', 'email', 'phone', 'address', 60 'birthday', 'anniversary', 'website', 'im', 'notes', 'photo'); 57 61 58 62 … … 153 157 * List the current set of contact records 154 158 * 155 * @param array List of cols to show 159 * @param array List of cols to show, Null means all 156 160 * @param int Only return this number of records, use negative values for tail 157 161 * @param boolean True to skip the count query (select only) … … 188 192 $this->group_id); 189 193 194 // determine whether we have to parse the vcard or if only db cols are requested 195 $read_vcard = !$cols || count(array_intersect($cols, $this->table_cols)) < count($cols); 196 190 197 while ($sql_result && ($sql_arr = $this->db->fetch_assoc($sql_result))) { 191 198 $sql_arr['ID'] = $sql_arr[$this->primary_key]; 199 200 if ($read_vcard) 201 $sql_arr = $this->convert_db_data($sql_arr); 202 else 203 $sql_arr['email'] = preg_split('/,\s*/', $sql_arr['email']); 204 192 205 // make sure we have a name to display 193 206 if (empty($sql_arr['name'])) 194 $sql_arr['name'] = $sql_arr['email']; 207 $sql_arr['name'] = $sql_arr['email'][0]; 208 195 209 $this->result->add($sql_arr); 196 210 } … … 223 237 * @param boolean True to skip the count query (select only) 224 238 * @param array List of fields that cannot be empty 225 * @return Indexed list of contact records and 'count' value239 * @return object rcube_result_set Contact records and 'count' value 226 240 */ 227 241 function search($fields, $value, $strict=false, $select=true, $nocount=false, $required=array()) … … 346 360 347 361 if ($sql_arr = $this->db->fetch_assoc()) { 348 $ sql_arr['ID'] = $sql_arr[$this->primary_key];362 $record = $this->convert_db_data($sql_arr); 349 363 $this->result = new rcube_result_set(1); 350 $this->result->add($ sql_arr);351 } 352 353 return $assoc && $ sql_arr ? $sql_arr: $this->result;364 $this->result->add($record); 365 } 366 367 return $assoc && $record ? $record : $this->result; 354 368 } 355 369 … … 390 404 function insert($save_data, $check=false) 391 405 { 392 if ( is_object($save_data) && is_a($save_data, rcube_result_set))393 return $this->insert_recset($save_data, $check);406 if (!is_array($save_data)) 407 return false; 394 408 395 409 $insert_id = $existing = false; 396 410 397 if ($check) 398 $existing = $this->search('email', $save_data['email'], true, false); 399 411 if ($check) { 412 foreach ($save_data as $col => $values) { 413 if (strpos($col, 'email') === 0) { 414 foreach ((array)$values as $email) { 415 if ($existing = $this->search('email', $email, true, false)) 416 break 2; 417 } 418 } 419 } 420 } 421 422 $save_data = $this->convert_save_data($save_data); 400 423 $a_insert_cols = $a_insert_values = array(); 401 424 402 foreach ($this->table_cols as $col) 403 if (isset($save_data[$col])) { 404 $a_insert_cols[] = $this->db->quoteIdentifier($col); 405 $a_insert_values[] = $this->db->quote($save_data[$col]); 406 } 425 foreach ($save_data as $col => $value) { 426 $a_insert_cols[] = $this->db->quoteIdentifier($col); 427 $a_insert_values[] = $this->db->quote($value); 428 } 407 429 408 430 if (!$existing->count && !empty($a_insert_cols)) { … … 427 449 428 450 /** 429 * Insert new contacts for each row in set430 */431 function insert_recset($result, $check=false)432 {433 $ids = array();434 while ($row = $result->next()) {435 if ($insert = $this->insert($row, $check))436 $ids[] = $insert;437 }438 return $ids;439 }440 441 442 /**443 451 * Update a specific contact record 444 452 * … … 451 459 $updated = false; 452 460 $write_sql = array(); 453 454 foreach ($this->table_cols as $col) 455 if (isset($save_cols[$col])) 456 $write_sql[] = sprintf("%s=%s", $this->db->quoteIdentifier($col), 457 $this->db->quote($save_cols[$col])); 461 $record = $this->get_record($id, true); 462 $save_cols = $this->convert_save_data($save_cols, $record); 463 464 foreach ($save_cols as $col => $value) { 465 $write_sql[] = sprintf("%s=%s", $this->db->quoteIdentifier($col), $this->db->quote($value)); 466 } 458 467 459 468 if (!empty($write_sql)) { … … 469 478 470 479 $updated = $this->db->affected_rows(); 480 $this->result = null; // clear current result (from get_record()) 471 481 } 472 482 473 483 return $updated; 484 } 485 486 487 private function convert_db_data($sql_arr) 488 { 489 $record = array(); 490 $record['ID'] = $sql_arr[$this->primary_key]; 491 492 if ($sql_arr['vcard']) { 493 unset($sql_arr['email']); 494 $vcard = new rcube_vcard($sql_arr['vcard']); 495 $record += $vcard->get_assoc() + $sql_arr; 496 } 497 else { 498 $record += $sql_arr; 499 $record['email'] = preg_split('/,\s*/', $record['email']); 500 } 501 502 return $record; 503 } 504 505 506 private function convert_save_data($save_data, $record = array()) 507 { 508 $out = array(); 509 510 // copy values into vcard object 511 $vcard = new rcube_vcard($record['vcard'] ? $record['vcard'] : $save_data['vcard']); 512 $vcard->reset(); 513 foreach ($save_data as $key => $values) { 514 list($field, $section) = explode(':', $key); 515 foreach ((array)$values as $value) { 516 if (isset($value)) 517 $vcard->set($field, $value, $section); 518 } 519 } 520 $out['vcard'] = $vcard->export(); 521 522 foreach ($this->table_cols as $col) { 523 $key = $col; 524 if (!isset($save_data[$key])) 525 $key .= ':home'; 526 if (isset($save_data[$key])) 527 $out[$col] = is_array($save_data[$key]) ? join(',', $save_data[$key]) : $save_data[$key]; 528 } 529 530 // save all e-mails in database column 531 $out['email'] = join(", ", $vcard->email); 532 533 return $out; 474 534 } 475 535 -
program/include/rcube_ldap.php
rf5e7b353 r0501b63 27 27 class rcube_ldap extends rcube_addressbook 28 28 { 29 var$conn;30 var$prop = array();31 var$fieldmap = array();32 33 var$filter = '';34 var$result = null;35 var$ldap_result = null;36 var$sort_col = '';37 var$mail_domain = '';38 var$debug = false;29 protected $conn; 30 protected $prop = array(); 31 protected $fieldmap = array(); 32 33 protected $filter = ''; 34 protected $result = null; 35 protected $ldap_result = null; 36 protected $sort_col = ''; 37 protected $mail_domain = ''; 38 protected $debug = false; 39 39 40 40 /** public properties */ 41 var $primary_key = 'ID'; 42 var $readonly = true; 43 var $list_page = 1; 44 var $page_size = 10; 45 var $ready = false; 41 public $primary_key = 'ID'; 42 public $readonly = true; 43 public $list_page = 1; 44 public $page_size = 10; 45 public $ready = false; 46 public $coltypes = array(); 46 47 47 48 … … 58 59 $this->prop = $p; 59 60 60 foreach ($p as $prop => $value) 61 if (preg_match('/^(.+)_field$/', $prop, $matches)) 62 $this->fieldmap[$matches[1]] = $this->_attr_name(strtolower($value)); 61 // fieldmap property is given 62 if (is_array($p['fieldmap'])) { 63 foreach ($p['fieldmap'] as $rf => $lf) 64 $this->fieldmap[$rf] = $this->_attr_name(strtolower($lf)); 65 } 66 else { 67 // read deprecated *_field properties to remain backwards compatible 68 foreach ($p as $prop => $value) 69 if (preg_match('/^(.+)_field$/', $prop, $matches)) 70 $this->fieldmap[$matches[1]] = $this->_attr_name(strtolower($value)); 71 } 72 73 // use fieldmap to advertise supported coltypes to the application 74 foreach ($this->fieldmap as $col => $lf) { 75 list($col, $type) = explode(':', $col); 76 if (!is_array($this->coltypes[$col])) { 77 $subtypes = $type ? array($type) : null; 78 $this->coltypes[$col] = array('limit' => 2, 'subtypes' => $subtypes); 79 } 80 else if ($type) { 81 $this->coltypes[$col]['subtypes'][] = $type; 82 $this->coltypes[$col]['limit']++; 83 } 84 if ($type && !$this->fieldmap[$col]) 85 $this->fieldmap[$col] = $lf; 86 } 87 88 if ($this->fieldmap['street'] && $this->fieldmap['locality']) 89 $this->coltypes['address'] = array('limit' => 1); 90 else if ($this->coltypes['address']) 91 $this->coltypes['address'] = array('type' => 'textarea', 'childs' => null, 'limit' => 1, 'size' => 40); 63 92 64 93 // make sure 'required_fields' is an array … … 456 485 if ($entry && ($rec = ldap_get_attributes($this->conn, $entry))) 457 486 { 458 $this->_debug("S: OK" );487 $this->_debug("S: OK"/* . print_r($rec, true)*/); 459 488 460 489 $rec = array_change_key_case($rec, CASE_LOWER); … … 483 512 $newentry = array(); 484 513 $newentry['objectClass'] = $this->prop['LDAP_Object_Classes']; 485 foreach ($save_cols as $col => $val) { 486 $fld = $this->_map_field($col); 514 foreach ($this->fieldmap as $col => $fld) { 515 $val = $save_cols[$col]; 516 if (is_array($val)) 517 $val = array_filter($val); // remove empty entries 487 518 if ($fld && $val) { 488 519 // The field does exist, add it to the entry. … … 492 523 493 524 // Verify that the required fields are set. 494 // We know that the email address is required as a default of rcube, so495 // we will default its value into any unfilled required fields.496 525 foreach ($this->prop['required_fields'] as $fld) { 526 $missing = null; 497 527 if (!isset($newentry[$fld])) { 498 $newentry[$fld] = $newentry[$this->_map_field('email')]; 499 } // end if 500 } // end foreach 528 $missing[] = $fld; 529 } 530 } 531 532 // abort process if requiered fields are missing 533 // TODO: generate message saying which fields are missing 534 if ($missing) { 535 $this->set_error(self::ERROR_INCOMPLETE, 'formincomplete'); 536 return false; 537 } 501 538 502 539 // Build the new entries DN. 503 $dn = $this->prop['LDAP_rdn'].'='.rcube_ldap::quote_string($newentry[$this->prop['LDAP_rdn']], true) 504 .','.$this->prop['base_dn']; 540 $dn = $this->prop['LDAP_rdn'].'='.rcube_ldap::quote_string($newentry[$this->prop['LDAP_rdn']], true).','.$this->prop['base_dn']; 505 541 506 542 $this->_debug("C: Add [dn: $dn]: ".print_r($newentry, true)); … … 509 545 if ($res === FALSE) { 510 546 $this->_debug("S: ".ldap_error($this->conn)); 547 $this->set_error(self::ERROR_SAVING, 'errorsaving'); 511 548 return false; 512 549 } // end if … … 534 571 $replacedata = array(); 535 572 $deletedata = array(); 536 foreach ($ save_cols as $col => $val) {537 $ fld = $this->_map_field($col);573 foreach ($this->fieldmap as $col => $fld) { 574 $val = $save_cols[$col]; 538 575 if ($fld) { 539 576 // The field does exist compare it to the ldap record. … … 567 604 if (!ldap_mod_del($this->conn, $dn, $deletedata)) { 568 605 $this->_debug("S: ".ldap_error($this->conn)); 606 $this->set_error(self::ERROR_SAVING, 'errorsaving'); 569 607 return false; 570 608 } … … 576 614 if ($replacedata[$this->prop['LDAP_rdn']]) { 577 615 $newdn = $this->prop['LDAP_rdn'].'=' 578 .rcube_ldap::quote_string($replacedata[$this->prop['LDAP_rdn']], true)579 .','.$this->prop['base_dn']; 616 .rcube_ldap::quote_string($replacedata[$this->prop['LDAP_rdn']], true) 617 .','.$this->prop['base_dn']; 580 618 if ($dn != $newdn) { 581 619 $newrdn = $this->prop['LDAP_rdn'].'=' 582 .rcube_ldap::quote_string($replacedata[$this->prop['LDAP_rdn']], true);620 .rcube_ldap::quote_string($replacedata[$this->prop['LDAP_rdn']], true); 583 621 unset($replacedata[$this->prop['LDAP_rdn']]); 584 622 } … … 590 628 $this->_debug("S: ".ldap_error($this->conn)); 591 629 return false; 592 }630 } 593 631 $this->_debug("S: OK"); 594 632 } // end if … … 600 638 if (!ldap_mod_add($this->conn, $dn, $newdata)) { 601 639 $this->_debug("S: ".ldap_error($this->conn)); 640 $this->set_error(self::ERROR_SAVING, 'errorsaving'); 602 641 return false; 603 642 } … … 639 678 if ($res === FALSE) { 640 679 $this->_debug("S: ".ldap_error($this->conn)); 680 $this->set_error(self::ERROR_SAVING, 'errorsaving'); 641 681 return false; 642 682 } // end if … … 680 720 private function _ldap2result($rec) 681 721 { 682 global $RCMAIL;683 684 722 $out = array(); 685 723 … … 689 727 foreach ($this->fieldmap as $rf => $lf) 690 728 { 691 if ($rec[$lf]['count']) { 692 if ($rf == 'email' && $this->mail_domain && !strpos($rec[$lf][0], '@')) 693 $out[$rf] = sprintf('%s@%s', $rec[$lf][0], $this->mail_domain); 729 for ($i=0; $i < $rec[$lf]['count']; $i++) { 730 if (!($value = $rec[$lf][$i])) 731 continue; 732 if ($rf == 'email' && $this->mail_domain && !strpos($value, '@')) 733 $out[$rf][] = sprintf('%s@%s', $value, $this->mail_domain); 734 else if (in_array($rf, array('street','zipcode','locality','country','region'))) 735 $out['address'][$i][$rf] = $value; 736 else if ($rec[$lf]['count'] > 1) 737 $out[$rf][] = $value; 694 738 else 695 $out[$rf] = $ rec[$lf][0];739 $out[$rf] = $value; 696 740 } 697 741 } … … 742 786 function quote_string($str, $dn=false) 743 787 { 788 // take firt entry if array given 789 if (is_array($str)) 790 $str = reset($str); 791 744 792 if ($dn) 745 793 $replace = array(','=>'\2c', '='=>'\3d', '+'=>'\2b', '<'=>'\3c', -
program/include/rcube_plugin.php
rf5e7b353 r0501b63 84 84 */ 85 85 abstract function init(); 86 86 87 88 /** 89 * Attempt to load the given plugin which is required for the current plugin 90 * 91 * @param string Plugin name 92 * @return boolean True on success, false on failure 93 */ 94 public function require_plugin($plugin_name) 95 { 96 return $this->api->load_plugin($plugin_name); 97 } 98 99 87 100 /** 88 101 * Load local config file from plugins directory. -
program/include/rcube_plugin_api.php
rba3377f r0501b63 110 110 $this->config = $rcmail->config; 111 111 112 $plugins_dir = dir($this->dir);113 $plugins_dir = unslashify($plugins_dir->path);114 112 $plugins_enabled = (array)$rcmail->config->get('plugins', array()); 115 116 113 foreach ($plugins_enabled as $plugin_name) { 117 $fn = $plugins_dir . DIRECTORY_SEPARATOR . $plugin_name . DIRECTORY_SEPARATOR . $plugin_name . '.php'; 118 119 if (file_exists($fn)) { 120 include($fn); 121 122 // instantiate class if exists 123 if (class_exists($plugin_name, false)) { 124 $plugin = new $plugin_name($this); 125 // check inheritance... 126 if (is_subclass_of($plugin, 'rcube_plugin')) { 127 // ... task, request type and framed mode 128 if ((!$plugin->task || preg_match('/^('.$plugin->task.')$/i', $rcmail->task)) 129 && (!$plugin->noajax || is_a($this->output, 'rcube_template')) 130 && (!$plugin->noframe || empty($_REQUEST['_framed'])) 131 ) { 132 $plugin->init(); 133 $this->plugins[] = $plugin; 134 } 135 } 136 } 137 else { 138 raise_error(array('code' => 520, 'type' => 'php', 139 'file' => __FILE__, 'line' => __LINE__, 140 'message' => "No plugin class $plugin_name found in $fn"), true, false); 141 } 142 } 143 else { 144 raise_error(array('code' => 520, 'type' => 'php', 145 'file' => __FILE__, 'line' => __LINE__, 146 'message' => "Failed to load plugin file $fn"), true, false); 147 } 114 $this->load_plugin($plugin_name); 148 115 } 149 116 … … 159 126 160 127 // load required core plugin if no derivate was found 161 if (!$loaded) { 162 $fn = $plugins_dir . DIRECTORY_SEPARATOR . $plugin_name . DIRECTORY_SEPARATOR . $plugin_name . '.php'; 163 164 if (file_exists($fn)) { 165 include_once($fn); 166 167 if (class_exists($plugin_name, false)) { 168 $plugin = new $plugin_name($this); 169 // check inheritance 170 if (is_subclass_of($plugin, 'rcube_plugin')) { 171 if (!$plugin->task || preg_match('/('.$plugin->task.')/i', $rcmail->task)) { 172 $plugin->init(); 173 $this->plugins[] = $plugin; 174 } 175 $loaded = true; 176 } 177 } 178 } 179 } 180 128 if (!$loaded) 129 $loaded = $this->load_plugin($plugin_name); 130 181 131 // trigger fatal error if still not loaded 182 132 if (!$loaded) { 183 133 raise_error(array('code' => 520, 'type' => 'php', 184 'file' => __FILE__, 'line' => __LINE__,185 'message' => "Requried plugin $plugin_name was not loaded"), true, true);134 'file' => __FILE__, 'line' => __LINE__, 135 'message' => "Requried plugin $plugin_name was not loaded"), true, true); 186 136 } 187 137 } … … 191 141 192 142 // maybe also register a shudown function which triggers shutdown functions of all plugin objects 143 } 144 145 146 /** 147 * Load the specified plugin 148 * 149 * @param string Plugin name 150 * @return boolean True on success, false if not loaded or failure 151 */ 152 public function load_plugin($plugin_name) 153 { 154 static $plugins_dir; 155 156 $rcmail = rcmail::get_instance(); 157 158 if (!$plugins_dir) { 159 $dir = dir($this->dir); 160 $plugins_dir = unslashify($dir->path); 161 } 162 163 // plugin already loaded 164 if ($this->plugins[$plugin_name] || class_exists($plugin_name, false)) 165 return true; 166 167 $fn = $plugins_dir . DIRECTORY_SEPARATOR . $plugin_name . DIRECTORY_SEPARATOR . $plugin_name . '.php'; 168 169 if (file_exists($fn)) { 170 include($fn); 171 172 // instantiate class if exists 173 if (class_exists($plugin_name, false)) { 174 $plugin = new $plugin_name($this); 175 // check inheritance... 176 if (is_subclass_of($plugin, 'rcube_plugin')) { 177 // ... task, request type and framed mode 178 if ((!$plugin->task || preg_match('/^('.$plugin->task.')$/i', $rcmail->task)) /* 179 && (!$plugin->noajax || is_a($rcmail->output, 'rcube_template')) 180 && (!$plugin->noframe || empty($_REQUEST['_framed']))*/ 181 ) { 182 $plugin->init(); 183 $this->plugins[$plugin_name] = $plugin; 184 } 185 return true; 186 } 187 } 188 else { 189 raise_error(array('code' => 520, 'type' => 'php', 190 'file' => __FILE__, 'line' => __LINE__, 191 'message' => "No plugin class $plugin_name found in $fn"), true, false); 192 } 193 } 194 else { 195 raise_error(array('code' => 520, 'type' => 'php', 196 'file' => __FILE__, 'line' => __LINE__, 197 'message' => "Failed to load plugin file $fn"), true, false); 198 } 199 200 return false; 193 201 } 194 202 -
program/include/rcube_shared.inc
rf5e7b353 r0501b63 486 486 } 487 487 488 489 /** 490 * Detect image type of the given binary data by checking magic numbers 491 * 492 * @param string Binary file content 493 * @return string Detected mime-type or jpeg as fallback 494 */ 495 function rc_image_content_type($data) 496 { 497 $type = 'jpeg'; 498 if (preg_match('/^\x89\x50\x4E\x47/', $data)) $type = 'png'; 499 else if (preg_match('/^\x47\x49\x46\x38/', $data)) $type = 'gif'; 500 else if (preg_match('/^\x00\x00\x01\x00/', $data)) $type = 'ico'; 501 // else if (preg_match('/^\xFF\xD8\xFF\xE0/', $data)) $type = 'jpeg'; 502 503 return 'image/' . $type; 504 } 505 506 488 507 /** 489 508 * A method to guess encoding of a string. -
program/include/rcube_template.php
rd8d467c6 r0501b63 997 997 998 998 // we already have a <form> tag 999 if ($attrib['form']) 999 if ($attrib['form']) { 1000 if ($this->framed || !empty($_REQUEST['_framed'])) 1001 $hidden->add(array('name' => '_framed', 'value' => '1')); 1000 1002 return $hidden->show() . $content; 1003 } 1001 1004 else 1002 1005 return $this->form_tag($attrib, $hidden->show() . $content); -
program/include/rcube_vcard.php
rf5e7b353 r0501b63 6 6 | | 7 7 | This file is part of the Roundcube Webmail client | 8 | Copyright (C) 2008-20 09, The Roundcube Dev Team |8 | Copyright (C) 2008-2011, The Roundcube Dev Team | 9 9 | Licensed under the GNU GPL | 10 10 | | … … 34 34 'N' => array(array('','','','','')), 35 35 ); 36 private $fieldmap = array( 37 'phone' => 'TEL', 38 'birthday' => 'BDAY', 39 'website' => 'URL', 40 'notes' => 'NOTE', 41 'email' => 'EMAIL', 42 'address' => 'ADR', 43 'gender' => 'X-GENDER', 44 'maidenname' => 'X-MAIDENNAME', 45 'anniversary' => 'X-ANNIVERSARY', 46 'assistant' => 'X-ASSISTANT', 47 'manager' => 'X-MANAGER', 48 'spouse' => 'X-SPOUSE', 49 ); 50 private $typemap = array('iPhone' => 'mobile', 'CELL' => 'mobile'); 51 private $phonetypemap = array('HOME1' => 'HOME', 'BUSINESS1' => 'WORK', 'BUSINESS2' => 'WORK2', 'WORKFAX' => 'BUSINESSFAX'); 52 private $addresstypemap = array('BUSINESS' => 'WORK'); 53 private $immap = array('X-JABBER' => 'jabber', 'X-ICQ' => 'icq', 'X-MSN' => 'msn', 'X-AIM' => 'aim', 'X-YAHOO' => 'yahoo', 'X-SKYPE' => 'skype', 'X-SKYPE-USERNAME' => 'skype'); 36 54 37 55 public $business = false; … … 108 126 109 127 /** 128 * Return vCard data as associative array to be unsed in Roundcube address books 129 * 130 * @return array Hash array with key-value pairs 131 */ 132 public function get_assoc() 133 { 134 $out = array('name' => $this->displayname); 135 $typemap = $this->typemap; 136 137 // copy name fields to output array 138 foreach (array('firstname','surname','middlename','nickname','organization') as $col) 139 $out[$col] = $this->$col; 140 141 $out['prefix'] = $this->raw['N'][0][3]; 142 $out['suffix'] = $this->raw['N'][0][4]; 143 144 // convert from raw vcard data into associative data for Roundcube 145 foreach (array_flip($this->fieldmap) as $tag => $col) { 146 foreach ((array)$this->raw[$tag] as $i => $raw) { 147 if (is_array($raw)) { 148 $k = -1; 149 $key = $col; 150 $subtype = $typemap[$raw['type'][++$k]] ? $typemap[$raw['type'][$k]] : strtolower($raw['type'][$k]); 151 while ($k < count($raw['type']) && ($subtype == 'internet' || $subtype == 'pref')) 152 $subtype = $typemap[$raw['type'][++$k]] ? $typemap[$raw['type'][$k]] : strtolower($raw['type'][$k]); 153 if ($subtype) 154 $key .= ':' . $subtype; 155 156 // split ADR values into assoc array 157 if ($tag == 'ADR') { 158 list(,, $value['street'], $value['locality'], $value['region'], $value['zipcode'], $value['country']) = $raw; 159 $out[$key][] = $value; 160 } 161 else 162 $out[$key][] = $raw[0]; 163 } 164 else { 165 $out[$col][] = $raw; 166 } 167 } 168 } 169 170 // handle special IM fields as used by Apple 171 foreach ($this->immap as $tag => $type) { 172 foreach ((array)$this->raw[$tag] as $i => $raw) { 173 $out['im:'.$type][] = $raw[0]; 174 } 175 } 176 177 // copy photo data 178 if ($this->raw['PHOTO']) 179 $out['photo'] = $this->raw['PHOTO'][0][0]; 180 181 return $out; 182 } 183 184 185 /** 110 186 * Convert the data structure into a vcard 3.0 string 111 187 */ … … 113 189 { 114 190 return self::rfc2425_fold(self::vcard_encode($this->raw)); 191 } 192 193 194 /** 195 * Clear the given fields in the loaded vcard data 196 * 197 * @param array List of field names to be reset 198 */ 199 public function reset($fields = null) 200 { 201 if (!$fields) 202 $fields = array_merge(array_values($this->fieldmap), array_keys($this->immap), array('FN','N','ORG','NICKNAME','EMAIL','ADR','BDAY')); 203 204 foreach ($fields as $f) 205 unset($this->raw[$f]); 206 207 if (!$this->raw['N']) 208 $this->raw['N'] = array(array('','','','','')); 209 if (!$this->raw['FN']) 210 $this->raw['FN'] = array(); 211 212 $this->email = array(); 115 213 } 116 214 … … 121 219 * @param string Field name 122 220 * @param string Field value 123 * @param string Section name 124 */ 125 public function set($field, $value, $section = 'HOME') 126 { 221 * @param string Type/section name 222 */ 223 public function set($field, $value, $type = 'HOME') 224 { 225 $field = strtolower($field); 226 $type = strtoupper($type); 227 $typemap = array_flip($this->typemap); 228 127 229 switch ($field) { 128 230 case 'name': … … 131 233 break; 132 234 235 case 'surname': 236 $this->raw['N'][0][0] = $value; 237 break; 238 133 239 case 'firstname': 134 240 $this->raw['N'][0][1] = $value; 135 241 break; 136 242 137 case 'surname': 138 $this->raw['N'][0][0] = $value; 139 break; 140 243 case 'middlename': 244 $this->raw['N'][0][2] = $value; 245 break; 246 247 case 'prefix': 248 $this->raw['N'][0][3] = $value; 249 break; 250 251 case 'suffix': 252 $this->raw['N'][0][4] = $value; 253 break; 254 141 255 case 'nickname': 142 256 $this->raw['NICKNAME'][0][0] = $value; … … 147 261 break; 148 262 263 case 'photo': 264 $encoded = !preg_match('![^a-z0-9/=+-]!i', $value); 265 $this->raw['PHOTO'][0] = array(0 => $encoded ? $value : base64_encode($value), 'BASE64' => true); 266 break; 267 149 268 case 'email': 150 $index = $this->get_type_index('EMAIL', $section); 151 if (!is_array($this->raw['EMAIL'][$index])) { 152 $this->raw['EMAIL'][$index] = array(0 => $value, 'type' => array('INTERNET', $section, 'pref')); 153 } 154 else { 155 $this->raw['EMAIL'][$index][0] = $value; 269 $this->raw['EMAIL'][] = array(0 => $value, 'type' => array_filter(array('INTERNET', $type))); 270 $this->email[] = $value; 271 break; 272 273 case 'im': 274 // save IM subtypes into extension fields 275 $typemap = array_flip($this->immap); 276 if ($field = $typemap[strtolower($type)]) 277 $this->raw[$field][] = array(0 => $value); 278 break; 279 280 case 'birthday': 281 if ($val = @strtotime($value)) 282 $this->raw['BDAY'][] = array(0 => date('Y-m-d', $val), 'value' => array('date')); 283 break; 284 285 case 'address': 286 if ($this->addresstypemap[$type]) 287 $type = $this->addresstypemap[$type]; 288 289 $value = $value[0] ? $value : array('', '', $value['street'], $value['locality'], $value['region'], $value['zipcode'], $value['country']); 290 291 // fall through if not empty 292 if (!strlen(join('', $value))) 293 break; 294 295 default: 296 if ($field == 'phone' && $this->phonetypemap[$type]) 297 $type = $this->phonetypemap[$type]; 298 299 if (($tag = $this->fieldmap[$field]) && (is_array($value) || strlen($value))) { 300 $index = count($this->raw[$tag]); 301 $this->raw[$tag][$index] = (array)$value; 302 if ($type) 303 $this->raw[$tag][$index]['type'] = array(($typemap[$type] ? $typemap[$type] : $type)); 156 304 } 157 305 break; -
program/js/app.js
re81a307 r0501b63 320 320 if ((this.env.action=='add' || this.env.action=='edit') && this.gui_objects.editform) { 321 321 this.enable_command('save', true); 322 $("input[type='text']").first().select(); 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'); 324 325 for (var col in this.env.coltypes) 326 this.init_edit_field(col, null); 327 328 $('.contactfieldgroup .row a.deletebutton').click(function(){ ref.delete_edit_field(this); return false }); 329 330 $('select.addfieldmenu').change(function(e){ 331 ref.insert_edit_field($(this).val(), $(this).attr('rel'), this); 332 this.selectedIndex = 0; 333 }); 334 335 $("input[type='text']").first().focus(); 323 336 } 324 337 else if (this.gui_objects.qsearchbox) { … … 640 653 break; 641 654 } 655 656 // clear empty input fields 657 $('input.placeholder').each(function(){ if (this.value == this._placeholder) this.value = ''; }); 642 658 } 643 659 … … 997 1013 case 'export': 998 1014 if (this.contact_list.rowcount > 0) { 999 var add_url = (this.env.source ? '_source='+urlencode(this.env.source)+'&' : ''); 1000 if (this.env.search_request) 1001 add_url += '_search='+this.env.search_request; 1002 1003 this.goto_url('export', add_url); 1004 } 1015 this.goto_url('export', { _source:this.env.source, _gid:this.env.group, _search:this.env.search_request }); 1016 } 1017 break; 1018 1019 case 'upload-photo': 1020 this.upload_contact_photo(props); 1021 break; 1022 1023 case 'delete-photo': 1024 this.replace_contact_photo('-del-'); 1005 1025 break; 1006 1026 … … 1159 1179 this.is_framed = function() 1160 1180 { 1161 return (this.env.framed && parent.rcmail );1181 return (this.env.framed && parent.rcmail && parent.rcmail != this && parent.rcmail.command); 1162 1182 }; 1163 1183 … … 3178 3198 // create hidden iframe and post upload form 3179 3199 if (send) { 3180 var ts = new Date().getTime(); 3181 var frame_name = 'rcmupload'+ts; 3182 3183 // have to do it this way for IE 3184 // otherwise the form will be posted to a new window 3185 if (document.all) { 3186 var html = '<iframe name="'+frame_name+'" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>'; 3187 document.body.insertAdjacentHTML('BeforeEnd',html); 3188 } 3189 else { // for standards-compilant browsers 3190 var frame = document.createElement('iframe'); 3191 frame.name = frame_name; 3192 frame.style.border = 'none'; 3193 frame.style.width = 0; 3194 frame.style.height = 0; 3195 frame.style.visibility = 'hidden'; 3196 document.body.appendChild(frame); 3197 } 3198 3199 // handle upload errors, parsing iframe content in onload 3200 $(frame_name).bind('load', {ts:ts}, function(e) { 3200 this.async_upload_form(form, 'upload', function(e) { 3201 3201 var d, content = ''; 3202 3202 try { … … 3218 3218 rcmail.env.uploadframe = e.data.ts; 3219 3219 }); 3220 3221 form.target = frame_name;3222 form.action = this.env.comm_path+'&_action=upload&_uploadid='+ts;3223 form.setAttribute('enctype', 'multipart/form-data');3224 form.submit();3225 3220 3226 3221 // display upload indicator and cancel button … … 3980 3975 3981 3976 3977 this.init_edit_field = function(col, elem) 3978 { 3979 if (!elem) 3980 elem = $('.ff_' + col); 3981 3982 elem.focus(function(){ ref.focus_textfield(this); }) 3983 .blur(function(){ ref.blur_textfield(this); }) 3984 .each(function(){ this._placeholder = ref.env.coltypes[col].label; ref.blur_textfield(this); }); 3985 }; 3986 3987 this.insert_edit_field = function(col, section, menu) 3988 { 3989 // just make pre-defined input field visible 3990 var elem = $('#ff_'+col); 3991 if (elem.length) { 3992 elem.show().focus(); 3993 $(menu).children('option[value="'+col+'"]').attr('disabled', true); 3994 } 3995 else { 3996 var lastelem = $('.ff_'+col), 3997 appendcontainer = $('#contactsection'+section+' .contactcontroller'+col); 3998 3999 if (!appendcontainer.length) 4000 appendcontainer = $('<fieldset>').addClass('contactfieldgroup contactcontroller'+col).insertAfter($('#contactsection'+section+' .contactfieldgroup').last()); 4001 4002 if (appendcontainer.length && appendcontainer.get(0).nodeName == 'FIELDSET') { 4003 var input, colprop = this.env.coltypes[col], 4004 row = $('<div>').addClass('row'), 4005 cell = $('<div>').addClass('contactfieldcontent data'), 4006 label = $('<div>').addClass('contactfieldlabel label'); 4007 4008 if (colprop.subtypes_select) 4009 label.html(colprop.subtypes_select); 4010 else 4011 label.html(colprop.label); 4012 4013 var name_suffix = colprop.limit != 1 ? '[]' : ''; 4014 if (colprop.type == 'text' || colprop.type == 'date') { 4015 input = $('<input>') 4016 .addClass('ff_'+col) 4017 .attr('type', 'text') 4018 .attr('name', '_'+col+name_suffix) 4019 .attr('size', colprop.size) 4020 .appendTo(cell); 4021 4022 this.init_edit_field(col, input); 4023 } 4024 else if (colprop.type == 'composite') { 4025 var childcol, cp, first; 4026 for (var childcol in colprop.childs) { 4027 cp = colprop.childs[childcol]; 4028 input = $('<input>') 4029 .addClass('ff_'+childcol) 4030 .attr('type', 'text') 4031 .attr('name', '_'+childcol+name_suffix) 4032 .attr('size', cp.size) 4033 .appendTo(cell); 4034 cell.append(" "); 4035 this.init_edit_field(childcol, input); 4036 if (!first) first = input; 4037 } 4038 input = first; // set focus to the first of this composite fields 4039 } 4040 else if (colprop.type == 'select') { 4041 input = $('<select>') 4042 .addClass('ff_'+col) 4043 .attr('name', '_'+col+name_suffix) 4044 .appendTo(cell); 4045 4046 var options = input.attr('options'); 4047 options[options.length] = new Option('---', ''); 4048 if (colprop.options) 4049 $.each(colprop.options, function(i, val){ options[options.length] = new Option(val, i); }); 4050 } 4051 4052 if (input) { 4053 var delbutton = $('<a href="#del"></a>') 4054 .addClass('contactfieldbutton deletebutton') 4055 .attr('title', this.get_label('delete')) 4056 .attr('rel', col) 4057 .html(this.env.delbutton) 4058 .click(function(){ ref.delete_edit_field(this); return false }) 4059 .appendTo(cell); 4060 4061 row.append(label).append(cell).appendTo(appendcontainer.show()); 4062 input.first().focus(); 4063 4064 // disable option if limit reached 4065 if (!colprop.count) colprop.count = 0; 4066 if (++colprop.count == colprop.limit && colprop.limit) 4067 $(menu).children('option[value="'+col+'"]').attr('disabled', true); 4068 } 4069 } 4070 } 4071 }; 4072 4073 this.delete_edit_field = function(elem) 4074 { 4075 var col = $(elem).attr('rel'), 4076 colprop = this.env.coltypes[col], 4077 fieldset = $(elem).parents('fieldset.contactfieldgroup'), 4078 addmenu = fieldset.parent().find('select.addfieldmenu'); 4079 4080 // just clear input but don't hide the last field 4081 if (--colprop.count <= 0 && colprop.visible) 4082 $(elem).parent().children('input').val('').blur(); 4083 else { 4084 $(elem).parents('div.row').remove(); 4085 // hide entire fieldset if no more rows 4086 if (!fieldset.children('div.row').length) 4087 fieldset.hide(); 4088 } 4089 4090 // enable option in add-field selector or insert it if necessary 4091 if (addmenu.length) { 4092 var option = addmenu.children('option[value="'+col+'"]'); 4093 if (option.length) 4094 option.attr('disabled', false); 4095 else 4096 option = $('<option>').attr('value', col).html(colprop.label).appendTo(addmenu); 4097 addmenu.show(); 4098 } 4099 }; 4100 4101 4102 this.upload_contact_photo = function(form) 4103 { 4104 if (form && form.elements._photo.value) { 4105 this.async_upload_form(form, 'upload-photo', function(e) { 4106 rcmail.set_busy(false, null, rcmail.photo_upload_id); 4107 }); 4108 4109 // display upload indicator 4110 this.photo_upload_id = this.set_busy(true, 'uploading'); 4111 } 4112 }; 4113 4114 this.replace_contact_photo = function(id) 4115 { 4116 $('#ff_photo').val(id); 4117 4118 var buttons = this.buttons['upload-photo']; 4119 for (var n=0; n < buttons.length; n++) 4120 $('#'+buttons[n].id).html(this.get_label(id == '-del-' ? 'addphoto' : 'replacephoto')); 4121 4122 var img_src = id == '-del-' ? this.env.photo_placeholder : 4123 this.env.comm_path + '&_action=photo&_source=' + this.env.source + '&_cid=' + this.env.cid + '&_photo=' + id; 4124 $(this.gui_objects.contactphoto).children('img').attr('src', img_src); 4125 4126 this.enable_command('delete-photo', id != '-del-'); 4127 }; 4128 4129 this.photo_upload_end = function() 4130 { 4131 this.set_busy(false, null, this.photo_upload_id); 4132 delete this.photo_upload_id; 4133 }; 4134 4135 3982 4136 /*********************************************************/ 3983 4137 /********* user settings methods *********/ … … 4506 4660 }; 4507 4661 4662 4663 this.focus_textfield = function(elem) 4664 { 4665 elem._hasfocus = true; 4666 var $elem = $(elem); 4667 if ($elem.hasClass('placeholder') || $elem.val() == elem._placeholder) 4668 $elem.val('').removeClass('placeholder').attr('spellcheck', true); 4669 }; 4670 4671 this.blur_textfield = function(elem) 4672 { 4673 elem._hasfocus = false; 4674 var $elem = $(elem); 4675 if (elem._placeholder && (!$elem.val() || $elem.val() == elem._placeholder)) 4676 $elem.addClass('placeholder').attr('spellcheck', false).val(elem._placeholder); 4677 }; 4678 4508 4679 // write to the document/window title 4509 4680 this.set_pagetitle = function(title) … … 4952 5123 /********* remote request methods *********/ 4953 5124 /********************************************************/ 5125 5126 // compose a valid url with the given parameters 5127 this.url = function(action, query) 5128 { 5129 var querystring = typeof(query) == 'string' ? '&' + query : ''; 5130 5131 if (typeof action != 'string') 5132 query = action; 5133 else if (!query || typeof(query) != 'object') 5134 query = {}; 5135 5136 if (action) 5137 query._action = action; 5138 else 5139 query._action = this.env.action; 5140 5141 var base = this.env.comm_path; 5142 5143 // overwrite task name 5144 if (query._action.match(/([a-z]+)\/([a-z-_]+)/)) { 5145 query._action = RegExp.$2; 5146 base = base.replace(/\_task=[a-z]+/, '_task='+RegExp.$1); 5147 } 5148 5149 // remove undefined values 5150 var param = {}; 5151 for (var k in query) { 5152 if (typeof(query[k]) != 'undefined' && query[k] !== null) 5153 param[k] = query[k]; 5154 } 5155 5156 return base + '&' + $.param(param) + querystring; 5157 }; 4954 5158 4955 5159 this.redirect = function(url, lock) … … 4966 5170 this.goto_url = function(action, query, lock) 4967 5171 { 4968 var url = this.env.comm_path, 4969 querystring = query ? '&'+query : ''; 4970 4971 // overwrite task name 4972 if (action.match(/([a-z]+)\/([a-z-_]+)/)) { 4973 action = RegExp.$2; 4974 url = url.replace(/\_task=[a-z]+/, '_task='+RegExp.$1); 4975 } 4976 4977 this.redirect(url+'&_action='+action+querystring, lock); 5172 this.redirect(this.url(action, query)); 4978 5173 }; 4979 5174 … … 4981 5176 this.http_request = function(action, query, lock) 4982 5177 { 4983 var url = this.env.comm_path; 4984 4985 // overwrite task name 4986 if (action.match(/([a-z]+)\/([a-z-_]+)/)) { 4987 action = RegExp.$2; 4988 url = url.replace(/\_task=[a-z]+/, '_task='+RegExp.$1); 4989 } 5178 var url = this.url(action, query); 4990 5179 4991 5180 // trigger plugin hook … … 5000 5189 } 5001 5190 5002 url += '&_remote=1 &_action=' + action + (query ? '&' : '') + query;5191 url += '&_remote=1'; 5003 5192 5004 5193 // send request … … 5014 5203 this.http_post = function(action, postdata, lock) 5015 5204 { 5016 var url = this.env.comm_path; 5017 5018 // overwrite task name 5019 if (action.match(/([a-z]+)\/([a-z-_]+)/)) { 5020 action = RegExp.$2; 5021 url = url.replace(/\_task=[a-z]+/, '_task='+RegExp.$1); 5022 } 5023 5024 url += '&_action=' + action; 5205 var url = this.url(action); 5025 5206 5026 5207 if (postdata && typeof(postdata) == 'object') { … … 5169 5350 }; 5170 5351 5352 // post the given form to a hidden iframe 5353 this.async_upload_form = function(form, action, onload) 5354 { 5355 var ts = new Date().getTime(); 5356 var frame_name = 'rcmupload'+ts; 5357 5358 // have to do it this way for IE 5359 // otherwise the form will be posted to a new window 5360 if (document.all) { 5361 var html = '<iframe name="'+frame_name+'" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>'; 5362 document.body.insertAdjacentHTML('BeforeEnd', html); 5363 } 5364 else { // for standards-compilant browsers 5365 var frame = document.createElement('iframe'); 5366 frame.name = frame_name; 5367 frame.style.border = 'none'; 5368 frame.style.width = 0; 5369 frame.style.height = 0; 5370 frame.style.visibility = 'hidden'; 5371 document.body.appendChild(frame); 5372 } 5373 5374 // handle upload errors, parsing iframe content in onload 5375 $(frame_name).bind('load', {ts:ts}, onload); 5376 5377 form.target = frame_name; 5378 form.action = this.url(action, { _uploadid:ts }); 5379 form.setAttribute('enctype', 'multipart/form-data'); 5380 form.submit(); 5381 }; 5382 5171 5383 // starts interval for keep-alive/check-recent signal 5172 5384 this.start_keepalive = function() -
program/localization/en_US/labels.inc
rf5e7b353 r0501b63 244 244 245 245 // address boook 246 $labels['name'] = 'Display name'; 247 $labels['firstname'] = 'First name'; 248 $labels['surname'] = 'Last name'; 249 $labels['email'] = 'E-Mail'; 250 246 $labels['name'] = 'Display name'; 247 $labels['firstname'] = 'First name'; 248 $labels['surname'] = 'Last name'; 249 $labels['middlename'] = 'Middle name'; 250 $labels['nameprefix'] = 'Prefix'; 251 $labels['namesuffix'] = 'Suffix'; 252 $labels['nickname'] = 'Nickname'; 253 $labels['jobtitle'] = 'Job title'; 254 $labels['organization'] = 'Company'; 255 $labels['department'] = 'Department'; 256 $labels['gender'] = 'Gender'; 257 $labels['maidenname'] = 'Maiden name'; 258 $labels['email'] = 'E-Mail'; 259 $labels['phone'] = 'Phone'; 260 $labels['address'] = 'Address'; 261 $labels['street'] = 'Street'; 262 $labels['locality'] = 'City'; 263 $labels['zipcode'] = 'Zip code'; 264 $labels['region'] = 'Region'; 265 $labels['country'] = 'Country'; 266 $labels['birthday'] = 'Birthday'; 267 $labels['anniversary'] = 'Anniversary'; 268 $labels['website'] = 'Website'; 269 $labels['instantmessenger'] = 'IM'; 270 $labels['notes'] = 'Notes'; 271 $labels['male'] = 'male'; 272 $labels['female'] = 'female'; 273 $labels['manager'] = 'Manager'; 274 $labels['assistant'] = 'Assistant'; 275 $labels['spouse'] = 'Spouse'; 276 277 $labels['addfield'] = 'Add field...'; 251 278 $labels['addcontact'] = 'Add new contact'; 252 279 $labels['editcontact'] = 'Edit contact'; … … 259 286 $labels['delete'] = 'Delete'; 260 287 $labels['rename'] = 'Rename'; 288 $labels['addphoto'] = 'Add'; 289 $labels['replacephoto'] = 'Replace'; 261 290 262 291 $labels['newcontact'] = 'Create new contact card'; -
program/localization/en_US/messages.inc
rf5e7b353 r0501b63 105 105 $messages['addresswriterror'] = 'The selected address book is not writeable'; 106 106 $messages['contactaddedtogroup'] = 'Successfully added the contacts to this group'; 107 $messages['contactremovedfromgroup'] = 'Successfully remove contacts from this group';107 $messages['contactremovedfromgroup'] = 'Successfully removed contacts from this group'; 108 108 $messages['importwait'] = 'Importing, please wait...'; 109 109 $messages['importerror'] = 'Import failed! The uploaded file is not a valid vCard file.'; … … 138 138 $messages['folderupdated'] = 'Folder updated successfully'; 139 139 $messages['foldercreated'] = 'Folder created successfully'; 140 $messages['invalidimageformat'] = 'Not a valid image format'; 140 141 141 142 ?> -
program/steps/addressbook/copy.inc
rf5e7b353 r0501b63 49 49 50 50 if (!$plugin['abort']) { 51 if ($insert_id = $TARGET->insert($ a_record, false)) {51 if ($insert_id = $TARGET->insert($plugin['record'], false)) { 52 52 $ids[] = $insert_id; 53 53 $success++; -
program/steps/addressbook/delete.inc
rf5e7b353 r0501b63 39 39 $result = $CONTACTS->count(); 40 40 41 // update saved search after data changed 42 if (($search_request = $_REQUEST['_search']) && isset($_SESSION['search'][$search_request])) 43 $_SESSION['search'][$search_request] = $CONTACTS->refresh_search(); 44 41 45 // update message count display 42 46 $OUTPUT->set_env('pagecount', ceil($result->count / $CONTACTS->page_size)); -
program/steps/addressbook/edit.inc
rf5e7b353 r0501b63 32 32 33 33 34 function rcmail_contact_edit form($attrib)35 { 36 global $RCMAIL, $CONTACTS , $OUTPUT;34 function rcmail_contact_edithead($attrib) 35 { 36 global $RCMAIL, $CONTACTS; 37 37 38 38 // check if we have a valid result … … 40 40 && !(($result = $CONTACTS->get_result()) && ($record = $result->first())) 41 41 ) { 42 $ OUTPUT->show_message('contactnotfound');42 $RCMAIL->output->show_message('contactnotfound'); 43 43 return false; 44 44 } 45 46 $i_size = !empty($attrib['size']) ? $attrib['size'] : 20; 47 48 $form = array( 49 'head' => array( 50 'content' => array( 51 'prefix' => array('size' => $i_size), 52 'firstname' => array('size' => $i_size, 'visible' => true), 53 'middlename' => array('size' => $i_size), 54 'surname' => array('size' => $i_size, 'visible' => true), 55 'suffix' => array('size' => $i_size), 56 'name' => array('size' => 2*$i_size), 57 'nickname' => array('size' => 2*$i_size), 58 'company' => array('size' => $i_size), 59 'department' => array('size' => $i_size), 60 'jobtitle' => array('size' => $i_size), 61 ) 62 ) 63 ); 64 65 list($form_start, $form_end) = get_form_tags($attrib); 66 unset($attrib['form'], $attrib['name'], $attrib['size']); 67 68 // return the address edit form 69 $out = rcmail_contact_form($form, $record, $attrib); 70 71 return $form_start . $out . $form_end; 72 } 73 74 75 function rcmail_contact_editform($attrib) 76 { 77 global $RCMAIL, $CONTACTS, $CONTACT_COLTYPES; 78 79 // check if we have a valid result 80 if ($RCMAIL->action != 'add' 81 && !(($result = $CONTACTS->get_result()) && ($record = $result->first())) 82 ) { 83 $RCMAIL->output->show_message('contactnotfound'); 84 return false; 85 } 45 86 46 87 // add some labels to client 47 $ OUTPUT->add_label('noemailwarning', 'nonamewarning');88 $RCMAIL->output->add_label('noemailwarning', 'nonamewarning'); 48 89 49 90 $i_size = !empty($attrib['size']) ? $attrib['size'] : 40; 50 $t_rows = !empty($attrib['textarearows']) ? $attrib['textarearows'] : 6;91 $t_rows = !empty($attrib['textarearows']) ? $attrib['textarearows'] : 10; 51 92 $t_cols = !empty($attrib['textareacols']) ? $attrib['textareacols'] : 40; 52 93 … … 55 96 'name' => rcube_label('contactproperties'), 56 97 'content' => array( 57 'name' => array('type' => 'text', 'size' => $i_size), 58 'firstname' => array('type' => 'text', 'size' => $i_size), 59 'surname' => array('type' => 'text', 'size' => $i_size), 60 'email' => array('type' => 'text', 'size' => $i_size), 98 'gender' => array('visible' => false), 99 'maidenname' => array('size' => $i_size), 100 'email' => array('size' => $i_size, 'visible' => true), 101 'phone' => array('size' => $i_size, 'visible' => true), 102 'address' => array('visible' => true), 103 'birthday' => array('size' => 12), 104 'anniversary' => array('size' => $i_size), 105 'website' => array('size' => $i_size), 106 'im' => array('size' => $i_size), 107 'manager' => array('size' => $i_size), 108 'assistant' => array('size' => $i_size), 109 'spouse' => array('size' => $i_size), 61 110 ), 62 111 ), 63 112 ); 64 113 114 if (isset($CONTACT_COLTYPES['notes'])) { 115 $form['notes'] = array( 116 'name' => rcube_label('notes'), 117 'content' => array( 118 'notes' => array('size' => $t_cols, 'rows' => $t_rows, 'label' => false, 'visible' => true, 'limit' => 1), 119 ), 120 'single' => true, 121 ); 122 } 65 123 66 124 list($form_start, $form_end) = get_form_tags($attrib); … … 68 126 69 127 // return the complete address edit form as table 70 $out = rcmail_contact_form($form, $record );128 $out = rcmail_contact_form($form, $record, $attrib); 71 129 72 130 return $form_start . $out . $form_end; 131 } 132 133 134 function rcmail_upload_photo_form($attrib) 135 { 136 global $OUTPUT; 137 138 // add ID if not given 139 if (!$attrib['id']) 140 $attrib['id'] = 'rcmUploadbox'; 141 142 // find max filesize value 143 $max_filesize = parse_bytes(ini_get('upload_max_filesize')); 144 $max_postsize = parse_bytes(ini_get('post_max_size')); 145 if ($max_postsize && $max_postsize < $max_filesize) 146 $max_filesize = $max_postsize; 147 $max_filesize = show_bytes($max_filesize); 148 149 $hidden = new html_hiddenfield(array('name' => '_cid', 'value' => $GLOBALS['cid'])); 150 $input = new html_inputfield(array('type' => 'file', 'name' => '_photo', 'size' => $attrib['size'])); 151 $button = new html_inputfield(array('type' => 'button')); 152 153 $out = html::div($attrib, 154 $OUTPUT->form_tag(array('name' => 'uploadform', 'method' => 'post', 'enctype' => 'multipart/form-data'), 155 $hidden->show() . 156 html::div(null, $input->show()) . 157 html::div('hint', rcube_label(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize)))) . 158 html::div('buttons', 159 $button->show(rcube_label('close'), array('class' => 'button', 'onclick' => "$('#$attrib[id]').hide()")) . ' ' . 160 $button->show(rcube_label('upload'), array('class' => 'button mainaction', 'onclick' => JS_OBJECT_NAME . ".command('upload-photo', this.form)")) 161 ) 162 ) 163 ); 164 165 $OUTPUT->add_label('addphoto','replacephoto'); 166 $OUTPUT->add_gui_object('uploadbox', $attrib['id']); 167 return $out; 73 168 } 74 169 … … 104 199 105 200 201 $OUTPUT->add_handler('contactedithead', 'rcmail_contact_edithead'); 106 202 $OUTPUT->add_handler('contacteditform', 'rcmail_contact_editform'); 203 $OUTPUT->add_handler('contactphoto', 'rcmail_contact_photo'); 204 $OUTPUT->add_handler('photouploadform', 'rcmail_upload_photo_form'); 107 205 108 206 if (!$CONTACTS->get_result() && $OUTPUT->template_exists('contactadd')) -
program/steps/addressbook/export.inc
rf5e7b353 r0501b63 31 31 32 32 while ($result && ($row = $result->next())) { 33 $vcard = new rcube_vcard($row['vcard']); 34 $vcard->set('displayname', $row['name']); 35 $vcard->set('firstname', $row['firstname']); 36 $vcard->set('surname', $row['surname']); 37 $vcard->set('email', $row['email']); 38 39 echo $vcard->export(); 33 // we already have a vcard record 34 if ($row['vcard']) { 35 echo $row['vcard']; 36 } 37 // copy values into vcard object 38 else { 39 $vcard = new rcube_vcard($row['vcard']); 40 $vcard->reset(); 41 foreach ($row as $key => $values) { 42 list($field, $section) = explode(':', $key); 43 foreach ((array)$values as $value) { 44 if (is_array($value) || strlen($value)) 45 $vcard->set($field, $value, strtoupper($section)); 46 } 47 } 48 49 echo $vcard->export(); 50 } 40 51 } 41 52 -
program/steps/addressbook/func.inc
r68d2d54 r0501b63 57 57 58 58 59 // general definition of contact coltypes 60 $CONTACT_COLTYPES = array( 61 'name' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('name')), 62 'firstname' => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('firstname')), 63 'surname' => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('surname')), 64 'middlename' => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('middlename')), 65 'prefix' => array('type' => 'text', 'size' => 8, 'limit' => 1, 'label' => rcube_label('nameprefix')), 66 'suffix' => array('type' => 'text', 'size' => 8, 'limit' => 1, 'label' => rcube_label('namesuffix')), 67 'nickname' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('nickname')), 68 'jobtitle' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('jobtitle')), 69 'organization' => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('organization')), 70 'department' => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('department')), 71 'gender' => array('type' => 'select', 'limit' => 1, 'label' => rcube_label('gender'), 'options' => array('male' => rcube_label('male'), 'female' => rcube_label('female'))), 72 'maidenname' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('maidenname')), 73 'email' => array('type' => 'text', 'size' => 40, 'label' => rcube_label('email'), 'subtypes' => array('home','work','other')), 74 'phone' => array('type' => 'text', 'size' => 40, 'label' => rcube_label('phone'), 'subtypes' => array('home','home2','work','work2','mobile','main','homefax','workfax','car','pager','video','assistant','other')), 75 'address' => array('type' => 'composite', 'label' => rcube_label('address'), 'subtypes' => array('home','work','other'), 'childs' => array( 76 'street' => array('type' => 'text', 'size' => 40, 'label' => rcube_label('street')), 77 'locality' => array('type' => 'text', 'size' => 28, 'label' => rcube_label('locality')), 78 'zipcode' => array('type' => 'text', 'size' => 8, 'label' => rcube_label('zipcode')), 79 'region' => array('type' => 'text', 'size' => 12, 'label' => rcube_label('region')), 80 'country' => array('type' => 'text', 'size' => 40, 'label' => rcube_label('country')), 81 )), 82 'birthday' => array('type' => 'date', 'size' => 12, 'label' => rcube_label('birthday'), 'limit' => 1, 'render_func' => 'rcmail_format_date_col'), 83 'anniversary' => array('type' => 'date', 'size' => 12, 'label' => rcube_label('anniversary'), 'limit' => 1, 'render_func' => 'rcmail_format_date_col'), 84 'website' => array('type' => 'text', 'size' => 40, 'label' => rcube_label('website'), 'subtypes' => array('homepage','work','blog','other')), 85 'im' => array('type' => 'text', 'size' => 40, 'label' => rcube_label('instantmessenger'), 'subtypes' => array('aim','icq','msn','yahoo','jabber','skype','other')), 86 'notes' => array('type' => 'textarea', 'size' => 40, 'rows' => 15, 'label' => rcube_label('notes'), 'limit' => 1), 87 'photo' => array('type' => 'image', 'limit' => 1), 88 'assistant' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('assistant')), 89 'manager' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('manager')), 90 'spouse' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('spouse')), 91 // TODO: define fields for vcards like GEO, KEY 92 ); 93 94 // reduce/extend $CONTACT_COLTYPES with specification from the current $CONTACT object 95 if (is_array($CONTACTS->coltypes)) { 96 // remove cols not listed by the backend class 97 $contact_cols = $CONTACTS->coltypes[0] ? array_flip($CONTACTS->coltypes) : $CONTACTS->coltypes; 98 $CONTACT_COLTYPES = array_intersect_key($CONTACT_COLTYPES, $contact_cols); 99 // add associative coltypes definition 100 if (!$CONTACTS->coltypes[0]) { 101 foreach ($CONTACTS->coltypes as $col => $colprop) 102 $CONTACT_COLTYPES[$col] = $CONTACT_COLTYPES[$col] ? array_merge($CONTACT_COLTYPES[$col], $colprop) : $colprop; 103 } 104 } 105 106 $OUTPUT->set_env('photocol', is_array($CONTACT_COLTYPES['photo'])); 107 108 59 109 function rcmail_directory_list($attrib) 60 110 { … … 73 123 'onclick' => "return ".JS_OBJECT_NAME.".command('list','%s',this)"), '%s')); 74 124 75 if (!$current && strtolower($RCMAIL->config->get('address_book_type', 'sql')) != 'ldap') { 76 $current = '0'; 77 } 78 else if (!$current) { 79 // DB address book not used, see if a source is set, if not use the 80 // first LDAP directory. 81 $current = key((array)$RCMAIL->config->get('ldap_public', array())); 82 } 125 // currently selected is the first address source in the list 126 if (!isset($current)) 127 $current = strval(key((array)$OUTPUT->env['address_sources'])); 83 128 84 129 foreach ((array)$OUTPUT->env['address_sources'] as $j => $source) { 85 $id = $source['id'] ? $source['id'] : $j;130 $id = strval($source['id'] ? $source['id'] : $j); 86 131 $js_id = JQ($id); 87 $dom_id = preg_replace('/[^a-z0-9\-_]/i', ' ', $id);88 $out .= sprintf($line_templ, $dom_id, ($current == $id ? 'selected' : ''),132 $dom_id = preg_replace('/[^a-z0-9\-_]/i', '_', $id); 133 $out .= sprintf($line_templ, $dom_id, ($current === $id ? 'selected' : ''), 89 134 Q(rcmail_url(null, array('_source' => $id))), 90 135 $js_id, (!empty($source['name']) ? Q($source['name']) : Q($id))); 91 $groupdata = rcmail_contact_groups(array('out' => $out, 'jsdata' => $jsdata, 'source' => $id)); 136 137 $groupdata = array('out' => $out, 'jsdata' => $jsdata, 'source' => $id); 138 if ($source['groups']) 139 $groupdata = rcmail_contact_groups($groupdata); 92 140 $jsdata = $groupdata['jsdata']; 93 141 $out = $groupdata['out']; … … 131 179 global $CONTACTS, $OUTPUT; 132 180 181 // define list of cols to be displayed 182 $a_show_cols = array('name'); 183 133 184 // count contacts for this user 134 $result = $CONTACTS->list_records( );185 $result = $CONTACTS->list_records($a_show_cols); 135 186 136 187 // add id to message list table if not specified 137 188 if (!strlen($attrib['id'])) 138 189 $attrib['id'] = 'rcmAddressList'; 139 140 // define list of cols to be displayed141 $a_show_cols = array('name');142 190 143 191 // create XHTML table … … 234 282 235 283 236 function rcmail_contact_form($form, $record )237 { 238 global $RCMAIL ;284 function rcmail_contact_form($form, $record, $attrib = null) 285 { 286 global $RCMAIL, $CONFIG; 239 287 240 288 // Allow plugins to modify contact form content … … 244 292 $form = $plugin['form']; 245 293 $record = $plugin['record']; 294 $edit_mode = $RCMAIL->action != 'show'; 295 $del_button = $attrib['deleteicon'] ? html::img(array('src' => $CONFIG['skin_path'] . $attrib['deleteicon'], 'alt' => rcube_label('delete'))) : rcube_label('delete'); 296 unset($attrib['deleteicon']); 246 297 $out = ''; 247 248 foreach ($form as $fieldset) { 298 299 // get default coltypes 300 $coltypes = $GLOBALS['CONTACT_COLTYPES']; 301 $coltype_lables = array(); 302 303 foreach ($coltypes as $col => $prop) { 304 if ($prop['subtypes']) { 305 $select_subtype = new html_select(array('name' => '_subtype_'.$col.'[]', 'class' => 'contactselectsubtype')); 306 $select_subtype->add($prop['subtypes']); 307 $coltypes[$col]['subtypes_select'] = $select_subtype->show(); 308 } 309 if ($prop['childs']) { 310 foreach ($prop['childs'] as $childcol => $cp) 311 $coltype_lables[$childcol] = array('label' => $cp['label']); 312 } 313 } 314 315 foreach ($form as $section => $fieldset) { 316 // skip empty sections 249 317 if (empty($fieldset['content'])) 250 318 continue; 251 319 320 $select_add = new html_select(array('class' => 'addfieldmenu', 'rel' => $section)); 321 $select_add->add(rcube_label('addfield'), ''); 322 323 // render head section with name fields (not a regular list of rows) 324 if ($section == 'head') { 325 $content = ''; 326 327 $names_arr = array($record['prefix'], $record['firstname'], $record['middlename'], $record['surname'], $record['suffix']); 328 if ($record['name'] == join(' ', array_filter($names_arr))) 329 unset($record['name']); 330 331 // group fields 332 $field_blocks = array( 333 'names' => array('prefix','firstname','middlename','surname','suffix'), 334 'displayname' => array('name'), 335 'nickname' => array('nickname'), 336 'jobnames' => array('organization','department','jobtitle'), 337 ); 338 foreach ($field_blocks as $blockname => $colnames) { 339 $fields = ''; 340 foreach ($colnames as $col) { 341 // skip cols unknown to the backend 342 if (!$coltypes[$col]) 343 continue; 344 345 if ($RCMAIL->action == 'show') { 346 if (!empty($record[$col])) 347 $fields .= html::span('namefield ' . $col, Q($record[$col])) . " "; 348 } 349 else { 350 $colprop = (array)$fieldset['content'][$col] + (array)$coltypes[$col]; 351 $colprop['id'] = 'ff_'.$col; 352 if (empty($record[$col]) && !$colprop['visible']) { 353 $colprop['style'] = 'display:none'; 354 $select_add->add($colprop['label'], $col); 355 } 356 $fields .= rcmail_get_edit_field($col, $record[$col], $colprop, $colprop['type']); 357 } 358 } 359 $content .= html::div($blockname, $fields); 360 } 361 362 if ($edit_mode) 363 $content .= html::p('addfield', $select_add->show(null)); 364 365 $out .= html::tag('fieldset', $attrib, (!empty($fieldset['name']) ? html::tag('legend', null, Q($fieldset['name'])) : '') . $content) ."\n"; 366 continue; 367 } 368 252 369 $content = ''; 253 370 if (is_array($fieldset['content'])) { 254 $table = new html_table(array('cols' => 2));255 256 371 foreach ($fieldset['content'] as $col => $colprop) { 257 $colprop['id'] = 'rcmfd_'.$col; 258 259 $label = !empty($colprop['label']) ? $colprop['label'] : rcube_label($col); 372 // remove subtype part of col name 373 list($field, $subtype) = explode(':', $col); 374 if (!$subtype) $subtype = 'home'; 375 $fullkey = $col.':'.$subtype; 376 377 // skip cols unknown to the backend 378 if (!$coltypes[$field]) 379 continue; 380 381 // merge colprop with global coltype configuration 382 $colprop += $coltypes[$field]; 383 $label = isset($colprop['label']) ? $colprop['label'] : rcube_label($col); 384 385 // prepare subtype selector in edit mode 386 if ($edit_mode && is_array($colprop['subtypes'])) { 387 $select_subtype = new html_select(array('name' => '_subtype_'.$col.'[]', 'class' => 'contactselectsubtype')); 388 $select_subtype->add($colprop['subtypes']); 389 } 390 else 391 $select_subtype = null; 260 392 261 393 if (!empty($colprop['value'])) { 262 $value = $colprop['value']; 263 } 264 else if ($RCMAIL->action == 'show') { 265 $value = $record[$col]; 394 $values = (array)$colprop['value']; 266 395 } 267 396 else { 268 $value = rcmail_get_edit_field($col, $record[$col], $colprop, $colprop['type']); 397 // iterate over possible subtypes and collect values with their subtype 398 if (is_array($colprop['subtypes'])) { 399 $values = $subtypes = array(); 400 foreach ($colprop['subtypes'] as $i => $st) { 401 $newval = false; 402 if ($record[$field.':'.$st]) { 403 $subtypes[count($values)] = $st; 404 $newval = $record[$field.':'.$st]; 405 } 406 else if ($i == 0 && $record[$field]) { 407 $subtypes[count($values)] = $st; 408 $newval = $record[$field]; 409 } 410 if ($newval !== false) { 411 if (is_array($newval) && isset($newval[0])) 412 $values = array_merge($values, $newval); 413 else 414 $values[] = $newval; 415 } 416 } 417 } 418 else { 419 $values = $record[$fullkey] ? $record[$fullkey] : $record[$field]; 420 $subtypes = null; 421 } 269 422 } 270 423 271 $table->add('title', sprintf('<label for="%s">%s</label>', $colprop['id'], Q($label))); 272 $table->add(null, $value); 424 // hack: create empty values array to force this field to be displayed 425 if (empty($values) && $colprop['visible']) 426 $values[] = ''; 427 428 $rows = ''; 429 foreach ((array)$values as $i => $val) { 430 if ($subtypes[$i]) 431 $subtype = $subtypes[$i]; 432 433 // render composite field 434 if ($colprop['type'] == 'composite') { 435 $composite = array(); $j = 0; 436 $template = $RCMAIL->config->get($col . '_template', '{'.join('} {', array_keys($colprop['childs'])).'}'); 437 foreach ($colprop['childs'] as $childcol => $cp) { 438 $childvalue = $val[$childcol] ? $val[$childcol] : $val[$j]; 439 440 if ($edit_mode) { 441 if ($colprop['subtypes'] || $colprop['limit'] != 1) $cp['array'] = true; 442 $composite['{'.$childcol.'}'] = rcmail_get_edit_field($childcol, $childvalue, $cp, $cp['type']) . " "; 443 } 444 else { 445 $childval = $cp['render_func'] ? call_user_func($cp['render_func'], $childvalue, $childcol) : Q($childvalue); 446 $composite['{'.$childcol.'}'] = html::span('data ' . $childcol, $childval) . " "; 447 } 448 $j++; 449 } 450 451 $coltypes[$field] += (array)$colprop; 452 $coltypes[$field]['count']++; 453 $val = strtr($template, $composite); 454 } 455 else if ($edit_mode) { 456 // call callback to render/format value 457 if ($colprop['render_func']) 458 $val = call_user_func($colprop['render_func'], $val, $col); 459 460 $coltypes[$field] = (array)$colprop + $coltypes[$field]; 461 462 if ($colprop['subtypes'] || $colprop['limit'] != 1) 463 $colprop['array'] = true; 464 465 $val = rcmail_get_edit_field($col, $val, $colprop, $colprop['type']); 466 $coltypes[$field]['count']++; 467 } 468 else if ($colprop['render_func']) 469 $val = call_user_func($colprop['render_func'], $val, $col); 470 else if (is_array($colprop['options']) && isset($colprop['options'][$val])) 471 $val = $colprop['options'][$val]; 472 else 473 $val = Q($val); 474 475 // use subtype as label 476 if ($colprop['subtypes']) 477 $label = $subtype; 478 479 // add delete button/link 480 if ($edit_mode && !($colprop['visible'] && $colprop['limit'] == 1)) 481 $val .= html::a(array('href' => '#del', 'class' => 'contactfieldbutton deletebutton', 'title' => rcube_label('delete'), 'rel' => $col), $del_button); 482 483 // display row with label 484 if ($label) { 485 $rows .= html::div('row', 486 html::div('contactfieldlabel label', $select_subtype ? $select_subtype->show($subtype) : Q($label)) . 487 html::div('contactfieldcontent '.$colprop['type'], $val)); 488 } 489 else // row without label 490 $rows .= html::div('row', html::div('contactfield', $val)); 491 } 492 493 // add option to the add-field menu 494 if (!$colprop['limit'] || $coltypes[$field]['count'] < $colprop['limit']) { 495 $select_add->add($colprop['label'], $col); 496 $select_add->_count++; 497 } 498 499 // wrap rows in fieldgroup container 500 $content .= html::tag('fieldset', array('class' => 'contactfieldgroup contactcontroller' . $col, 'style' => ($rows ? null : 'display:none')), 501 ($colprop['subtypes'] ? html::tag('legend', null, Q($colprop['label'])) : ' ') . 502 $rows); 273 503 } 274 $content = $table->show(); 504 505 // also render add-field selector 506 if ($edit_mode) 507 $content .= html::p('addfield', $select_add->show(null, array('style' => $select_add->_count ? null : 'display:none'))); 508 509 $content = html::div(array('id' => 'contactsection' . $section), $content); 275 510 } 276 511 else { … … 280 515 $out .= html::tag('fieldset', null, html::tag('legend', null, Q($fieldset['name'])) . $content) ."\n"; 281 516 } 517 518 if ($edit_mode) { 519 $RCMAIL->output->set_env('coltypes', $coltypes + $coltype_lables); 520 $RCMAIL->output->set_env('delbutton', $del_button); 521 $RCMAIL->output->add_label('delete'); 522 } 523 524 return $out; 525 } 526 527 528 function rcmail_contact_photo($attrib) 529 { 530 global $CONTACTS, $CONTACT_COLTYPES, $RCMAIL, $CONFIG; 531 532 if ($result = $CONTACTS->get_result()) 533 $record = $result->first(); 534 535 $photo_img = $attrib['placeholder'] ? $CONFIG['skin_path'] . $attrib['placeholder'] : 'program/blank.gif'; 536 unset($attrib['placeholder']); 537 538 if ($CONTACT_COLTYPES['photo']) { 539 $RCMAIL->output->set_env('photo_placeholder', $photo_img); 540 541 if ($record['photo']) 542 $photo_img = $RCMAIL->url(array('_action' => 'photo', '_cid' => $record['ID'], '_source' => $_REQUEST['_source'])); 543 $img = html::img(array('src' => $photo_img, 'border' => 1, 'alt' => '')); 544 $content = html::div($attrib, $img); 545 546 if ($RCMAIL->action == 'edit' || $RCMAIL->action == 'add') { 547 $RCMAIL->output->add_gui_object('contactphoto', $attrib['id']); 548 $hidden = new html_hiddenfield(array('name' => '_photo', 'id' => 'ff_photo')); 549 $content .= $hidden->show(); 550 } 551 } 282 552 283 return $out; 553 return $content; 554 } 555 556 557 function rcmail_format_date_col($val) 558 { 559 global $RCMAIL; 560 return format_date($val, $RCMAIL->config->get('date_format', 'Y-m-d')); 284 561 } 285 562 … … 298 575 $RCMAIL->register_action_map(array( 299 576 'add' => 'edit.inc', 577 'photo' => 'show.inc', 578 'upload-photo' => 'save.inc', 300 579 'group-create' => 'groups.inc', 301 580 'group-rename' => 'groups.inc', -
program/steps/addressbook/groups.inc
rf5e7b353 r0501b63 80 80 if ($created && $OUTPUT->ajax_call) { 81 81 $OUTPUT->show_message('groupcreated', 'confirmation'); 82 $OUTPUT->command('insert_contact_group', array( 83 'source' => $source, 'id' => $created['id'], 'name' => $created['name'])); 82 $OUTPUT->command('insert_contact_group', array('source' => $source) + $created); 84 83 } 85 84 else if (!$created) { -
program/steps/addressbook/import.inc
rf5e7b353 r0501b63 151 151 } 152 152 153 $a_record = array( 154 'name' => $vcard->displayname, 155 'firstname' => $vcard->firstname, 156 'surname' => $vcard->surname, 157 'email' => $email, 158 'vcard' => $vcard->export(), 159 ); 153 $a_record = $vcard->get_assoc(); 154 $a_record['vcard'] = $vcard->export(); 160 155 161 156 $plugin = $RCMAIL->plugins->exec_hook('contact_create', array('record' => $a_record, 'source' => null)); -
program/steps/addressbook/list.inc
rf5e7b353 r0501b63 21 21 22 22 // get contacts for this user 23 $result = $CONTACTS->list_records( );23 $result = $CONTACTS->list_records(array('name')); 24 24 25 25 // update message count display -
program/steps/addressbook/mailto.inc
rf5e7b353 r0501b63 30 30 $recipients = $CONTACTS->search($CONTACTS->primary_key, $cid); 31 31 32 while (is_object($recipients) && ($rec = $recipients->iterate())) 33 $mailto[] = format_email_recipient($rec['email'], $rec['name']); 32 while (is_object($recipients) && ($rec = $recipients->iterate())) { 33 $emails = $CONTACTS->get_col_values('email', $rec, true); 34 $mailto[] = format_email_recipient($emails[0], $rec['name']); 35 } 34 36 } 35 37 -
program/steps/addressbook/save.inc
rf5e7b353 r0501b63 6 6 | | 7 7 | This file is part of the Roundcube Webmail client | 8 | Copyright (C) 2005-20 09, The Roundcube Dev Team |8 | Copyright (C) 2005-2011, The Roundcube Dev Team | 9 9 | Licensed under the GNU GPL | 10 10 | | … … 30 30 } 31 31 32 // Basic input checks 33 if ((!get_input_value('_name', RCUBE_INPUT_POST) || !get_input_value('_email', RCUBE_INPUT_POST))) { 32 33 // handle photo upload for contacts 34 if ($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 // check file type and resize image 40 $imageprop = rcmail::imageprops($_FILES['_photo']['tmp_name']); 41 42 if ($imageprop['width'] && $imageprop['height']) { 43 $maxsize = intval($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' => '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 98 // read POST values into hash array 99 $a_record = array(); 100 foreach ($GLOBALS['CONTACT_COLTYPES'] as $col => $colprop) { 101 $fname = '_'.$col; 102 if ($colprop['composite']) 103 continue; 104 // gather form data of composite fields 105 if ($colprop['childs']) { 106 $values = array(); 107 foreach ($colprop['childs'] as $childcol => $cp) { 108 $vals = get_input_value('_'.$childcol, RCUBE_INPUT_POST); 109 foreach ((array)$vals as $i => $val) 110 $values[$i][$childcol] = $val; 111 } 112 $subtypes = get_input_value('_subtype_' . $col, RCUBE_INPUT_POST); 113 foreach ($subtypes as $i => $subtype) 114 if ($values[$i]) 115 $a_record[$col.':'.$subtype][] = $values[$i]; 116 } 117 // assign values and subtypes 118 else if (is_array($_POST[$fname])) { 119 $values = get_input_value($fname, RCUBE_INPUT_POST); 120 $subtypes = get_input_value('_subtype_' . $col, RCUBE_INPUT_POST); 121 foreach ($values as $i => $val) { 122 $subtype = $subtypes[$i] ? ':'.$subtypes[$i] : ''; 123 $a_record[$col.$subtype][] = $val; 124 } 125 } 126 else if (isset($_POST[$fname])) { 127 $a_record[$col] = get_input_value($fname, RCUBE_INPUT_POST); 128 } 129 } 130 131 if (empty($a_record['name'])) 132 $a_record['name'] = join(' ', array_filter(array($a_record['prefix'], $a_record['firstname'], $a_record['middlename'], $a_record['surname'], $a_record['suffix'],))); 133 134 #var_dump($a_record); 135 136 // Basic input checks (TODO: delegate to $CONTACTS instance) 137 if (empty($a_record['name'])/* || empty($a_record['email'])*/) { 34 138 $OUTPUT->show_message('formincomplete', 'warning'); 35 139 rcmail_overwrite_action($return_action); … … 37 141 } 38 142 39 40 // setup some vars we need41 $a_save_cols = array('name', 'firstname', 'surname', 'email');42 $a_record = array();43 44 // read POST values into hash array45 foreach ($a_save_cols as $col) {46 $fname = '_'.$col;47 if (isset($_POST[$fname]))48 $a_record[$col] = get_input_value($fname, RCUBE_INPUT_POST);49 }50 51 143 // Validity checks 52 $_email = idn_to_ascii($a_record['email']); 53 if (!check_email($_email, false)) { 54 $OUTPUT->show_message('emailformaterror', 'warning', array('email' => $_email)); 55 rcmail_overwrite_action($return_action); 56 return; 144 foreach ($CONTACTS->get_col_values('email', $a_record, true) as $email) { 145 if (strlen($email)) { 146 $_email = idn_to_ascii($email); 147 if (!check_email($_email, false)) { 148 $OUTPUT->show_message('emailformaterror', 'warning', array('email' => $email)); 149 rcmail_overwrite_action($return_action); 150 return; 151 } 152 } 153 } 154 155 // get raw photo data if changed 156 if (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'); 57 171 } 58 172 … … 93 207 else { 94 208 // show error message 95 $OUTPUT->show_message($plugin['message'] ? $plugin['message'] : 'errorsaving', 'error', null, false); 209 $err = $CONTACTS->get_error(); 210 $OUTPUT->show_message($plugin['message'] ? $plugin['message'] : ($err['message'] ? $err['message'] : 'errorsaving'), 'error', null, false); 96 211 rcmail_overwrite_action('show'); 97 212 } … … 101 216 else { 102 217 // check for existing contacts 103 $existing = $CONTACTS->search('email', $a_record['email'], true, false); 218 $existing = false; 219 foreach ($CONTACTS->get_col_values('email', $a_record, true) as $email) { 220 if (($res = $CONTACTS->search('email', $email, true, false)) && $res->count) { 221 $existing = true; 222 break; 223 } 224 } 104 225 105 226 // show warning message 106 if ($existing ->count) {227 if ($existing) { 107 228 $OUTPUT->show_message('contactexists', 'warning', null, false); 108 229 rcmail_overwrite_action('add'); … … 139 260 else { 140 261 // show error message 141 $OUTPUT->show_message($plugin['message'] ? $plugin['message'] : 'errorsaving', 'error', null, false); 262 $err = $CONTACTS->get_error(); 263 $OUTPUT->show_message($plugin['message'] ? $plugin['message'] : ($err['message'] ? $err['message'] : 'errorsaving'), 'error', null, false); 142 264 rcmail_overwrite_action('add'); 143 265 } -
program/steps/addressbook/search.inc
rf5e7b353 r0501b63 6 6 | | 7 7 | This file is part of the Roundcube Webmail client | 8 | Copyright (C) 2005-20 07, The Roundcube Dev Team |8 | Copyright (C) 2005-2011, The Roundcube Dev Team | 9 9 | Licensed under the GNU GPL | 10 10 | | … … 29 29 $result = $CONTACTS->search(array('name','email'), $search); 30 30 31 // save search settings in session 32 $_SESSION['search'][$search_request] = $CONTACTS->get_search_set(); 33 31 34 if ($result->count > 0) 32 35 { 33 // save search settings in session34 $_SESSION['search'][$search_request] = $CONTACTS->get_search_set();35 36 36 // create javascript list 37 37 rcmail_js_contacts_list($result); -
program/steps/addressbook/show.inc
rf5e7b353 r0501b63 26 26 } 27 27 28 // return raw photo of the given contact 29 if ($RCMAIL->action == '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']) { 40 $data = is_array($record['photo']) ? $record['photo'][0] : $record['photo']; 41 if (!preg_match('![^a-z0-9/=+-]!i', $data)) 42 $data = base64_decode($data, true); 43 } 44 45 header('Content-Type: ' . rc_image_content_type($data)); 46 echo $data ? $data : file_get_contents('program/blank.gif'); 47 exit; 48 } 28 49 29 function rcmail_contact_details($attrib) 50 51 function rcmail_contact_head($attrib) 30 52 { 31 53 global $CONTACTS, $RCMAIL; … … 37 59 } 38 60 61 $microformats = array('name' => 'fn', 'email' => 'email'); 62 63 $form = array( 64 'head' => array( // section 'head' is magic! 65 'content' => array( 66 'prefix' => array('type' => 'text'), 67 'firstname' => array('type' => 'text'), 68 'middlename' => array('type' => 'text'), 69 'surname' => array('type' => 'text'), 70 'suffix' => array('type' => 'text'), 71 ), 72 ), 73 ); 74 75 unset($attrib['name']); 76 return rcmail_contact_form($form, $record, $attrib); 77 } 78 79 80 function rcmail_contact_details($attrib) 81 { 82 global $CONTACTS, $RCMAIL, $CONTACT_COLTYPES; 83 84 // check if we have a valid result 85 if (!(($result = $CONTACTS->get_result()) && ($record = $result->first()))) { 86 //$RCMAIL->output->show_message('contactnotfound'); 87 return false; 88 } 89 39 90 $i_size = !empty($attrib['size']) ? $attrib['size'] : 40; 40 $t_rows = !empty($attrib['textarearows']) ? $attrib['textarearows'] : 6;41 $t_cols = !empty($attrib['textareacols']) ? $attrib['textareacols'] : 40;42 43 $microformats = array('name' => 'fn', 'email' => 'email');44 91 45 92 $form = array( … … 47 94 'name' => rcube_label('contactproperties'), 48 95 'content' => array( 49 'name' => array('type' => 'text', 'size' => $i_size), 50 'firstname' => array('type' => 'text', 'size' => $i_size), 51 'surname' => array('type' => 'text', 'size' => $i_size), 52 'email' => array('type' => 'text', 'size' => $i_size), 96 'gender' => array('size' => $i_size), 97 'maidenname' => array('size' => $i_size), 98 'email' => array('size' => $i_size, 'render_func' => 'rcmail_render_email_value'), 99 'phone' => array('size' => $i_size), 100 'address' => array(), 101 'birthday' => array('size' => $i_size), 102 'anniversary' => array('size' => $i_size), 103 'website' => array('size' => $i_size, 'render_func' => 'rcmail_render_url_value'), 104 'im' => array('size' => $i_size), 105 'manager' => array('size' => $i_size), 106 'assistant' => array('size' => $i_size), 107 'spouse' => array('size' => $i_size), 53 108 ), 54 109 ), 55 'groups' => array( 110 ); 111 112 if (isset($CONTACT_COLTYPES['notes'])) { 113 $form['notes'] = array( 114 'name' => rcube_label('notes'), 115 'content' => array( 116 'notes' => array('type' => 'textarea', 'label' => false), 117 ), 118 ); 119 } 120 121 if ($CONTACTS->groups) { 122 $form['groups'] = array( 56 123 'name' => rcube_label('groups'), 57 'content' => '', 58 ), 59 ); 60 61 // Get content of groups fieldset 62 if ($groups = rcmail_contact_record_groups($record['ID'])) { 63 $form['groups']['content'] = $groups; 64 } 65 else { 66 unset($form['groups']); 67 } 68 69 if (!empty($record['email'])) { 70 $form['info']['content']['email']['value'] = html::a(array( 71 'href' => 'mailto:' . $record['email'], 72 'onclick' => sprintf("return %s.command('compose','%s',this)", JS_OBJECT_NAME, JQ($record['email'])), 73 'title' => rcube_label('composeto'), 74 'class' => $microformats['email'], 75 ), Q($record['email'])); 76 } 77 foreach (array('name', 'firstname', 'surname') as $col) { 78 if ($record[$col]) { 79 $form['info']['content'][$col]['value'] = html::span($microformats[$col], Q($record[$col])); 80 } 124 'content' => rcmail_contact_record_groups($record['ID']), 125 ); 81 126 } 82 127 83 128 return rcmail_contact_form($form, $record); 129 } 130 131 132 function rcmail_render_email_value($email, $col) 133 { 134 return html::a(array( 135 'href' => 'mailto:' . $email, 136 'onclick' => sprintf("return %s.command('compose','%s',this)", JS_OBJECT_NAME, JQ($email)), 137 'title' => rcube_label('composeto'), 138 'class' => 'email', 139 ), Q($email)); 140 } 141 142 143 function rcmail_render_url_value($url, $col) 144 { 145 $prefix = preg_match('![htfps]+://!', $url) ? '' : 'http://'; 146 return html::a(array( 147 'href' => $prefix . $url, 148 'target' => '_blank', 149 'class' => 'url', 150 ), Q($url)); 84 151 } 85 152 … … 125 192 126 193 //$OUTPUT->framed = $_framed; 194 $OUTPUT->add_handler('contacthead', 'rcmail_contact_head'); 127 195 $OUTPUT->add_handler('contactdetails', 'rcmail_contact_details'); 196 $OUTPUT->add_handler('contactphoto', 'rcmail_contact_photo'); 128 197 129 198 $OUTPUT->send('contact'); -
program/steps/mail/autocomplete.inc
re019f2d r0501b63 30 30 $abook->set_pagesize(1000); // TODO: limit number of group members by config 31 31 $result = $abook->list_records(array('email','name')); 32 while ($result && ($sql_arr = $result->iterate())) 33 $members[] = format_email_recipient($sql_arr['email'], $sql_arr['name']); 32 while ($result && ($sql_arr = $result->iterate())) { 33 foreach ((array)$sql_arr['email'] as $email) 34 $members[] = format_email_recipient($email, $sql_arr['name']); 35 } 34 36 35 37 $OUTPUT->command('replace_group_recipients', $gid, join(', ', $members)); … … 46 48 if ($result = $abook->search(array('email','name'), $search, false, true, true, 'email')) { 47 49 while ($sql_arr = $result->iterate()) { 48 $contact = format_email_recipient($sql_arr['email'], $sql_arr['name']); 49 // when we've got more than one book, we need to skip duplicates 50 if ($books_num == 1 || !in_array($contact, $contacts)) { 51 $contacts[] = $contact; 52 if (count($contacts) >= $MAXNUM) 53 break 2; 50 foreach ((array)$abook->get_col_values('email', $sql_arr, true) as $email) { 51 $contact = format_email_recipient($email, $sql_arr['name']); 52 // when we've got more than one book, we need to skip duplicates 53 if ($books_num == 1 || !in_array($contact, $contacts)) { 54 $contacts[] = $contact; 55 if (count($contacts) >= $MAXNUM) 56 break 2; 57 } 54 58 } 55 59 } -
program/steps/mail/compose.inc
rf5e7b353 r0501b63 1134 1134 $out = html::div($attrib, 1135 1135 $OUTPUT->form_tag(array('name' => 'uploadform', 'method' => 'post', 'enctype' => 'multipart/form-data'), 1136 html::div(null, rcmail_compose_attachment_field(array('size' => $attrib[ attachmentfieldsize]))) .1136 html::div(null, rcmail_compose_attachment_field(array('size' => $attrib['attachmentfieldsize']))) . 1137 1137 html::div('hint', rcube_label(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize)))) . 1138 1138 html::div('buttons', -
skins/default/addressbook.css
re019f2d r0501b63 215 215 } 216 216 217 #contacttabs 218 { 219 position: relative; 220 padding-bottom: 22px; 221 } 222 223 #contacttabs div.tabsbar { 224 top: 0; 225 left: 2px; 226 } 227 228 #contacttabs fieldset.tabbed { 229 position: relative; 230 top: 22px; 231 min-height: 5em; 232 } 233 234 #contacthead 235 { 236 margin-bottom: 1em; 237 border: 0; 238 padding: 0; 239 } 240 241 #contacthead .names span.namefield, 242 #contacthead .names input 243 { 244 font-size: 140%; 245 } 246 247 #contacthead .displayname span.namefield 248 { 249 font-size: 120%; 250 } 251 252 #contacthead span.nickname:before, 253 #contacthead span.nickname:after, 254 #contacthead input.ff_nickname:before, 255 #contacthead input.ff_nickname:after 256 { 257 content: '"'; 258 } 259 260 #contacthead input 261 { 262 margin-right: 6px; 263 margin-bottom: 0.2em; 264 } 265 266 #contacthead .names input, 267 #contacthead .addnames input, 268 #contacthead .jobnames input 269 { 270 width: 180px; 271 } 272 273 #contacthead input.ff_prefix, 274 #contacthead input.ff_suffix 275 { 276 width: 90px; 277 } 278 279 #contacthead .addnames input.ff_name 280 { 281 width: 374px; 282 } 283 284 #contactphoto 285 { 286 float: right; 287 width: 60px; 288 margin-left: 3em; 289 margin-right: 4px; 290 } 291 292 #contactpic 293 { 294 width: 60px; 295 min-height: 60px; 296 border: 1px solid #ccc; 297 background: white; 298 } 299 300 #contactpic img { 301 width: 60px; 302 } 303 304 #contactphoto .formlinks 305 { 306 margin-top: 0.5em; 307 text-align: center; 308 } 309 310 fieldset.contactfieldgroup 311 { 312 border: 0; 313 margin: 0.5em 0; 314 padding: 0.5em 2px; 315 } 316 317 fieldset.contactfieldgroup legend 318 { 319 font-size: 0.9em; 320 } 321 322 .contactfieldgroup .row 323 { 324 position: relative; 325 margin-bottom: 0.4em; 326 } 327 328 .contactfieldgroup .contactfieldlabel 329 { 330 position: absolute; 331 top: 0; 332 left: 2px; 333 width: 90px; 334 white-space: nowrap; 335 overflow: hidden; 336 text-overflow: ellipsis; 337 color: #666; 338 font-weight: bold; 339 } 340 341 .contactfieldgroup .contactfieldlabel select 342 { 343 width: 78px; 344 background: none; 345 border: 0; 346 color: #666; 347 font-weight: bold; 348 padding-left: 0; 349 } 350 351 .contactfieldgroup .contactfieldcontent 352 { 353 padding-left: 100px; 354 min-height: 1em; 355 line-height: 1.3em; 356 } 357 358 .contactfieldgroup .contactfield { 359 line-height: 1.3em; 360 } 361 362 .contactcontrolleraddress .contactfieldcontent input { 363 margin-bottom: 0.1em; 364 } 365 366 .contactfieldcontent .contactfieldbutton { 367 vertical-align: middle; 368 margin-left: 0.5em; 369 } 370 371 #upload-form 372 { 373 padding: 6px; 374 } 375 376 #upload-form div 377 { 378 padding: 2px; 379 } -
skins/default/common.css
ra3861ba r0501b63 75 75 color: black; 76 76 padding: 1px 3px; 77 } 78 79 input.placeholder, 80 textarea.placeholder 81 { 82 color: #aaa; 77 83 } 78 84 … … 113 119 color: #666; 114 120 font-size: 11px; 121 } 122 123 .formlinks a, 124 .formlinks a:visited 125 { 126 color: #CC0000; 127 font-size: 11px; 128 text-decoration: none; 129 } 130 131 .formlinks a.disabled, 132 .formlinks a.disabled:visited 133 { 134 color: #999999; 115 135 } 116 136 -
skins/default/functions.js
r5206569 r0501b63 26 26 function rcube_init_tabs(id, current) 27 27 { 28 var content = document.getElementById(id), 29 // get fieldsets of the higher-level (skip nested fieldsets) 30 fs = $('fieldset', content).not('fieldset > fieldset'); 28 var content = $('#'+id), 29 fs = content.children('fieldset'); 31 30 32 31 if (!fs.length) … … 43 42 // convert fildsets into tabs 44 43 fs.each(function(idx) { 45 var tab, a, elm = $(this), 46 // get first legend element 47 legend = $(elm).children('legend'); 44 var tab, a, elm = $(this), legend = elm.children('legend'); 48 45 49 46 // create a tab … … 67 64 function rcube_show_tab(id, index) 68 65 { 69 var content = document.getElementById(id), 70 fs = $('fieldset', content).not('fieldset > fieldset'); 66 var fs = $('#'+id).children('fieldset'); 71 67 72 68 fs.each(function(idx) { … … 95 91 composemenu: {id:'composeoptionsmenu', editable:1}, 96 92 // toggle: #1486823, #1486930 97 uploadmenu: {id:'attachment-form', editable:1, above:1, toggle:!bw.ie&&!bw.linux } 93 uploadmenu: {id:'attachment-form', editable:1, above:1, toggle:!bw.ie&&!bw.linux }, 94 uploadform: {id:'upload-form', editable:1, toggle:!bw.ie&&!bw.linux } 98 95 }; 99 96 … … 136 133 if (!above && pos.top + ref.offsetHeight + obj.height() > window.innerHeight) 137 134 above = true; 135 136 if (pos.left + obj.width() > window.innerWidth) 137 pos.left = window.innerWidth - obj.width() - 30; 138 138 139 139 obj.css({ left:pos.left, top:(pos.top + (above ? -obj.height() : ref.offsetHeight)) }); … … 501 501 rcmail_ui.init_compose_form(); 502 502 } 503 else if (rcmail.env.task == 'addressbook') { 504 rcmail.addEventListener('afterupload-photo', function(){ rcmail_ui.show_popup('uploadform', false); }); 505 } 503 506 } 504 507 -
skins/default/iehacks.css
raf3c045e r0501b63 237 237 margin-top: 2px; 238 238 } 239 240 .contactfieldgroup legend 241 { 242 padding: 0 0 0.5em 0; 243 margin-left: -4px; 244 } -
skins/default/mail.css
r3ee5a72 r0501b63 1343 1343 } 1344 1344 1345 .formlinks a,1346 .formlinks a:visited1347 {1348 color: #999999;1349 font-size: 11px;1350 text-decoration: none;1351 }1352 1353 .formlinks a,1354 .formlinks a:visited1355 {1356 color: #CC0000;1357 }1358 1359 1345 #compose-editorfooter 1360 1346 { -
skins/default/templates/contact.html
r83ba22c r0501b63 10 10 <div id="contact-title" class="boxtitle"><roundcube:label name="contactproperties" /></div> 11 11 <div id="contact-details" class="boxcontent"> 12 <roundcube:object name="contactdetails" /> 12 <div id="contactphoto"><roundcube:object name="contactphoto" id="contactpic" placeholder="/images/contactpic.png" /></div> 13 <roundcube:object name="contacthead" id="contacthead" /> 14 <div style="clear:both"></div> 15 <div id="contacttabs"> 16 <roundcube:object name="contactdetails" /> 17 </div> 13 18 <p> 14 19 <roundcube:button command="edit" type="input" class="button" label="editcontact" condition="!ENV:readonly" /> 15 20 </p> 16 21 </div> 17 <script type="text/javascript">rcube_init_tabs('contact -details')</script>22 <script type="text/javascript">rcube_init_tabs('contacttabs')</script> 18 23 19 24 </body> -
skins/default/templates/contactadd.html
r83ba22c r0501b63 6 6 <script type="text/javascript" src="/functions.js"></script> 7 7 </head> 8 <body class="iframe" >8 <body class="iframe" onload="rcube_init_mail_ui()"> 9 9 10 10 <div id="contact-title" class="boxtitle"><roundcube:label name="addcontact" /></div> 11 11 <div id="contact-details" class="boxcontent"> 12 <roundcube:object name="contacteditform" size="40" /> 12 <form name="editform" method="post" action="./"> 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 23 <div id="contacttabs"> 24 <roundcube:object name="contacteditform" size="40" textareacols="60" deleteIcon="/images/icons/delete.png" form="editform" /> 25 </div> 13 26 <p> 14 27 <input type="button" value="<roundcube:label name="cancel" />" class="button" onclick="history.back()" /> … … 17 30 </form> 18 31 </div> 19 <script type="text/javascript">rcube_init_tabs('contact-details')</script> 32 33 <roundcube:object name="photoUploadForm" id="upload-form" size="30" class="popupmenu" /> 34 35 <script type="text/javascript">rcube_init_tabs('contacttabs')</script> 20 36 21 37 </body> -
skins/default/templates/contactedit.html
r83ba22c r0501b63 6 6 <script type="text/javascript" src="/functions.js"></script> 7 7 </head> 8 <body class="iframe" >8 <body class="iframe" onload="rcube_init_mail_ui()"> 9 9 10 10 <div id="contact-title" class="boxtitle"><roundcube:label name="editcontact" /></div> 11 11 <div id="contact-details" class="boxcontent"> 12 <roundcube:object name="contacteditform" size="40" /> 12 <form name="editform" method="post" action="./"> 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 23 <div id="contacttabs"> 24 <roundcube:object name="contacteditform" size="40" textareacols="60" deleteIcon="/images/icons/delete.png" form="editform" /> 25 </div> 13 26 <p> 14 27 <roundcube:button command="show" type="input" class="button" label="cancel" /> … … 17 30 </form> 18 31 </div> 19 <script type="text/javascript">rcube_init_tabs('contact-details')</script> 32 33 <roundcube:object name="photoUploadForm" id="upload-form" size="30" class="popupmenu" /> 34 35 <script type="text/javascript">rcube_init_tabs('contacttabs')</script> 20 36 21 37 </body>
Note: See TracChangeset
for help on using the changeset viewer.
