/** * Convert the given date to a human readable form * This uses the date formatting properties from config * * @param mixed Date representation (string, timestamp or DateTime object) * @param string Date format to use * @param bool Enables date convertion according to user timezone * * @return string Formatted date string */ public function format_date($date, $format = null, $convert = true) { if (is_object($date) && is_a($date, 'DateTime')) { $timestamp = $date->format('U'); } else { if (!empty($date)) { $timestamp = rcube_strtotime($date); } if (empty($timestamp)) { return ''; } try { $date = new DateTime("@" . $timestamp); } catch (Exception $e) { return ''; } } if ($convert) { try { // convert to the right timezone $stz = date_default_timezone_get(); $tz = new DateTimeZone($this->config->get('timezone')); $date->setTimezone($tz); date_default_timezone_set($tz->getName()); $timestamp = $date->format('U'); } catch (Exception $e) { } } // define date format depending on current time if (!$format) { $now = time(); $now_date = getdate($now); $today_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday'], $now_date['year']); $week_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday'] - 6, $now_date['year']); $pretty_date = $this->config->get('prettydate'); if ($pretty_date && $timestamp > $today_limit && $timestamp < $now) { $format = $this->config->get('date_today', $this->config->get('time_format', 'H:i')); $today = true; } else { if ($pretty_date && $timestamp > $week_limit && $timestamp < $now) { $format = $this->config->get('date_short', 'D H:i'); } else { $format = $this->config->get('date_long', 'Y-m-d H:i'); } } } // strftime() format if (preg_match('/%[a-z]+/i', $format)) { $format = strftime($format, $timestamp); if ($stz) { date_default_timezone_set($stz); } return $today ? $this->gettext('today') . ' ' . $format : $format; } // parse format string manually in order to provide localized weekday and month names // an alternative would be to convert the date() format string to fit with strftime() $out = ''; for ($i = 0; $i < strlen($format); $i++) { if ($format[$i] == "\\") { // skip escape chars continue; } // write char "as-is" if ($format[$i] == ' ' || $format[$i - 1] == "\\") { $out .= $format[$i]; } else { if ($format[$i] == 'D') { $out .= $this->gettext(strtolower(date('D', $timestamp))); } else { if ($format[$i] == 'l') { $out .= $this->gettext(strtolower(date('l', $timestamp))); } else { if ($format[$i] == 'M') { $out .= $this->gettext(strtolower(date('M', $timestamp))); } else { if ($format[$i] == 'F') { $out .= $this->gettext('long' . strtolower(date('M', $timestamp))); } else { if ($format[$i] == 'x') { $out .= strftime('%x %X', $timestamp); } else { $out .= date($format[$i], $timestamp); } } } } } } } if ($today) { $label = $this->gettext('today'); // replcae $ character with "Today" label (#1486120) if (strpos($out, '$') !== false) { $out = preg_replace('/\\$/', $label, $out, 1); } else { $out = $label . ' ' . $out; } } if ($stz) { date_default_timezone_set($stz); } return $out; }
/** * Creates a new or updates an existing vcard from save data. */ private function create_vcard_from_save_data($save_data, $vcf = null) { unset($save_data['vcard']); if (!$vcf) { // create fresh minimal vcard $vcf = new VObject\Component\VCard(array('UID' => $save_data['cuid'], 'REV' => date('c'))); } else { // update revision $vcf->REV = date("c"); } // N is mandatory if (array_key_exists('kind', $save_data) && $save_data['kind'] === 'group') { $vcf->N = $save_data['name']; } else { $vcf->N = array($save_data['surname'], $save_data['firstname'], $save_data['middlename'], $save_data['prefix'], $save_data['suffix']); } $new_org_value = array(); if (array_key_exists("organization", $save_data) && strlen($save_data['organization']) > 0) { $new_org_value[] = $save_data['organization']; } if (array_key_exists("department", $save_data)) { if (is_array($save_data['department'])) { foreach ($save_data['department'] as $key => $value) { $new_org_value[] = $value; } } else { if (strlen($save_data['department']) > 0) { $new_org_value[] = $save_data['department']; } } } if (count($new_org_value) > 0) { $vcf->ORG = $new_org_value; } else { unset($vcf->ORG); } // normalize date fields to RFC2425 YYYY-MM-DD date values foreach ($this->datefields as $key) { if (array_key_exists($key, $save_data) && strlen($save_data[$key]) > 0) { $val = rcube_strtotime($save_data[$key]); $save_data[$key] = date('Y-m-d', $val); } } // due to a bug in earlier versions of RCMCardDAV the PHOTO field was encoded base64 TWICE // This was recognized and fixed on 2013-01-09 and should be kept here until reasonable // certain that it's been fixed on users data, too. if (!array_key_exists('photo', $save_data) && strlen($vcf->PHOTO) > 0) { $save_data['photo'] = $vcf->PHOTO; } if (array_key_exists('photo', $save_data) && strlen($save_data['photo']) > 0 && base64_decode($save_data['photo'], true) !== FALSE) { self::$helper->debug("photo is base64 encoded. Decoding..."); $i = 0; while (base64_decode($save_data['photo'], true) !== FALSE && $i++ < 10) { self::$helper->debug("Decoding {$i}..."); $save_data['photo'] = base64_decode($save_data['photo'], true); } if ($i >= 10) { lef::$helper->warn("PHOTO of " . $save_data['uid'] . " does not decode after 10 attempts..."); } } // process all simple attributes foreach ($this->vcf2rc['simple'] as $vkey => $rckey) { if (array_key_exists($rckey, $save_data)) { if (strlen($save_data[$rckey]) > 0) { $vcf->{$vkey} = $save_data[$rckey]; } else { // delete the field unset($vcf->{$vkey}); } } } // Special handling for PHOTO if ($property = $vcf->PHOTO) { $property['ENCODING'] = 'B'; $property['VALUE'] = 'BINARY'; } // process all multi-value attributes foreach ($this->vcf2rc['multi'] as $vkey => $rckey) { // delete and fully recreate all entries // there is no easy way of mapping an address in the existing card // to an address in the save data, as subtypes may have changed unset($vcf->{$vkey}); $stmap = array($rckey => 'other'); foreach ($this->coltypes[$rckey]['subtypes'] as $subtype) { $stmap[$rckey . ':' . $subtype] = $subtype; } foreach ($stmap as $rcqkey => $subtype) { if (array_key_exists($rcqkey, $save_data)) { $avalues = is_array($save_data[$rcqkey]) ? $save_data[$rcqkey] : array($save_data[$rcqkey]); foreach ($avalues as $evalue) { if (strlen($evalue) > 0) { $prop = $vcf->add($vkey, $evalue); $this->set_attr_label($vcf, $prop, $rckey, $subtype); // set label } } } } } // process address entries unset($vcf->ADR); foreach ($this->coltypes['address']['subtypes'] as $subtype) { $rcqkey = 'address:' . $subtype; if (array_key_exists($rcqkey, $save_data)) { foreach ($save_data[$rcqkey] as $avalue) { if (strlen($avalue['street']) || strlen($avalue['locality']) || strlen($avalue['region']) || strlen($avalue['zipcode']) || strlen($avalue['country'])) { $prop = $vcf->add('ADR', array('', '', $avalue['street'], $avalue['locality'], $avalue['region'], $avalue['zipcode'], $avalue['country'])); $this->set_attr_label($vcf, $prop, 'address', $subtype); // set label } } } } return $vcf; }
/** * Setter for address record fields * * @param string Field name * @param string Field value * @param string Type/section name */ public function set($field, $value, $type = 'HOME') { $field = strtolower($field); $type_uc = strtoupper($type); $typemap = array_flip($this->typemap); switch ($field) { case 'name': case 'displayname': $this->raw['FN'][0][0] = $value; break; case 'surname': $this->raw['N'][0][0] = $value; break; case 'firstname': $this->raw['N'][0][1] = $value; break; case 'middlename': $this->raw['N'][0][2] = $value; break; case 'prefix': $this->raw['N'][0][3] = $value; break; case 'suffix': $this->raw['N'][0][4] = $value; break; case 'nickname': $this->raw['NICKNAME'][0][0] = $value; break; case 'organization': $this->raw['ORG'][0][0] = $value; break; case 'photo': if (strpos($value, 'http:') === 0) { // TODO: fetch file from URL and save it locally? $this->raw['PHOTO'][0] = array(0 => $value, 'url' => true); } else { $this->raw['PHOTO'][0] = array(0 => $value, 'base64' => (bool) preg_match('![^a-z0-9/=+-]!i', $value)); } break; case 'email': $this->raw['EMAIL'][] = array(0 => $value, 'type' => array_filter(array('INTERNET', $type_uc))); $this->email[] = $value; break; case 'im': // save IM subtypes into extension fields $typemap = array_flip($this->immap); if ($field = $typemap[strtolower($type)]) { $this->raw[$field][] = array(0 => $value); } break; case 'birthday': case 'anniversary': if (($val = rcube_strtotime($value)) && ($fn = self::$fieldmap[$field])) { $this->raw[$fn][] = array(0 => date('Y-m-d', $val), 'value' => array('date')); } break; case 'address': if ($this->addresstypemap[$type_uc]) { $type = $this->addresstypemap[$type_uc]; } $value = $value[0] ? $value : array('', '', $value['street'], $value['locality'], $value['region'], $value['zipcode'], $value['country']); // fall through if not empty if (!strlen(join('', $value))) { break; } default: if ($field == 'phone' && $this->phonetypemap[$type_uc]) { $type = $this->phonetypemap[$type_uc]; } if (($tag = self::$fieldmap[$field]) && (is_array($value) || strlen($value))) { $index = count($this->raw[$tag]); $this->raw[$tag][$index] = (array) $value; if ($type) { $this->raw[$tag][$index]['type'] = explode(',', $typemap[$type] ? $typemap[$type] : $type); } } break; } }