Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- class vcard_addressbook_backend extends rcube_addressbook
- {
- public $primary_key = 'ID';
- public $readonly = true;
- public $groups = false;
- private $name;
- private $contacts = [];
- private $filter;
- public function __construct($name, $vcard_file)
- {
- $this->ready = true;
- $this->name = $name;
- $this->load_vcard_file($vcard_file);
- }
- /**
- * Load the contacts from a .vcard file
- */
- private function load_vcard_file($vcard_file)
- {
- // error_log("load_vcard_file: " . $vcard_file); // Log contacts without ID
- if (file_exists($vcard_file)) {
- $vcard_data = file_get_contents($vcard_file);
- // error_log("VCard data: " . $vcard_data); // Log if file doesn't exist
- $this->contacts = $this->parse_vcards($vcard_data, pathinfo($vcard_file, PATHINFO_FILENAME));
- // error_log("Loaded contacts: " . print_r($this->contacts, true)); // Log loaded contacts
- } else {
- error_log("VCard file does not exist: " . $vcard_file); // Log if file doesn't exist
- }
- }
- /**
- * Parse VCard data into a list of contacts
- */
- private function parse_vcards($vcard_data, $filename)
- {
- $contacts = [];
- $vcard_blocks = preg_split('/(?=BEGIN:VCARD)/', $vcard_data);
- foreach ($vcard_blocks as $vcard) {
- // Validate that the vCard block starts with BEGIN:VCARD
- if (strpos($vcard, 'BEGIN:VCARD') === false) {
- error_log("Skipping invalid block: " . $vcard);
- continue;
- }
- $contact = [];
- $lines = explode("\n", trim($vcard));
- foreach ($lines as $line) {
- $line = trim($line);
- if (empty($line)) {
- continue; // Skip empty lines
- }
- // Parse vCard properties with improved structure
- if (strpos($line, 'UID:') === 0) {
- $contact['ID'] = trim(substr($line, strlen('UID:')));
- } elseif (strpos($line, 'FN:') === 0) {
- $fullname = trim(substr($line, strlen('FN:')));
- $contact['name'] = $fullname;
- // Construct firstname and surname if they are not already set
- $name_parts = explode(' ', $fullname);
- $contact['firstname'] = $name_parts[0] ?? '';
- $contact['surname'] = end($name_parts) ?? ''; // Get last name
- } elseif (strpos($line, 'EMAIL:') === 0) {
- $contact['email'] = strtolower(trim(substr($line, strlen('EMAIL:'))));
- } elseif (strpos($line, 'TEL:') === 0) {
- // Allow multiple phone numbers
- $contact['phone'][] = trim(substr($line, strlen('TEL:')));
- } elseif (strpos($line, 'ORG:') === 0) {
- $contact['org'] = trim(substr($line, strlen('ORG:')));
- } elseif (strpos($line, 'TITLE:') === 0) {
- $contact['title'] = trim(substr($line, strlen('TITLE:')));
- } elseif (strpos($line, 'ADR:') === 0) {
- // Allow multiple addresses
- $contact['address'][] = trim(substr($line, strlen('ADR:')));
- } elseif (strpos($line, 'NOTE:') === 0 && strpos($line, 'GB Archery Number') !== false) {
- $contact['gb_archerynumber'] = trim(substr($line, strpos($line, ':') + 1));
- }
- }
- // Ensure the contact has an ID before adding
- if (!empty($contact['ID'])) {
- // Flatten the address and phone into strings if they are arrays
- if (isset($contact['phone']) && is_array($contact['phone'])) {
- $contact['phone'] = implode(', ', array_map('trim', $contact['phone'])); // Join multiple phone numbers
- }
- if (isset($contact['address']) && is_array($contact['address'])) {
- $contact['address'] = implode('; ', array_map('trim', $contact['address'])); // Join multiple addresses
- }
- // Add groups based on the filename
- $contact['groups'] = [basename($filename, '.vcf')]; // Assuming the file is in .vcf format
- $contacts[] = $contact;
- } else {
- error_log("Skipping contact with no ID: " . print_r($contact, true));
- }
- }
- return $contacts;
- }
- #[Override]
- public function set_search_set($filter) {
- error_log("Setting search filter: " . print_r($filter, true));
- $this->filter = $filter; // Set the search filter
- }
- #[Override]
- public function get_search_set() {
- return $this->filter; // Return the current search filter
- }
- #[Override]
- public function reset() {
- $this->contacts = []; // Reset contacts
- $this->filter = null; // Reset filter
- $this->result = null;
- }
- #[Override]
- public function list_records($cols = null, $subset = 0, $nocount = false)
- {
- error_log("list_records() called.");
- $result = new rcube_result_set();
- // Add each contact to the result set
- foreach ($this->contacts as $contact) {
- // Only add contacts that match the filter
- if ($this->is_matching_filter($contact)) {
- $result->add($contact);
- }
- }
- // Set the count of records in the result set
- if (!$nocount) {
- $result->count = count($result->records); // Set count of filtered records
- }
- // error_log("list_records() result set: " . print_r($result, true));
- return $result;
- }
- #[Override]
- public function search($fields, $value, $mode = 0, $select = true, $nocount = false, $required = [])
- {
- error_log("fields: " . $fields);
- error_log("value: " . $value);
- $this->filter[$fields] = $value;
- error_log("$this->filter: " . $this->filter);
- // Log input for debugging
- error_log("search() called with fields: " . print_r($fields, true) . " and value: " . print_r($value, true));
- $result = new rcube_result_set();
- // Convert '*' wildcard to all available fields if necessary
- if (is_string($fields) && $fields === '*') {
- $fields = array_keys($this->contacts[0]); // Assuming all contacts have the same structure
- } elseif (!is_array($fields)) {
- error_log("Warning: Expected fields to be an array, received: " . gettype($fields));
- return $result; // Exit early if $fields is not an array
- }
- foreach ($this->contacts as $contact) {
- // Ensure $contact is an array
- if (!is_array($contact)) {
- error_log("Warning: Expected contact to be an array, received: " . gettype($contact));
- continue; // Skip if $contact is not an array
- }
- // Check for a match across the specified fields
- foreach ($fields as $field) {
- // Only proceed if the field exists in the contact
- if (isset($contact[$field])) {
- $data = is_array($contact[$field]) ? implode(' ', $contact[$field]) : (string)$contact[$field];
- if (stripos($data, $value) !== false && $this->is_matching_filter($contact)) {
- error_log("Matching contact found: " . print_r($contact, true));
- $result->add($contact);
- break; // Stop searching other fields for this contact if a match is found
- }
- }
- }
- }
- // Set the count of records in the result set based on the number of entries added
- if (!$nocount) {
- $result->count = count($result->records); // Set count of matched records
- }
- // Log the result for debugging
- error_log("search() result set: " . print_r($result, true));
- return $result;
- }
- #[Override]
- public function count()
- {
- // Count only the contacts that match the current filter
- $count = 0;
- foreach ($this->contacts as $contact) {
- if ($this->is_matching_filter($contact)) {
- $count++; // Increment count for each matching contact
- }
- }
- return $count; // Return the count of matching contacts
- }
- #[Override]
- public function get_record($id, $assoc = false)
- {
- // Create a new rcube_result_set to hold the results
- $this->result = new rcube_result_set(0);
- error_log("get_record id: " . $id);
- // Search for the contact with the matching ID
- foreach ($this->contacts as $contact) {
- if ($contact['ID'] == $id) {
- if ($assoc) {
- error_log("contact: " . print_r($contact, true));
- return $contact; // Return associative array if requested
- }
- // Add the found contact to the result set
- $this->result->add($contact);
- $this->result->count = 1; // Set the count to 1 since we found one record
- break; // Exit the loop once the contact is found
- }
- }
- error_log("get_record result: " . print_r($this->result, true));
- return $this->result; // Return the result set
- }
- #[Override]
- public function get_result()
- {
- error_log("get_result");
- // Create a new rcube_result_set and add all contacts to it
- // $result = new rcube_result_set(count($this->contacts)); // Set the initial count
- // foreach ($this->contacts as $contact) {
- // $result->add($contact); // Add each contact to the result set
- // }
- error_log("result: " . print_r($this->result, true));
- return $this->result; // Return the result set with all contacts
- }
- #[Override]
- public function get_name()
- {
- return $this->name; // Return the name of the address book
- }
- #[Override]
- public function get_record_groups($id)
- {
- return []; // Assuming no groups for this backend
- }
- #[Override]
- public function set_group($gid)
- {
- // No groups implemented, do nothing
- }
- #[Override]
- public function create_group($name)
- {
- return true; // No groups implemented
- }
- #[Override]
- public function delete_group($gid)
- {
- return false; // No groups implemented
- }
- #[Override]
- public function rename_group($gid, $newname, &$newid)
- {
- return $newname; // No groups implemented
- }
- #[Override]
- public function add_to_group($group_id, $ids)
- {
- return 0; // No groups implemented
- }
- #[Override]
- public function remove_from_group($group_id, $ids)
- {
- return 0; // No groups implemented
- }
- private function is_matching_filter($contact) {
- // error_log("is_matching_filter: " . print_r($contact, true) . " filter: " . print_r($this->filter));
- // Check if a contact matches the current filter criteria
- if ($this->filter) {
- foreach ($this->filter as $field => $value) {
- error_log("field: " . $field);
- error_log("value: " . $value);
- // If the field is "*", check all fields in the contact
- if ($field === "*") {
- // Search through all fields in the contact
- $matchFound = false;
- foreach ($contact as $contactField => $contactValue) {
- // Ensure $contactValue is a string before calling trim()
- if (is_string($contactValue) && stripos(trim($contactValue), trim($value)) !== false) {
- $matchFound = true;
- break; // Break out if a match is found
- }
- }
- if (!$matchFound) {
- error_log("is_matching_filter: false for wildcard search with value: $value");
- return false; // No matches found across all fields
- }
- } else {
- // Ensure field exists in the contact and value is trimmed
- if (isset($contact[$field]) && is_string($contact[$field]) && stripos(trim($contact[$field]), trim($value)) === false) {
- error_log("is_matching_filter: false for field: $field with value: $value");
- return false; // Contact does not match the filter
- }
- }
- }
- }
- error_log("is_matching_filter: true");
- return true; // Contact matches the filter
- }
- }
- class vcard_addressbook extends rcube_plugin
- {
- private $abook_id = 'vcard';
- #[Override]
- public function init()
- {
- $this->add_hook('addressbooks_list', [$this, 'address_sources']);
- $this->add_hook('addressbook_get', [$this, 'get_address_book']);
- }
- public function address_sources($p)
- {
- $config = rcmail::get_instance()->config;
- $vcard_directory = $config->get('vcard_directory');
- // error_log("address_sources() called. vCard directory: " . $vcard_directory);
- if (is_dir($vcard_directory)) {
- $vcard_files = glob($vcard_directory . '/*.vcard');
- // error_log("vCard files found: " . print_r($vcard_files, true));
- foreach ($vcard_files as $vcard_file) {
- $abook_name = basename($vcard_file, '.vcard');
- // error_log("Loading vCard file: " . $vcard_file);
- $abook = new vcard_addressbook_backend($abook_name, $vcard_file);
- $p['sources'][$this->abook_id . '_' . $abook_name] = [
- 'id' => $this->abook_id . '_' . $abook_name,
- 'name' => $abook_name,
- 'readonly' => $abook->readonly,
- 'groups' => $abook->groups,
- ];
- }
- } else {
- error_log("vCard directory does not exist or is not readable: " . $vcard_directory);
- }
- // error_log("Address book sources: " . print_r($p['sources'], true));
- return $p;
- }
- public function get_address_book($p)
- {
- $config = rcmail::get_instance()->config;
- $vcard_directory = $config->get('vcard_directory');
- if (strpos($p['id'], $this->abook_id . '_') === 0) {
- $abook_name = substr($p['id'], strlen($this->abook_id . '_'));
- $vcard_file = $vcard_directory . '/' . $abook_name . '.vcard';
- if (file_exists($vcard_file)) {
- $p['instance'] = new vcard_addressbook_backend($abook_name, $vcard_file);
- }
- }
- return $p;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment