diff --git a/action/ajax.php b/action/ajax.php --- a/action/ajax.php +++ b/action/ajax.php @@ -1,274 +1,274 @@ */ if(!defined('DOKU_INC')) die(); class action_plugin_davcard_ajax extends DokuWiki_Action_Plugin { /** * @var helper_plugin_davcard */ private $hlp = null; function __construct() { $this->hlp =& plugin_load('helper','davcard'); } function register(Doku_Event_Handler $controller) { $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handle_ajax_call_unknown'); } function handle_ajax_call_unknown(&$event, $param) { if($event->data != 'plugin_davcard') return; $event->preventDefault(); $event->stopPropagation(); global $INPUT; $action = trim($INPUT->post->str('action')); $id = trim($INPUT->post->str('id')); $page = trim($INPUT->post->str('page')); $params = $INPUT->post->arr('params'); if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) $user = $_SERVER['REMOTE_USER']; else $user = null; if(!checkSecurityToken()) { echo "CSRF Attack."; return; } $data = array(); $data['result'] = false; $data['html'] = $this->getLang('unknown_error'); - $acl = auth_quickaclcheck($id); + $acl = $this->hlp->checkAddressbookPermission($id); if($acl > AUTH_READ) { $write = true; } elseif($acl < AUTH_READ) { $data['result'] = false; $data['html'] = $this->getLang('no_permission'); // Overwrite $action to bypass switch statement below $action = 'invalid'; } else { $write = false; } // Parse the requested action switch($action) { // Add a new Contact case 'newContact': if($write && ($this->hlp->addContactEntryToAddressbookForPage($id, $user, $params) === true)) { $data['result'] = true; } else { $data['result'] = false; if(!$write) $data['html'] = $this->getLang('no_permission'); else $data['html'] = $this->getLang('error_adding'); } break; // Retrieve contact details case 'getContactDetails': $contactdata = $this->hlp->getContactByUri($id, $params['uri']); if($contactdata['result'] === true) { // When we support pictures for editing contacts, // we need to use the following line: // $contactdata['photo'] = base64_encode($contactdata['photo']); // For now, we just save bandwidth :) unset($contactdata['photo']); $data['result'] = true; $data['contactdata'] = $contactdata; } else { $data['result'] = false; $data['html'] = sprintf($this->getLang('contact_not_found'), 'ID='.$id.' URI='.$params['uri']); } break; // Edit a contact case 'editContact': if($write && ($this->hlp->editContactEntryToAddressbookForPage($id, $user, $params['uri'], $params) === true)) { $data['result'] = true; } else { $data['result'] = false; if(!$write) $data['html'] = $this->getLang('no_permission'); else $data['html'] = $this->getLang('error_editing'); } break; // Delete a Contact case 'deleteContact': if($write && ($this->hlp->deleteContactEntryToAddressbookForPage($id, $user, $params['uri']) === true)) { $data['result'] = true; } else { $data['result'] = false; if(!$write) $data['html'] = $this->getLang('no_permission'); else $data['html'] = $this->getLang('error_deleting'); } break; // Get AJAX popup case 'getContactAjax': $contactdata = $this->hlp->getContactByUri($id, $params['uri']); $cardpattern = $this->getConf('popup_content'); if($contactdata['result'] === false) { echo hsc($contactdata['formattedname']); return; } echo '
'; foreach($contactdata['photo'] as $data) { if(isset($data['type'])) $type = $data['type']; else $type = ''; echo '
'; $imgdata = base64_encode($data['photo']); $pattern = '/^(?:[;\/?:@&=+$,]|(?:[^\W_]|[-_.!~*\()\[\] ])|(?:%[\da-fA-F]{2}))*$/'; // PNG images if($type == 'png') { $imgdata = 'data:image/png;base64,'.$imgdata; echo 'contact image'; } // JPEG images elseif(($type == 'jpeg') || ($type == 'jpg')) { $imgdata = 'data:image/jpeg;base64,'.$imgdata; echo 'contact image'; } // GIF images elseif($type == 'gif') { $imgdata = 'data:image/gif;base64,'.$imgdata; echo 'contact image'; } // URLs (no type given) elseif(preg_match( $pattern, $string ) == 1) { echo 'contact image'; } echo '
'; } echo '
'; $contactname = explode(';', $contactdata['structuredname']); if(count($contactname) > 1) { $cardpattern = str_replace('', $contactname[0], $cardpattern); $cardpattern = str_replace('', $contactname[1], $cardpattern); } if(count($contactdata['addr']) > 0) { foreach($contactdata['addr'] as $data) { if(isset($data['type']) && ($data['type'] == 'work')) $prefix = 'WORK'; else $prefix = 'PRIVATE'; $cardpattern = str_replace('', $data['address'][2], $cardpattern); $cardpattern = str_replace('', $data['address'][3], $cardpattern); $cardpattern = str_replace('', $data['address'][5], $cardpattern); $cardpattern = str_replace('', $data['address'][6], $cardpattern); } } if(count($contactdata['tel']) > 0) { $telArr = array(); foreach($contactdata['tel'] as $data) { if(isset($data['type'])) $type = $data['type']; else $type = 'other'; $type = $data['type']; $telArr[] = $this->getLang('tel'.$type).': '.$data['number']; } $telStr = implode(' \\\\ ', $telArr); $cardpattern = str_replace('', $telStr, $cardpattern); } if(count($contactdata['mail']) > 0) { $mailArr = array(); foreach($contactdata['mail'] as $data) { $mailArr[] = '[['.$data['mail'].']]'; } $mailStr = implode(' \\\\ ', $mailArr); $cardpattern = str_replace('', $mailStr, $cardpattern); } if($contactdata['birthday'] != '') { $date = DateTime::createFromFormat('Ymd', $contactdata['birthday']); $dateStr = $date->format($this->getConf('date_format')); $cardpattern = str_replace('', $dateStr, $cardpattern); } if($contactdata['note'] != '') { $notestr = str_replace('\n', ' \\ ', $contactdata['note']); $cardpattern = str_replace('', $notestr, $cardpattern); } if($contactdata['title'] != '') { $cardpattern = str_replace('', $contactdata['title'], $cardpattern); } if($contactdata['url'] != '') { $url = $contactdata['url']; if(strpos($url, '://') === false) $url = 'http://'.$url; $url = '[['.$url.']]'; $cardpattern = str_replace('', $url, $cardpattern); } $replace_match = '/^.*.*$(?:\r\n|\n)?/m'; $cardpattern = preg_replace($replace_match, '', $cardpattern); echo $this->render_text($cardpattern); echo '
'; echo '
'; return; break; } // If we are still here, JSON output is requested //json library of DokuWiki require_once DOKU_INC . 'inc/JSON.php'; $json = new JSON(); //set content type header('Content-Type: application/json'); echo $json->encode($data); } } diff --git a/helper.php b/helper.php --- a/helper.php +++ b/helper.php @@ -1,830 +1,862 @@ */ // must be run within Dokuwiki if(!defined('DOKU_INC')) die(); class helper_plugin_davcard extends DokuWiki_Plugin { protected $sqlite = null; /** * Constructor to load the configuration */ public function helper_plugin_davcard() { } /** Establish and initialize the database if not already done * @return sqlite interface or false */ private function getDB() { if($this->sqlite === null) { $this->sqlite = plugin_load('helper', 'sqlite'); if(!$this->sqlite) { dbglog('This plugin requires the sqlite plugin. Please install it.'); msg('This plugin requires the sqlite plugin. Please install it.', -1); return false; } - if(!$this->sqlite->init('dvacard', DOKU_PLUGIN.'davcard/db/')) + if(!$this->sqlite->init('davcard', DOKU_PLUGIN.'davcard/db/')) { $this->sqlite = null; dbglog('Error initialising the SQLite DB for davcard'); return false; } } return $this->sqlite; } /** * Retrieve a contact by specifying details like the name * * @param int $id The address book ID * @param string $type The type to look for * @param array $params The parameter array * * @return array An array containing the results */ private function getContactByDetails($id, $type, $params = array()) { $write = false; if(strpos($id, 'webdav://') === 0) { $wdc =& plugin_load('helper', 'webdavclient'); if(is_null($wdc)) return $this->getLang('no_wdc'); $connectionId = str_replace('webdav://', '', $id); $settings = $wdc->getConnection($connectionId); if($settings === false) return array('formattedname' => $this->getLang('settings_not_found'), 'result' => false); if($settings['type'] !== 'contacts') return array('formattedname' => $this->getLang('wrong_type'), 'result' => false); $entries = $wdc->getAddressbookEntries($connectionId); $write = $settings['write']; } else { $acl = auth_quickaclcheck($id); if($acl > AUTH_READ) { $write = true; } elseif($acl < AUTH_READ) { return array('formattedname' => $this->getLang('no_permission'), 'result' => false); } else { $write = false; } $addressbookid = $this->getAddressbookIdForPage($id); $entries = $this->getAddressbookEntries($addressbookid); } foreach($entries as $entry) { switch($type) { case 'structuredname': $contactdata = explode(';', strtolower($entry['structuredname'])); if(count($contactdata) < 2) // We need at least first and last name return array('formattedname' => sprintf($this->getLang('contact_not_found'), $params['firstname']. ' '.$params['lastname']), 'result' => false); if(($params['lastname'] != '') && ($contactdata[0] === $params['lastname']) || $params['lastname'] === '') { // last name matched or no last name given if(($params['firstname'] != '') && ($contactdata[1] === $params['firstname']) || $params['firstname'] === '') { // first name matched too or no first name given $info = $this->parseVcard($entry['contactdata'], $entry['uri'], $write); return $info; } } break; case 'formattedname': if(trim(strtolower($entry['formattedname'])) == $params['formattedname']) { $info = $this->parseVcard($entry['contactdata'], $entry['uri'], $write); return $info; } break; case 'email': $info = $this->parseVcard($entry['contactdata'], $entry['uri'], $write); foreach($info['mail'] as $data) { if(trim(strtolower($data['mail'])) === $params['email']) return $info; } break; } } return array('formattedname' => sprintf($this->getLang('contact_not_found'), $this->getLang('invalid_options')), 'result' => false); } /** * Retreive all address book entries * * @param int $id The addressbook ID to retrieve * * @return array All address book entries */ public function getAddressbookEntries($id) { $sqlite = $this->getDB(); if(!$sqlite) return false; $query = "SELECT contactdata, uri, formattedname, structuredname FROM addressbookobjects WHERE addressbookid = ? ORDER BY formattedname ASC"; $res = $sqlite->query($query, $id); return $sqlite->res2arr($res); } /** * Retrieve a contact by the structured name * * @param string $id The addressbook ID to work with * @param string $firstname The contact's first name * @param string $lastname The contact's last name * * @return array The contact's details */ public function getContactByStructuredName($id, $firstname = '', $lastname = '') { return $this->getContactByDetails($id, 'structuredname', array('firstname' => strtolower($firstname), 'lastname' => strtolower($lastname))); } /** * Retrieve a contact by e-mail address * * @param string $id The address book ID * @param string $email The E-Mail address * * @return array The contact's details */ public function getContactByEmail($id, $email) { // FIXME: Maybe it's a good idea to save the e-mail in the database as well! return $this->getContactByDetails($id, 'email', array('email' => strtolower($email))); } /** * Retrieve a contact by formatted name * * @param string $id The address book ID * @param string $name The contact's formatted name * * @return array The contact's details */ public function getContactByFormattedName($id, $name) { return $this->getContactByDetails($id, 'formattedname', array('formattedname' => strtolower($name))); } /** * Retrieve a contact object by its URI * * @param string $ID The address book ID * @param string $uri The object URI * * @return array An array containing the result */ public function getContactByUri($id, $uri) { $write = false; if(strpos($id, 'webdav://') === 0) { $wdc =& plugin_load('helper', 'webdavclient'); if(is_null($wdc)) return $this->getLang('no_wdc'); $connectionId = str_replace('webdav://', '', $id); $settings = $wdc->getConnection($connectionId); if($settings === false) return array('formattedname' => $this->getLang('settings_not_found'), 'result' => false); if($settings['type'] !== 'contacts') return array('formattedname' => $this->getLang('wrong_type'), 'result' => false); $row = $wdc->getAddressbookEntryByUri($connectionId, $uri); $write = $settings['write']; } else { $acl = auth_quickaclcheck($id); if($acl > AUTH_READ) { $write = true; } elseif($acl < AUTH_READ) { return array('formattedname' => $this->getLang('no_permission'), 'result' => false); } else { $write = false; } $addressbookid = $this->getAddressbookIdForPage($id); $row = $this->getAddressbookEntryByUri($addressbookid, $uri); } if($row === false) return array('formattedname' => sprintf($this->getLang('contact_not_found'), 'ID='.$id.' URI='.$uri), 'result' => false); $info = $this->parseVcard($row['contactdata'], $row['uri'], $write); $info['result'] = true; return $info; } /** * Retrieve an address book entry by URI (low-level version) * * @param int $id The address book ID * @param string $uri The object URI * * @return array The contact's details */ private function getAddressbookEntryByUri($id, $uri) { $sqlite = $this->getDB(); if(!$sqlite) return false; $query = "SELECT contactdata, addressbookid, etag, uri, formattedname, structuredname FROM addressbookobjects WHERE addressbookid = ? AND uri = ?"; $res = $sqlite->query($query, $id, $uri); return $sqlite->res2row($res); } /** * Set the addressbook name for a given page * * @param string $name The name to set * @param string $description The address book description * @param int $id (optional) The page ID * @param string $userid (optional) The user's ID * * @return boolean true on success, otherwise false */ public function setAddressbookNameForPage($name, $description, $id = null, $userid = null) { if(is_null($id)) { global $ID; $id = $ID; } if(is_null($userid)) { if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) { $userid = $_SERVER['REMOTE_USER']; } else { $userid = uniqid('davcard-'); } } $bookid = $this->getAddressbookIdForPage($id); if($bookid === false) return $this->createAddressbookForPage($name, $description, $id, $userid); $sqlite = $this->getDB(); if(!$sqlite) return false; $query = "UPDATE addressbooks SET displayname = ?, description = ? WHERE id = ?"; $res = $sqlite->query($query, $name, $description, $bookid); if($res !== false) return true; return false; } /** * Get the address book ID associated with a given page * * @param string $id (optional) The page ID * * @return mixed The address book ID or false */ public function getAddressbookIdForPage($id = null) { if(is_null($id)) { global $ID; $id = $ID; } $sqlite = $this->getDB(); if(!$sqlite) return false; $query = "SELECT addressbookid FROM pagetoaddressbookmapping WHERE page = ?"; $res = $sqlite->query($query, $id); $row = $sqlite->res2row($res); if(isset($row['addressbookid'])) { $addrbkid = $row['addressbookid']; return $addrbkid; } return false; } /** * Create a new address book for a given page * * @param string $name The name of the new address book * @param string $description The address book's description * @param string $id (optional) The page ID * @param string $userid (optional) The user's ID * * @return boolean True on success, otherwise false */ public function createAddressbookForPage($name, $description, $id = null, $userid = null) { if(is_null($id)) { global $ID; $id = $ID; } if(is_null($userid)) { if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) { $userid = $_SERVER['REMOTE_USER']; } else { $userid = uniqid('davcard-'); } } $sqlite = $this->getDB(); if(!$sqlite) return false; $values = array('principals/'.$userid, $name, str_replace(array('/', ' ', ':'), '_', $id), $description, 1); $query = "INSERT INTO addressbooks (principaluri, displayname, uri, description, synctoken) ". "VALUES (?, ?, ?, ?, ?)"; $res = $sqlite->query($query, $values); if($res === false) return false; // Get the new addressbook ID $query = "SELECT id FROM addressbooks WHERE principaluri = ? AND displayname = ? AND ". "uri = ? AND description = ? AND synctoken = ?"; $res = $sqlite->query($query, $values); $row = $sqlite->res2row($res); // Update the pagetocalendarmapping table with the new calendar ID if(isset($row['id'])) { $query = "INSERT INTO pagetoaddressbookmapping (page, addressbookid) VALUES (?, ?)"; $res = $sqlite->query($query, $id, $row['id']); return ($res !== false); } return false; } /** * Delete a contact entry from an address book by URI * * @param string $id The address book ID * @param string $user The user's ID * @param string $uri The object URI to delete * * @return boolean True on success, otherwise false */ public function deleteContactEntryToAddressbookForPage($id, $user, $uri) { if(strpos($id, 'webdav://') === 0) { $wdc =& plugin_load('helper', 'webdavclient'); if(is_null($wdc)) return $this->getLang('no_wdc'); $connectionId = str_replace('webdav://', '', $id); $settings = $wdc->getConnection($connectionId); if($settings === false) return array('formattedname' => $this->getLang('settings_not_found'), 'result' => false); if($settings['type'] !== 'contacts') return array('formattedname' => $this->getLang('wrong_type'), 'result' => false); return $wdc->deleteAddressbookEntry($connectionId, $uri); } $sqlite = $this->getDB(); if(!$sqlite) return false; $addressbookid = $this->getAddressbookIdForPage($id); $query = "DELETE FROM addressbookobjects WHERE uri = ? AND addressbookid = ?"; $res = $sqlite->query($query, $uri, $addressbookid); if($res !== false) { $this->updateSyncTokenLog($addressbookid, $uri, 'deleted'); return true; } return false; } /** * Edit a contact for a given address book * * @param string $id The address book ID * @param string $user The user name * @param string $uri The object URI * @param array $params The new address book parameters * * @return boolean True on success, otherwise false */ public function editContactEntryToAddressbookForPage($id, $user, $uri, $params) { require_once(DOKU_PLUGIN.'davcard/vendor/autoload.php'); if(strpos($id, 'webdav://') === 0) { $wdc =& plugin_load('helper', 'webdavclient'); if(is_null($wdc)) return $this->getLang('no_wdc'); $connectionId = str_replace('webdav://', '', $id); $settings = $wdc->getConnection($connectionId); if($settings === false) return array('formattedname' => $this->getLang('settings_not_found'), 'result' => false); if($settings['type'] !== 'contacts') return array('formattedname' => $this->getLang('wrong_type'), 'result' => false); $row = $wdc->getAddressbookEntryByUri($connectionId, $uri); } else { $addressbookid = $this->getAddressbookIdForPage($id); $row = $this->getAddressbookEntryByUri($addressbookid, $uri); } $vcard = \Sabre\VObject\Reader::read($row['contactdata']); $vcard->remove('ADR'); $vcard->remove('TEL'); $vcard->remove('EMAIL'); if(isset($params['phones'])) { foreach($params['phones'] as $data) { $vcard->add('TEL', $data['number'], array('type' => $data['type'])); } } if(isset($params['email'])) { foreach($params['email'] as $data) { $vcard->add('EMAIL', $data['mail'], array('type' => $data['type'])); } } if(isset($params['addresses'])) { foreach($params['addresses'] as $data) { $vcard->add('ADR', array('', '', $data['street'], $data['city'], '', $data['zipcode'], $data['country']), array('type' => $data['type'])); } } $structuredname = explode(';', (string)$vcard->N); $structuredname[0] = $params['lastname']; $structuredname[1] = $params['firstname']; $formattedname = $params['firstname'].' '.$params['lastname']; // FIXME: Make this configurable? $vcard->N = $structuredname; $vcard->FN = $formattedname; $contactdata = $vcard->serialize(); if(strpos($id, 'webdav://') === 0) { return $wdc->editAddressbookEntry($connectionId, $uri, $contactdata); } else { $sqlite = $this->getDB(); if(!$sqlite) return false; $now = new \DateTime(); $query = "UPDATE addressbookobjects SET contactdata = ?, lastmodified = ?, etag = ?, size = ?, formattedname = ?, structuredname = ? WHERE addressbookid = ? AND uri = ?"; $res = $sqlite->query($query, $contactdata, $now->getTimestamp(), md5($contactdata), strlen($contactdata), $formattedname, implode(';', $structuredname), $addressbookid, $uri ); if($res !== false) { $this->updateSyncTokenLog($addressbookid, $uri, 'modified'); return true; } } return false; } /** * Add a new contact entry to an address book page * * @param string $id The page ID * @param string $user The user ID * @param array $params The entry's parameters * * @return boolean True on success, otherwise false */ public function addContactEntryToAddressbookForPage($id, $user, $params) { require_once(DOKU_PLUGIN.'davcard/vendor/autoload.php'); $vcard = new \Sabre\VObject\Component\VCard(); $formattedname = $params['firstname'].' '.$params['lastname']; // FIXME: Make this configurable? $structuredname = array($params['lastname'], $params['firstname'], '', '', ''); $vcard->FN = $formattedname; $vcard->N = $structuredname; if(isset($params['phones'])) { foreach($params['phones'] as $data) { $vcard->add('TEL', $data['number'], array('type' => $data['type'])); } } if(isset($params['email'])) { foreach($params['email'] as $data) { $vcard->add('EMAIL', $data['mail'], array('type' => $data['type'])); } } if(isset($params['addresses'])) { foreach($params['addresses'] as $data) { $vcard->add('ADR', array('', '', $data['street'], $data['city'], '', $data['zipcode'], $data['country']), array('type' => $data['type'])); } } $contactdata = $vcard->serialize(); if(strpos($id, 'webdav://') === 0) { $wdc =& plugin_load('helper', 'webdavclient'); if(is_null($wdc)) return false; $connectionId = str_replace('webdav://', '', $id); return $wdc->addAddressbookEntry($connectionId, $contactdata); } else { $sqlite = $this->getDB(); if(!$sqlite) return false; $addressbookid = $this->getAddressbookIdForPage($id); $uri = uniqid('dokuwiki-').'.vcf'; $now = new \DateTime(); $query = "INSERT INTO addressbookobjects (contactdata, uri, addressbookid, lastmodified, etag, size, formattedname, structuredname) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; $res = $sqlite->query($query, $contactdata, $uri, $addressbookid, $now->getTimestamp(), md5($contactdata), strlen($contactdata), $formattedname, implode(';', $structuredname) ); // If successfully, update the sync token database if($res !== false) { $this->updateSyncTokenLog($addressbookid, $uri, 'added'); return true; } } return false; } /** * Parse a VCard and extract important contact information * * @param string $card The VCard data * @param string $uri The object URI * @param boolean $write Writable * * @return array An array with parsed data */ public function parseVcard($card, $uri, $write) { require_once(DOKU_PLUGIN.'davcard/vendor/autoload.php'); $vObject = \Sabre\VObject\Reader::read($card); $formattedname = ''; $structuredname = ''; $tel = array(); $addr = array(); $mail = array(); $photo = array(); $birthday = ''; $note = ''; $title = ''; $url = ''; if(isset($vObject->FN)) $formattedname = (string)$vObject->FN; if(isset($vObject->N)) $structuredname = join(';', $vObject->N->getParts()); if(isset($vObject->TEL)) { foreach($vObject->TEL as $number) { if(isset($number['TYPE'])) $tel[] = array('type' => strtolower((string)$number['TYPE']), 'number' => (string)$number); else $tel[] = array('number' => (string)$number); } } if(isset($vObject->ADR)) { foreach($vObject->ADR as $adr) { if(isset($adr['TYPE'])) $addr[] = array('type' => strtolower((string)$adr['TYPE']), 'address' => $adr->getParts()); else $addr[] = array('address' => $adr->getParts()); } } if(isset($vObject->EMAIL)) { foreach($vObject->EMAIL as $email) { if(isset($email['TYPE'])) $mail[] = array('type' => strtolower((string)$email['TYPE']), 'mail' => (string)$email); else $mail[] = array('mail' => (string)$email); } } if(isset($vObject->PHOTO)) { if(isset($vObject->PHOTO['TYPE'])) { $photo[] = array('type' => strtolower((string)$vObject->PHOTO['TYPE']), 'photo' => (string)$vObject->PHOTO); } else $photo[] = array('photo' => (string)$vObject->PHOTO); } if(isset($vObject->BDAY)) { $birthday = (string)$vObject->BDAY; $birthday = str_replace('-', '', $birthday); } if(isset($vObject->NOTE)) { $note = (string)$vObject->NOTE; } if(isset($vObject->TITLE)) { $title = (string)$vObject->TITLE; } if(isset($vObject->URL)) { $url = (string)$vObject->URL; } return array( 'formattedname' => $formattedname, 'structuredname' => $structuredname, 'tel' => $tel, 'mail' => $mail, 'addr' => $addr, 'uri' => $uri, 'photo' => $photo, 'birthday' => $birthday, 'note' => $note, 'title' => $title, 'url' => $url, 'result' => true, 'write' => $write ); } /** * Retrieve the settings of a given address book * * @param int $addressbookid The addressbook's ID * * @return array The settings */ public function getAddressbookSettings($addressbookid) { $sqlite = $this->getDB(); if(!$sqlite) return false; $query = "SELECT id, principaluri, displayname, uri, description, synctoken FROM addressbooks WHERE id= ? "; $res = $sqlite->query($query, $addressbookid); $row = $sqlite->res2row($res); return $row; } /** * Retrieve the current synctoken for an address book * * @param int $addressbookid The addressbook's ID * * @return string The current synctoken */ public function getSyncTokenForAddressbook($addressbookid) { $row = $this->getAddressbookSettings($addressbookid); if(isset($row['synctoken'])) return $row['synctoken']; return false; } /** * Helper function to convert the operation name to * an operation code as stored in the database * * @param string $operationName The operation name * * @return mixed The operation code or false */ public function operationNameToOperation($operationName) { switch($operationName) { case 'added': return 1; break; case 'modified': return 2; break; case 'deleted': return 3; break; } return false; } /** * Update the synctoken log for a given address book * * @param string $addressbookid The addressbook ID to work with * @param string $uri The object URI that was modified * @param string $operation The operation that was performed * * @return boolean True on success, otherwise false */ private function updateSyncTokenLog($addressbookid, $uri, $operation) { $currentToken = $this->getSyncTokenForAddressbook($addressbookid); $operationCode = $this->operationNameToOperation($operation); if(($operationCode === false) || ($currentToken === false)) return false; $values = array($uri, $currentToken, $addressbookid, $operationCode ); $sqlite = $this->getDB(); if(!$sqlite) return false; $query = "INSERT INTO addressbookchanges (uri, synctoken, addressbookid, operation) VALUES(?, ?, ?, ?)"; $res = $sqlite->query($query, $uri, $currentToken, $addressbookid, $operationCode); if($res === false) return false; $currentToken++; $query = "UPDATE addressbooks SET synctoken = ? WHERE id = ?"; $res = $sqlite->query($query, $currentToken, $addressbookid); return ($res !== false); } + + /** + * Check the permission of a user for a given addressbook ID + * + * @param string $id The addressbook ID to check + * @return int AUTH_* constants + */ + public function checkAddressbookPermission($id) + { + if(strpos($id, 'webdav://') === 0) + { + $wdc =& plugin_load('helper', 'webdavclient'); + if(is_null($wdc)) + return AUTH_NONE; + $connectionId = str_replace('webdav://', '', $id); + $settings = $wdc->getConnection($connectionId); + if($settings === false) + return AUTH_NONE; + if($settings['write'] === '1') + return AUTH_CREATE; + return AUTH_READ; + } + else + { + $addr = $this->getAddressbookIdForPage($id); + // We return AUTH_READ if the calendar does not exist. This makes + // davcard happy when there are just included addressbooks + if($addr === false) + return AUTH_READ; + return auth_quickaclcheck($id); + } + } } diff --git a/lang/en/lang.php b/lang/en/lang.php --- a/lang/en/lang.php +++ b/lang/en/lang.php @@ -1,80 +1,82 @@ */ $lang['unknown_error'] = 'Unknown Error'; $lang['id_name_not_set'] = 'Either ID or Name must be set'; $lang['loading_via_ajax'] = 'Loadig Contact Data...'; $lang['no_wdc'] = 'Loading webdavclient PlugIn failed.'; $lang['settings_not_found'] = 'The requested WebDAV connection was not found'; $lang['wrong_type'] = 'The requested WebDAV connection is not of type contact'; $lang['contact_not_found'] = 'The requested contact (%s) was not found'; $lang['error_adding'] = 'Error adding contact'; $lang['error_editing'] = 'Error editing contact'; $lang['error_deleting'] = 'Error deleting contact'; $lang['invalid_options'] = 'invalid options given'; -$lang['no_permission'] = 'You do not have permission to read this address book'; +$lang['no_permission'] = 'You do not have permission for this address book'; $lang['telvoice'] = 'Voice'; $lang['telhome'] = 'Home'; $lang['telmsg'] = 'Message'; $lang['telwork'] = 'Work'; $lang['telpref'] = 'preferred'; $lang['telfax'] = 'Fax'; $lang['telcell'] = 'Cell'; $lang['telvideo'] = 'Video'; $lang['telpager'] = 'Pager'; $lang['telbbs'] = 'BBS'; $lang['telmodem'] = 'Modem'; $lang['telcar'] = 'Car'; $lang['telisdn'] = 'ISDN'; $lang['telpcs'] = 'PCS'; $lang['telother'] = 'Other'; $lang['adrintl'] = 'International'; $lang['adrpostal'] = 'Postal'; $lang['adrparcel'] = 'Parcel'; $lang['adrwork'] = 'Work'; $lang['adrdom'] = 'Domestic'; $lang['adrhome'] = 'Home'; $lang['adrpref'] = 'preferred'; $lang['adrother'] = 'Other'; $lang['created_by_davcard'] = 'Created by DAVCard'; $lang['add_new'] = 'Add new entry'; $lang['edit'] = 'Edit'; $lang['name'] = 'Name'; $lang['address'] = 'Address'; $lang['phone'] = 'Phone'; $lang['email'] = 'E-Mail'; $lang['js']['confirmation'] = 'Confirmation'; $lang['js']['yes'] = 'Yes'; $lang['js']['no'] = 'No'; $lang['js']['cancel'] = 'Cancel'; $lang['js']['create'] = 'Create'; $lang['js']['info'] = 'Info'; $lang['js']['ok'] = 'OK'; $lang['js']['edit'] = 'Edit'; $lang['js']['edit_entry'] = 'Edit Entry'; $lang['js']['create_entry'] = 'Create Entry'; $lang['js']['delete'] = 'Delete'; $lang['js']['really_delete_this_entry'] = 'Really delete this entry?'; $lang['js']['firstname'] = 'First Name'; $lang['js']['lastname'] = 'Last Name'; $lang['js']['city'] = 'City'; $lang['js']['zipcode'] = 'Zip Code'; $lang['js']['cellphone'] = 'Cell Phone'; $lang['js']['phone'] = 'Phone'; $lang['js']['add_phone'] = 'Add Phonenumber'; $lang['js']['add_mail'] = 'Add Mail'; $lang['js']['addresses'] = 'Addresses'; $lang['js']['street'] = 'Street'; $lang['js']['country'] = 'Country'; $lang['js']['email'] = 'Email'; $lang['js']['loading'] = 'Loading...'; $lang['js']['error_loading'] = 'Error loading contact data from server!'; $lang['js']['workphone'] = 'Work Phone'; $lang['js']['otherphone'] = 'Other Phone'; $lang['js']['work'] = 'Work'; $lang['js']['home'] = 'Home'; $lang['js']['add_address'] = 'Add address'; $lang['js']['other_address'] = 'Other Address'; +$lang['js']['addressbook'] = 'Addressbook'; + diff --git a/plugin.info.txt b/plugin.info.txt --- a/plugin.info.txt +++ b/plugin.info.txt @@ -1,7 +1,7 @@ base davcard author Andreas Boehler email dev@aboehler.at -date 2016-12-13 +date 2017-02-21 name Addressbook PlugIn with CardDAV client support desc Show contact information from a CardDAV address book (needs webdavclient) url http://www.dokuwiki.org/plugin:davcard diff --git a/script.js b/script.js --- a/script.js +++ b/script.js @@ -1,745 +1,765 @@ jQuery(function() { var links = 0; // Remove the CSS-only popup and replace it with a jQuery/AJAX popup jQuery('div.dokuwiki a.plugin_davcard_url span.plugin_davcard_popup').each(function() { jQuery(this).addClass('plugin_davcard_nopopup'); jQuery(this).removeClass('plugin_davcard_popup'); var $link = jQuery(this).parents('a.plugin_davcard_url'); if(!$link) return; $link.davcard_popup_id = 'plugin_davcard_popup_'+(links++); $link.mouseover(function () { $link.davcard_timer = window.setTimeout( function () { dw_davcard__modals.showOverlay($link); $link.davcard_timer = null; }, 300 ); }); // Attach edit event dialog to link $link.click( function(e) { dw_davcard__modals.attachEditDialog($link); e.preventDefault(); return ''; } ); $link.mouseout(function () { $link = jQuery(this); if ($link.davcard_timer) window.clearTimeout($link.davcard_timer); $link.davcard_timer = null; }); }); // Attach to addressbook links var addressbookpage = jQuery('#davcardAddressbookList').data('addressbookpage'); if(!addressbookpage) return; jQuery('div.davcardAddressbookAddNew a').each(function() { var $link = jQuery(this); var href = $link.attr('href'); if (!href) return; $link.click( function(e) { dw_davcard__modals.id = addressbookpage; dw_davcard__modals.showEditContactDialog(null, false, true); e.preventDefault(); return ''; } ); } ); jQuery('div.dokuwiki a.plugin_davcard_edit_vcard').each(function() { var $link = jQuery(this); // Attach edit event dialog to link $link.click( function(e) { dw_davcard__modals.attachEditDialog($link); e.preventDefault(); return ''; } ); }); }); /** * This holds all modal windows that DAVCard uses. */ var dw_davcard__modals = { $editContactDialog: null, $confirmDialog: null, $loadingDialog: null, page: JSINFO['id'], id: null, uri: null, action: null, completeCb: null, msg: null, attachEditDialog : function($link) { dw_davcard__modals.showLoadingDialog(); dw_davcard__modals.id = $link.data('davcardid'); var write = $link.data('write'); jQuery.post( DOKU_BASE + 'lib/exe/ajax.php', { call: 'plugin_davcard', id: $link.data('davcardid'), page: dw_davcard__modals.page, action: 'getContactDetails', params: { uri: $link.data('davcarduri'), }, sectok: JSINFO.plugin.davcard['sectok'] }, function(data) { var result = data['result']; if(result === true) { dw_davcard__modals.hideLoadingDialog(); dw_davcard__modals.showEditContactDialog(data['contactdata'], true, write); } else { dw_davcard__modals.hideLoadingDialog(); dw_davcard__modals.msg = LANG.plugins.davcard['error_loading']; dw_davcard__modals.showDialog(false); } } ); }, showEditContactDialog : function(entry, edit, write) { if(dw_davcard__modals.$editContactDialog) return; var title = ''; var dialogButtons = {}; if(edit) { title = LANG.plugins.davcard['edit_entry']; dialogButtons[LANG.plugins.davcard['edit']] = function() { var postArray = { }; var addrArray = new Array(); var mailArray = new Array(); var phoneArray = new Array(); + var pageid = jQuery("#dw_davcard__addressbook option:selected").val(); jQuery("tr.dw_davcard__editentry_phone_row").each(function() { var type = jQuery(this).children("td.dw_davcard__editentry_phone_row_type").data('type'); var phone = jQuery(this).children("td.dw_davcard__editentry_phone_row_number").html(); var phoneRow = { }; phoneRow['type'] = type; phoneRow['number'] = phone; phoneArray.push(phoneRow); }); jQuery("tr.dw_davcard__editentry_email_row").each(function() { var type = jQuery(this).children("td.dw_davcard__editentry_email_row_data").data('type'); var email = jQuery(this).children("td.dw_davcard__editentry_email_row_data").html(); mailRow = { }; mailRow['type'] = type; mailRow['mail'] = email; mailArray.push(mailRow); }); jQuery("tr.dw_davcard__editentry_addresses_row").each(function() { var $rowData = jQuery(this).children("td.dw_davcard__editentry_addresses_row_data"); var type = $rowData.data('type'); var city = $rowData.data('city'); var street = $rowData.data('street'); var zipcode = $rowData.data('zipcode'); var country = $rowData.data('country'); var addrRow = { }; addrRow['type'] = type; addrRow['city'] = city; addrRow['street'] = street; addrRow['zipcode'] = zipcode; addrRow['country'] = country; addrArray.push(addrRow); }); postArray['phones'] = phoneArray; postArray['email'] = mailArray; postArray['addresses'] = addrArray; postArray['uri'] = entry['uri']; jQuery("input.dw_davcard__editcontact").each(function() { if(jQuery(this).attr('type') == 'checkbox') { postArray[jQuery(this).prop('name')] = jQuery(this).prop('checked') ? 1 : 0; } else { postArray[jQuery(this).prop('name')] = jQuery(this).val(); } }); jQuery('#dw_davcard__ajaxedit').html(''); jQuery.post( DOKU_BASE + 'lib/exe/ajax.php', { call: 'plugin_davcard', - id: dw_davcard__modals.id, + id: pageid, page: dw_davcard__modals.page, action: 'editContact', params: postArray, sectok: JSINFO.plugin.davcard['sectok'] }, function(data) { var result = data['result']; var html = data['html']; jQuery('#dw_davcard__ajaxedit').html(html); if(result === true) { dw_davcard__modals.hideEditContactDialog(); location.reload(); } } ); }; dialogButtons[LANG.plugins.davcard['delete']] = function() { dw_davcard__modals.action = 'deleteContact'; dw_davcard__modals.uri = entry['uri']; dw_davcard__modals.msg = LANG.plugins.davcard['really_delete_this_entry']; dw_davcard__modals.completeCb = function(data) { var result = data['result']; jQuery('#dw_davcard__ajaxedit').html(data['html']); if(result === true) { dw_davcard__modals.hideEditContactDialog(); location.reload(); } }; dw_davcard__modals.showDialog(true); }; } else { title = LANG.plugins.davcard['create_entry']; dialogButtons[LANG.plugins.davcard['create']] = function() { var postArray = { }; var addrArray = new Array(); var mailArray = new Array(); var phoneArray = new Array(); + var pageid = jQuery("#dw_davcard__addressbook option:selected").val(); jQuery("tr.dw_davcard__editentry_phone_row").each(function() { var type = jQuery(this).children("td.dw_davcard__editentry_phone_row_type").data('type'); var phone = jQuery(this).children("td.dw_davcard__editentry_phone_row_number").html(); var phoneRow = { }; phoneRow['type'] = type; phoneRow['number'] = phone; phoneArray.push(phoneRow); }); jQuery("tr.dw_davcard__editentry_email_row").each(function() { var type = jQuery(this).children("td.dw_davcard__editentry_email_row_data").data('type'); var email = jQuery(this).children("td.dw_davcard__editentry_email_row_data").html(); mailRow = { }; mailRow['type'] = type; mailRow['mail'] = email; mailArray.push(mailRow); }); jQuery("tr.dw_davcard__editentry_addresses_row").each(function() { var $rowData = jQuery(this).children("td.dw_davcard__editentry_addresses_row_data"); var type = $rowData.data('type'); var city = $rowData.data('city'); var street = $rowData.data('street'); var zipcode = $rowData.data('zipcode'); var country = $rowData.data('country'); var addrRow = { }; addrRow['type'] = type; addrRow['city'] = city; addrRow['street'] = street; addrRow['zipcode'] = zipcode; addrRow['country'] = country; addrArray.push(addrRow); }); postArray['phones'] = phoneArray; postArray['email'] = mailArray; postArray['addresses'] = addrArray; jQuery("input.dw_davcard__editcontact").each(function() { if(jQuery(this).attr('type') == 'checkbox') { postArray[jQuery(this).prop('name')] = jQuery(this).prop('checked') ? 1 : 0; } else { postArray[jQuery(this).prop('name')] = jQuery(this).val(); } }); jQuery('#dw_davcard__ajaxedit').html(''); jQuery.post( DOKU_BASE + 'lib/exe/ajax.php', { call: 'plugin_davcard', - id: dw_davcard__modals.id, + id: pageid, page: dw_davcard__modals.page, action: 'newContact', params: postArray, sectok: JSINFO.plugin.davcard['sectok'] }, function(data) { var result = data['result']; var html = data['html']; jQuery('#dw_davcard__ajaxedit').html(html); if(result === true) { dw_davcard__modals.hideEditContactDialog(); location.reload(); } } ); }; } // Remove create/edit buttons if we are read only if(!write) { delete dialogButtons['create']; delete dialogButtons['edit']; } dialogButtons[LANG.plugins.davcard['cancel']] = function() { dw_davcard__modals.hideEditContactDialog(); }; dw_davcard__modals.$editContactDialog = jQuery(document.createElement('div')) .dialog({ autoOpen: false, draggable: true, // fix for dragging: http://stackoverflow.com/questions/17247486/jquery-ui-dialog-dragging-issues drag: function(event, ui) { var fixPix = jQuery(document).scrollTop(); iObj = ui.position; iObj.top = iObj.top - fixPix; jQuery(this).closest(".ui-dialog").css("top", iObj.top + "px"); }, title: title, resizable: true, buttons: dialogButtons, }) .html( '
' + - // FIXME: '' + + '' + '' + '' + '' + '' + '' + '
' + LANG.plugins.davcard['calendar'] + '
' + LANG.plugins.davcard['addressbook'] + '
' + LANG.plugins.davcard['firstname'] + '
' + LANG.plugins.davcard['lastname'] + '
' + LANG.plugins.davcard['phone'] + '
' + LANG.plugins.davcard['add_phone'] + '
' + LANG.plugins.davcard['email'] + '
' + LANG.plugins.davcard['add_mail'] + '
' + LANG.plugins.davcard['addresses'] + '' + '' + '' + '' + '' + '
' + LANG.plugins.davcard['street'] + '
' + LANG.plugins.davcard['zipcode'] + '
' + LANG.plugins.davcard['city'] + '
' + LANG.plugins.davcard['country'] + '
' + LANG.plugins.davcard['add_address'] + '
' + '' + '
' + '
' ) .parent() .attr('id','dw_davcard__edit') .css({'overflow-y': 'auto'}) .show() .appendTo('.dokuwiki:first'); // attach event handlers jQuery('#dw_davcard__edit .ui-dialog-titlebar-close').click(function(){ dw_davcard__modals.hideEditContactDialog(); }); // Populate Dropdowns var $dropdown = jQuery('#dw_davcard__editentry_phones_select'); $dropdown.append(''); $dropdown.append(''); $dropdown.append(''); $dropdown = jQuery('#dw_davcard__editentry_addresses_select'); $dropdown.append(''); $dropdown.append(''); + + $dropdown = jQuery('#dw_davcard__addressbook'); + jQuery('#davcardAddressbookList option').each(function() { + var sel = ''; + if(edit && dw_davcard__modals.id == jQuery(this).val()) + sel = ' selected="selected"'; + if(edit || (!edit && (jQuery(this).data('write') == "1" || jQuery(this).data('write') == "true"))) + { + $dropdown.append(''); + } + }); + + if(edit) + { + $dropdown.prop('disabled', true); + } + + dw_davcard__modals.attachPhoneDeleteHandlers(); dw_davcard__modals.attachMailDeleteHandlers(); dw_davcard__modals.attachAddressDeleteHandlers(); jQuery('#dw_davcard__editentry_phone_add').on("click", function(e) { e.preventDefault(); var number = jQuery('#dw_davcard__editentry_phone_edit').val(); if(number == '') return false; var type = jQuery('#dw_davcard__editentry_phones_select option:selected').val(); var typeText = jQuery('#dw_davcard__editentry_phones_select option:selected').text(); jQuery('#dw_davcard__editentry_phone_edit').val(''); dw_davcard__modals.addPhone(type, typeText, number); return false; }); jQuery('#dw_davcard__editentry_email_add').on("click", function(e) { e.preventDefault(); var mail = jQuery('#dw_davcard__editentry_email_edit').val(); if(mail == '') return false; jQuery('#dw_davcard__editentry_email_edit').val(''); dw_davcard__modals.addMail('internet', mail); return false; }); jQuery('#dw_davcard__editentry_addresses_add').on("click", function(e) { e.preventDefault(); var street = jQuery('#dw_davcard__editentry_addresses_street').val(); var zipcode = jQuery('#dw_davcard__editentry_addresses_zipcode').val(); var country = jQuery('#dw_davcard__editentry_addresses_country').val(); var city = jQuery('#dw_davcard__editentry_addresses_city').val(); var type = jQuery('#dw_davcard__editentry_addresses_select option:selected').val(); var typeText = jQuery('#dw_davcard__editentry_addresses_select option:selected').text(); if(street == '' && zipcode == '' && country == '' && city == '') return false; jQuery('#dw_davcard__editentry_addresses_street').val(''); jQuery('#dw_davcard__editentry_addresses_city').val(''); jQuery('#dw_davcard__editentry_addresses_country').val(''); jQuery('#dw_davcard__editentry_addresses_zipcode').val(''); dw_davcard__modals.addAddress(type, typeText, street, zipcode, city, country); return false; }); if(edit) { var name = entry['structuredname'].split(';'); jQuery('#dw_davcard__lastname_edit').val(name[0]); jQuery('#dw_davcard__firstname_edit').val(name[1]); for(var i=0; i jQuery(window).height()) { jQuery('#dw_davcard__edit').height(jQuery(window).height() * 0.9); } jQuery('#dw_davcard__edit').position({ my: "center", at: "center", of: window }); }, addressTypeToString: function(type) { var ret = ''; switch(type) { case 'work': ret = LANG.plugins.davcard['work']; break; case 'home': ret = LANG.plugins.davcard['home']; break; default: ret = LANG.plugins.davcard['other_address']; break; } return ret; }, phoneTypeToString: function(type) { var ret = ''; switch(type) { case 'work': ret = LANG.plugins.davcard['workphone']; break; case 'cell': ret = LANG.plugins.davcard['cellphone']; break; case 'home': ret = LANG.plugins.davcard['phone']; break; default: ret = LANG.plugins.davcard['otherphone']; break; } return ret; }, addPhone: function(type, typeText, number) { var row = '' + typeText + '' + number + '' + LANG.plugins.davcard['delete'] + ''; jQuery('#dw_davcard__editentry_phones > tbody:last').append(row); dw_davcard__modals.attachPhoneDeleteHandlers(); }, addMail: function(type, mail) { var row = '' + mail + '' + LANG.plugins.davcard['delete'] + ''; jQuery('#dw_davcard__editentry_emails > tbody:last').append(row); dw_davcard__modals.attachMailDeleteHandlers(); }, addAddress: function(type, typeText, street, zipcode, city, country) { var row = '' + typeText + '' + LANG.plugins.davcard['street'] + ': ' + street + '
' + '' + LANG.plugins.davcard['city'] + ': ' + zipcode + ' ' + city + '
' + '' + LANG.plugins.davcard['country'] + ': ' + country + '
' + '' + LANG.plugins.davcard['delete'] + ''; jQuery('#dw_davcard__editentry_addresses > tbody:last').append(row); dw_davcard__modals.attachAddressDeleteHandlers(); }, attachAddressDeleteHandlers: function() { jQuery("#dw_davcard__editentry_addresses .deleteAddress").on("click", function(e) { e.preventDefault(); var tr = jQuery(this).closest('tr'); tr.css("background-color", "#FF3700"); tr.fadeOut(400, function() { tr.remove(); }); return false; }); }, attachPhoneDeleteHandlers: function() { jQuery("#dw_davcard__editentry_phones .deletePhoneNumber").on("click", function(e) { e.preventDefault(); var tr = jQuery(this).closest('tr'); tr.css("background-color", "#FF3700"); tr.fadeOut(400, function() { tr.remove(); }); return false; }); }, attachMailDeleteHandlers: function() { jQuery("#dw_davcard__editentry_emails .deleteEmail").on("click", function(e) { e.preventDefault(); var tr = jQuery(this).closest('tr'); tr.css("background-color", "#FF3700"); tr.fadeOut(400, function() { tr.remove(); }); return false; }); }, showLoadingDialog: function() { if(dw_davcard__modals.$loadingDialog) return; var dialogButtons = {}; var title = ''; title = LANG.plugins.davcard['loading']; dialogButtons[LANG.plugins.davcard['cancel']] = function() { dw_davcard__modals.hideLoadingDialog(); }; dw_davcard__modals.$loadingDialog = jQuery(document.createElement('div')) .dialog({ autoOpen: false, draggable: true, //fix for dragging: http://stackoverflow.com/questions/17247486/jquery-ui-dialog-dragging-issues drag: function(event, ui) { var fixPix = jQuery(document).scrollTop(); iObj = ui.position; iObj.top = iObj.top - fixPix; jQuery(this).closest(".ui-dialog").css("top", iObj.top + "px"); }, title: title, resizable: true, buttons: dialogButtons, }) .html('
' + LANG.plugins.davcard['loading'] + '
' ) .parent() .attr('id','dw_davcard__loading') .show() .appendTo('.dokuwiki:first'); jQuery('#dw_davcard__loading').position({ my: "center", at: "center", of: window }); // attach event handlers jQuery('#dw_davcard__loading .ui-dialog-titlebar-close').click(function(){ dw_davcard__modals.hideDialog(); }); }, hideEditContactDialog : function() { dw_davcard__modals.$editContactDialog.empty(); dw_davcard__modals.$editContactDialog.remove(); dw_davcard__modals.$editContactDialog = null; }, hideDialog: function() { dw_davcard__modals.$dialog.empty(); dw_davcard__modals.$dialog.remove(); dw_davcard__modals.$dialog = null; }, hideLoadingDialog: function() { dw_davcard__modals.$loadingDialog.empty(); dw_davcard__modals.$loadingDialog.remove(); dw_davcard__modals.$loadingDialog = null; }, showOverlay: function($link) { if(!$link.davcard_popup) { $link.davcard_popup = dw_page.insituPopup($link, $link.davcard_popup_id); $link.davcard_popup.addClass('plugin_davcard_popup_overlay'); $link.davcard_popup.load( DOKU_BASE + 'lib/exe/ajax.php', { call: 'plugin_davcard', sectok: JSINFO.plugin.davcard['sectok'], action: 'getContactAjax', id: $link.data('davcardid'), params: { uri: $link.data('davcarduri'), } } ); } $link.davcard_popup.show(); }, showDialog : function(confirm) { if(dw_davcard__modals.$confirmDialog) return; var dialogButtons = {}; var title = ''; if(confirm) { title = LANG.plugins.davcard['confirmation']; dialogButtons[LANG.plugins.davcard['yes']] = function() { jQuery.post( DOKU_BASE + 'lib/exe/ajax.php', { call: 'plugin_davcard', id: dw_davcard__modals.id, page: dw_davcard__modals.page, action: dw_davcard__modals.action, params: { uri: dw_davcard__modals.uri }, sectok: JSINFO.plugin.davcard['sectok'] }, function(data) { dw_davcard__modals.completeCb(data); } ); dw_davcard__modals.hideDialog(); }; dialogButtons[LANG.plugins.davcard['cancel']] = function() { dw_davcard__modals.hideDialog(); }; } else { title = LANG.plugins.davcard['info']; dialogButtons[LANG.plugins.davcard['ok']] = function() { dw_davcard__modals.hideDialog(); }; } dw_davcard__modals.$dialog = jQuery(document.createElement('div')) .dialog({ autoOpen: false, draggable: true, //fix for dragging: http://stackoverflow.com/questions/17247486/jquery-ui-dialog-dragging-issues drag: function(event, ui) { var fixPix = jQuery(document).scrollTop(); iObj = ui.position; iObj.top = iObj.top - fixPix; jQuery(this).closest(".ui-dialog").css("top", iObj.top + "px"); }, title: title, resizable: true, buttons: dialogButtons, }) .html( '
' + dw_davcard__modals.msg + '
' ) .parent() .attr('id','dw_davcard__confirm') .show() .appendTo('.dokuwiki:first'); jQuery('#dw_davcard__confirm').position({ my: "center", at: "center", of: window }); // attach event handlers jQuery('#dw_davcard__confirm .ui-dialog-titlebar-close').click(function(){ dw_davcard__modals.hideDialog(); }); }, }; \ No newline at end of file diff --git a/syntax/book.php b/syntax/book.php --- a/syntax/book.php +++ b/syntax/book.php @@ -1,313 +1,323 @@ */ // must be run within Dokuwiki if(!defined('DOKU_INC')) die(); if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); require_once(DOKU_PLUGIN.'syntax.php'); class syntax_plugin_davcard_book extends DokuWiki_Syntax_Plugin { protected $hlp = null; // Load the helper plugin public function syntax_plugin_davcard_book() { $this->hlp =& plugin_load('helper', 'davcard'); } /** * What kind of syntax are we? */ function getType(){ return 'substition'; } /** * What about paragraphs? */ function getPType(){ return 'normal'; } /** * Where to sort in? */ function getSort(){ return 165; } /** * Connect pattern to lexer */ function connectTo($mode) { $this->Lexer->addSpecialPattern('\{\{davcardbook>[^}]*\}\}',$mode,'plugin_davcard_book'); } /** * Handle the match */ function handle($match, $state, $pos, Doku_Handler $handler){ global $ID; $options = trim(substr($match,14,-2)); $options = explode(',', $options); $data = array('name' => $ID, 'description' => $this->getLang('created_by_davcard'), 'id' => array(), 'filter' => array(), ); foreach($options as $option) { list($key, $val) = explode('=', $option); $key = strtolower(trim($key)); $val = trim($val); switch($key) { case 'filter': list($k, $v) = explode(':', strtolower($val), 2); $data['filter'][$k] = $v; break; case 'id': if(!in_array($val, $data['id'])) $data['id'][] = $val; break; default: $data[$key] = $val; } } // Handle the default case when the user didn't enter a different ID if(empty($data['id'])) { $data['id'] = array($ID); } // Only update the addressbook name/description if the ID matches the page ID. // Otherwise, the addressbook is included in another page and we don't want // to interfere with its data. if(in_array($ID, $data['id'])) { if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER'])) $username = $_SERVER['REMOTE_USER']; else $username = uniqid('davcard-'); $this->hlp->setAddressbookNameForPage($data['name'], $data['description'], $ID, $username); } $meta = p_get_metadata($ID, 'plugin_davcard'); if(is_null($meta)) $meta = array(); $meta['addressbooks'] = $data; // Add webdavclient information so that we can disable caching if need be foreach($data['id'] as $addrbkid) { if(strpos($addrbkid, 'webdav://') === 0) { $connectionId = str_replace('webdav://', '', $addrbkid); if(!is_array($meta['webdavclient'])) $meta['webdavclient'] = array(); if(!in_array($addrbkid, $meta['webdavclient'])) $meta['webdavclient'][] = $connectionId; } } p_set_metadata($ID, array('plugin_davcard' => $meta)); return $data; } /** * Create output */ function render($format, Doku_Renderer $R, $data) { global $ID; if($format !== 'xhtml') return false; - if(in_array($ID, $data['id'])) - { - $R->doc .= '
'.$this->getLang('add_new').'
'; - } - // FIXME: Add new is not yet permission checked and does not support - // included address books! + + $addressbooklist = array(); + + $R->doc .= '
'.$this->getLang('add_new').'
'; $R->doc .= '
'; $R->doc .= ''; $R->doc .= ''; foreach($data['id'] as $id) { $write = false; if(strpos($id, 'webdav://') === 0) { $wdc =& plugin_load('helper', 'webdavclient'); if(is_null($wdc)) { echo $this->getLang('no_wdc'); continue; } $connectionId = str_replace('webdav://', '', $id); $settings = $wdc->getConnection($connectionId); - if($settings === false) { echo $this->getLang('settings_not_found'); continue; } if($settings['type'] !== 'contacts') { echo $this->getLang('wrong_type'); continue; } + $name = $settings['displayname']; $entries = $wdc->getAddressbookEntries($connectionId); $write = $settings['write']; } else { $acl = auth_quickaclcheck($id); if($acl > AUTH_READ) { $write = true; } elseif($acl < AUTH_READ) { continue; } else { $write = false; } $addressbookid = $this->hlp->getAddressbookIdForPage($id); + $name = $this->hlp->getAddressBookSettings($addressbookid); + $name = $name['displayname']; $entries = $this->hlp->getAddressbookEntries($addressbookid); } + + $addressbooklist[] = array('id' => $id, 'name' => $name, 'write' => $write); + foreach($entries as $entry) { $contactdata = $this->hlp->parseVcard($entry['contactdata'], $entry['uri'], $write); if(!$this->contactFilterMatch($data['filter'], $contactdata)) continue; $R->doc .= ''; } } $R->doc .= '
'.$this->getLang('name').''.$this->getLang('address').''.$this->getLang('phone').''.$this->getLang('email').'
'.$entry['formattedname'].''; if(count($contactdata['addr']) > 0) { $R->doc .= ''; foreach($contactdata['addr'] as $dat) { if(isset($dat['type'])) $type = $dat['type']; else $type = 'other'; $R->doc .= ''.$this->getLang('adr'.strtolower($type)).''; if($dat['address'][2] != '') { $R->doc .= ''.$dat['address'][2].'
'; } if($dat['address'][5] != '') { $R->doc .= ''.$dat['address'][5].' '; } if($dat['address'][3] != '') { $R->doc .= ''.$dat['address'][3].'
'; } if($dat['address'][6] != '') { $R->doc .= ''.$dat['address'][6].''; } } $R->doc .= '
'; } $R->doc .= '
'; if(count($contactdata['tel']) > 0) { $R->doc .= ''; foreach($contactdata['tel'] as $dat) { if(isset($dat['type'])) $type = $dat['type']; else $type = 'other'; $R->doc .= ''.$this->getLang('tel'.strtolower($type)).' '; $R->doc .= $dat['number'].'
'; } $R->doc .= '
'; } $R->doc .= '
'; if(count($contactdata['mail']) > 0) { foreach($contactdata['mail'] as $dat) { $R->doc .= '
'; } } $R->doc .= '
'; + $R->doc .= ''; $R->doc .= '
'; } /** * Check if a contact matches a given filter pattern * * @param array $filter The filter array * @param array $contactdata The contact's data to match * * @return true on success, otherwise false */ private function contactFilterMatch($filter, $contactdata) { if(empty($filter)) return true; foreach($filter as $type => $params) { $params = '/'.$params.'/i'; switch($type) { case 'name': if(preg_match($params, $contactdata['formattedname']) !== 1) return false; break; case 'mail': $found = false; foreach($contactdata['mail'] as $dat) { if(preg_match($params, $dat['mail']) === 1) $found = true; } if(!$found) return false; break; case 'address': $found = false; foreach($contactdata['addr'] as $dat) { foreach($dat['address'] as $da) { if(preg_match($params, $da) === 1) $found = true; } } if(!$found) return false; break; case 'tel': $found = false; foreach($contactdata['tel'] as $dat) { if(preg_match($params, $dat['number']) === 1) $found = true; } if(!$found) return false; break; } } return true; } } // vim:ts=4:sw=4:et:enc=utf-8: diff --git a/syntax/card.php b/syntax/card.php --- a/syntax/card.php +++ b/syntax/card.php @@ -1,215 +1,207 @@ */ // must be run within Dokuwiki if(!defined('DOKU_INC')) die(); if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); require_once(DOKU_PLUGIN.'syntax.php'); class syntax_plugin_davcard_card extends DokuWiki_Syntax_Plugin { protected $hlp = null; // Load the helper plugin public function syntax_plugin_davcard_card() { $this->hlp =& plugin_load('helper', 'davcard'); } /** * What kind of syntax are we? */ function getType(){ return 'substition'; } /** * What about paragraphs? */ function getPType(){ return 'normal'; } /** * Where to sort in? */ function getSort(){ return 165; } /** * Connect pattern to lexer */ function connectTo($mode) { $this->Lexer->addSpecialPattern('\{\{davcard>[^}]*\}\}',$mode,'plugin_davcard_card'); $this->Lexer->addSpecialPattern('\{\{davcardclient>[^}]*\}\}',$mode,'plugin_davcard_card'); } /** * Handle the match */ function handle($match, $state, $pos, Doku_Handler $handler){ global $ID; $data = array('name' => '', 'id' => $ID, 'firstname' => '', 'lastname' => '', 'email' => '', 'uri' => '', ); if(strpos($match, '{{davcardclient') === 0) { $options = trim(substr($match,16,-2)); $defaultId = $this->getConf('default_client_id'); if(isset($defaultId) && ($defaultId != '')) { $data['id'] = $defaultId; } } else { $options = trim(substr($match,10,-2)); } $options = explode(',', $options); foreach($options as $option) { list($key, $val) = explode('=', $option); $key = strtolower(trim($key)); $val = trim($val); switch($key) { default: $data[$key] = $val; } } - // FIXME: This is nonsense - if($data['id'] === '') - { - if(($data['name'] === '') || (($data['firstname'] === '') && ($data['lastname'] === ''))) - { - msg($this->getLang('id_name_not_set'), -1); - } - } return $data; } /** * Create output */ function render($format, Doku_Renderer $R, $data) { if($format == 'metadata') { if(strpos($data['id'], 'webdav://') === 0) { $connectionId = str_replace('webdav://', '', $data['id']); $R->meta['plugin_davcard']['webdavclient'][] = $connectionId; return true; } } if($format != 'xhtml') return false; $contactdata = array(); $srch = ''; if($data['name'] !== '') { $contactdata = $this->hlp->getContactByFormattedName($data['id'], $data['name']); $srch = $data['name']; } elseif(($data['firstname'] !== '') || ($data['lastname'] !== '')) { $contactdata = $this->hlp->getContactByStructuredName($data['id'], $data['firstname'], $data['lastname']); $srch = $data['firstname'].' '.$data['lastname']; } elseif(($data['email'] !== '')) { $contactdata = $this->hlp->getContactByEmail($data['id'], $data['email']); $srch = 'E-Mail = '.$data['email']; } elseif(($data['uri'] !== '')) { $contactdata = $this->hlp->getContactByUri($data['id'], $data['uri']); $srch = 'URI = '.$data['uri']; } if($contactdata['result'] === false) { $R->doc .= sprintf($this->getLang('contact_not_found'), $srch); return; } $R->doc .= ''.$contactdata['formattedname']; $R->doc .= ''; if(count($contactdata['addr']) > 0) { $R->doc .= ''; foreach($contactdata['addr'] as $dat) { if(isset($data['type'])) $type = $dat['type']; else $type = 'other'; $R->doc .= ''.$this->getLang('adr'.strtolower($type)).''; if($dat['address'][2] != '') { $R->doc .= ''.$dat['address'][2].'
'; } if($dat['address'][5] != '') { $R->doc .= ''.$dat['address'][5].' '; } if($dat['address'][3] != '') { $R->doc .= ''.$dat['address'][3].'
'; } if($dat['address'][6] != '') { $R->doc .= ''.$dat['address'][6].''; } } $R->doc .= '
'; } if(count($contactdata['tel']) > 0) { $R->doc .= ''; foreach($contactdata['tel'] as $dat) { if(isset($dat['type'])) $type = $dat['type']; else $type = 'other'; $R->doc .= ''.$this->getLang('tel'.strtolower($type)).' '; $R->doc .= $dat['number'].'
'; } $R->doc .= '
'; } if(count($contactdata['mail']) > 0) { $R->doc .= ''; } $R->doc .= '
'; $R->doc .= '
'; } } // vim:ts=4:sw=4:et:enc=utf-8: