function get() { if (!local_channel()) { return; } if (get_pconfig(local_channel(), 'cdav', 'enabled') != 1) { return t('You have to enable this plugin in Feature/Addon Settings > CalDAV/CardDAV Settings before you can use it.'); } //TODO: add a possibility to enable this plugin here. $channel = \App::get_channel(); $principalUri = 'principals/' . $channel['channel_address']; if (!cdav_principal($principalUri)) { return; } if (\DBA::$dba && \DBA::$dba->connected) { $pdovars = \DBA::$dba->pdo_get(); } else { killme(); } $pdo = new \PDO($pdovars[0], $pdovars[1], $pdovars[2]); $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); require_once 'vendor/autoload.php'; head_add_css('addon/cdav/view/css/cdav.css'); if (argv(1) === 'calendar') { $caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo); $calendars = $caldavBackend->getCalendarsForUser($principalUri); } //Display calendar(s) here if (argc() == 2 && argv(1) === 'calendar') { head_add_css('library/fullcalendar/fullcalendar.css'); head_add_css('addon/cdav/view/css/cdav_calendar.css'); head_add_js('library/moment/moment.min.js', 1); head_add_js('library/fullcalendar/fullcalendar.min.js', 1); head_add_js('library/fullcalendar/locale-all.js', 1); foreach ($calendars as $calendar) { $editable = $calendar['share-access'] == 2 ? 'false' : 'true'; // false/true must be string since we're passing it to javascript $color = $calendar['{http://apple.com/ns/ical/}calendar-color'] ? $calendar['{http://apple.com/ns/ical/}calendar-color'] : '#3a87ad'; $sharer = $calendar['share-access'] == 3 ? $calendar['{urn:ietf:params:xml:ns:caldav}calendar-description'] : ''; $switch = get_pconfig(local_channel(), 'cdav_calendar', $calendar['id'][0]); if ($switch) { $sources .= '{ url: \'/cdav/calendar/json/' . $calendar['id'][0] . '/' . $calendar['id'][1] . '\', color: \'' . $color . '\' }, '; } if ($calendar['share-access'] != 2) { $writable_calendars[] = ['displayname' => $calendar['{DAV:}displayname'], 'sharer' => $sharer, 'id' => $calendar['id']]; } } $sources = rtrim($sources, ', '); $first_day = get_pconfig(local_channel(), 'system', 'cal_first_day'); $first_day = $first_day ? $first_day : 0; $title = ['title', t('Event title')]; $dtstart = ['dtstart', t('Start date and time'), '', t('Example: YYYY-MM-DD HH:mm')]; $dtend = ['dtend', t('End date and time'), '', t('Example: YYYY-MM-DD HH:mm')]; $description = ['description', t('Description')]; $location = ['location', t('Location')]; $o .= replace_macros(get_markup_template('cdav_calendar.tpl', 'addon/cdav'), ['$sources' => $sources, '$color' => $color, '$lang' => \App::$language, '$first_day' => $first_day, '$prev' => t('Previous'), '$next' => t('Next'), '$today' => t('Today'), '$month' => t('Month'), '$week' => t('Week'), '$day' => t('Day'), '$list_month' => t('List month'), '$list_week' => t('List week'), '$list_day' => t('List day'), '$title' => $title, '$writable_calendars' => $writable_calendars, '$dtstart' => $dtstart, '$dtend' => $dtend, '$description' => $description, '$location' => $location, '$more' => t('More'), '$less' => t('Less'), '$calendar_select_label' => t('Select calendar'), '$delete' => t('Delete'), '$delete_all' => t('Delete all'), '$cancel' => t('Cancel'), '$recurrence_warning' => t('Sorry! Editing of recurrent events is not yet implemented.')]); return $o; } //Provide json data for calendar if (argc() == 5 && argv(1) === 'calendar' && argv(2) === 'json' && intval(argv(3)) && intval(argv(4))) { $id = [argv(3), argv(4)]; if (!cdav_perms($id[0], $calendars)) { killme(); } if (x($_GET, 'start')) { $start = new \DateTime($_GET['start']); } if (x($_GET, 'end')) { $end = new \DateTime($_GET['end']); } $filters['name'] = 'VCALENDAR'; $filters['prop-filters'][0]['name'] = 'VEVENT'; $filters['comp-filters'][0]['name'] = 'VEVENT'; $filters['comp-filters'][0]['time-range']['start'] = $start; $filters['comp-filters'][0]['time-range']['end'] = $end; $uris = $caldavBackend->calendarQuery($id, $filters); if ($uris) { $objects = $caldavBackend->getMultipleCalendarObjects($id, $uris); foreach ($objects as $object) { $vcalendar = \Sabre\VObject\Reader::read($object['calendardata']); if (isset($vcalendar->VEVENT->RRULE)) { $vcalendar = $vcalendar->expand($start, $end); } foreach ($vcalendar->VEVENT as $vevent) { $title = (string) $vevent->SUMMARY; $dtstart = (string) $vevent->DTSTART; $dtend = (string) $vevent->DTEND; $description = (string) $vevent->DESCRIPTION; $location = (string) $vevent->LOCATION; $rw = cdav_perms($id[0], $calendars, true) ? true : false; $recurrent = isset($vevent->{'RECURRENCE-ID'}) ? true : false; $editable = $rw ? true : false; if ($recurrent) { $editable = false; } $allDay = false; // allDay event rules if (!strpos($dtstart, 'T') && !strpos($dtend, 'T')) { $allDay = true; } if (strpos($dtstart, 'T000000') && strpos($dtend, 'T000000')) { $allDay = true; } $events[] = ['calendar_id' => $id, 'uri' => $object['uri'], 'title' => $title, 'start' => $dtstart, 'end' => $dtend, 'description' => $description, 'location' => $location, 'allDay' => $allDay, 'editable' => $editable, 'recurrent' => $recurrent, 'rw' => $rw]; } } json_return_and_die($events); } else { killme(); } } //enable/disable calendars if (argc() == 5 && argv(1) === 'calendar' && argv(2) === 'switch' && intval(argv(3)) && (argv(4) == 1 || argv(4) == 0)) { $id = argv(3); if (!cdav_perms($id, $calendars)) { killme(); } set_pconfig(local_channel(), 'cdav_calendar', argv(3), argv(4)); killme(); } //drop calendar if (argc() == 5 && argv(1) === 'calendar' && argv(2) === 'drop' && intval(argv(3)) && intval(argv(4))) { $id = [argv(3), argv(4)]; if (!cdav_perms($id[0], $calendars)) { killme(); } $caldavBackend->deleteCalendar($id); killme(); } //drop sharee if (argc() == 6 && argv(1) === 'calendar' && argv(2) === 'dropsharee' && intval(argv(3)) && intval(argv(4))) { $id = [argv(3), argv(4)]; $hash = argv(5); if (!cdav_perms($id[0], $calendars)) { killme(); } $sharee_arr = channelx_by_hash($hash); $sharee = new \Sabre\DAV\Xml\Element\Sharee(); $sharee->href = 'mailto:' . $sharee_arr['channel_hash']; $sharee->principal = 'principals/' . $sharee_arr['channel_address']; $sharee->access = 4; $caldavBackend->updateInvites($id, [$sharee]); killme(); } if (argv(1) === 'addressbook') { $carddavBackend = new \Sabre\CardDAV\Backend\PDO($pdo); $addressbooks = $carddavBackend->getAddressBooksForUser($principalUri); } //Display Adressbook here if (argc() == 3 && argv(1) === 'addressbook' && intval(argv(2))) { $id = argv(2); $displayname = cdav_perms($id, $addressbooks); if (!$displayname) { return; } head_add_css('addon/cdav/view/css/cdav_addressbook.css'); $o = ''; $sabrecards = $carddavBackend->getCards($id); foreach ($sabrecards as $sabrecard) { $uris[] = $sabrecard['uri']; } if ($uris) { $objects = $carddavBackend->getMultipleCards($id, $uris); foreach ($objects as $object) { $vcard = \Sabre\VObject\Reader::read($object['carddata']); $photo = ''; if ($vcard->PHOTO) { $photo_value = strtolower($vcard->PHOTO->getValueType()); // binary or uri if ($photo_value === 'binary') { $photo_type = strtolower($vcard->PHOTO['TYPE']); // mime jpeg, png or gif $photo = 'data:image/' . $photo_type . ';base64,' . base64_encode((string) $vcard->PHOTO); } else { $url = parse_url((string) $vcard->PHOTO); $photo = 'data:' . $url['path']; } } $fn = ''; if ($vcard->FN) { $fn = (string) $vcard->FN; } $org = ''; if ($vcard->ORG) { $org = (string) $vcard->ORG; } $title = ''; if ($vcard->TITLE) { $title = (string) $vcard->TITLE; } $tels = []; if ($vcard->TEL) { foreach ($vcard->TEL as $tel) { $type = $tel['TYPE'] ? translate_type((string) $tel['TYPE']) : ''; $tels[] = ['type' => $type, 'nr' => (string) $tel]; } } $emails = []; if ($vcard->EMAIL) { foreach ($vcard->EMAIL as $email) { $type = $email['TYPE'] ? translate_type((string) $email['TYPE']) : ''; $emails[] = ['type' => $type, 'address' => (string) $email]; } } $impps = []; if ($vcard->IMPP) { foreach ($vcard->IMPP as $impp) { $type = $impp['TYPE'] ? translate_type((string) $impp['TYPE']) : ''; $impps[] = ['type' => $type, 'address' => (string) $impp]; } } $urls = []; if ($vcard->URL) { foreach ($vcard->URL as $url) { $type = $url['TYPE'] ? translate_type((string) $url['TYPE']) : ''; $urls[] = ['type' => $type, 'address' => (string) $url]; } } $adrs = []; if ($vcard->ADR) { foreach ($vcard->ADR as $adr) { $type = $adr['TYPE'] ? translate_type((string) $adr['TYPE']) : ''; $adrs[] = ['type' => $type, 'address' => $adr->getParts()]; } } $note = ''; if ($vcard->NOTE) { $note = (string) $vcard->NOTE; } $cards[] = ['id' => $object['id'], 'uri' => $object['uri'], 'photo' => $photo, 'fn' => $fn, 'org' => $org, 'title' => $title, 'tels' => $tels, 'emails' => $emails, 'impps' => $impps, 'urls' => $urls, 'adrs' => $adrs, 'note' => $note]; } usort($cards, function ($a, $b) { return strcasecmp($a['fn'], $b['fn']); }); } $o .= replace_macros(get_markup_template('cdav_addressbook.tpl', 'addon/cdav'), ['$id' => $id, '$cards' => $cards, '$displayname' => $displayname, '$name_label' => t('Name'), '$org_label' => t('Organisation'), '$title_label' => t('Title'), '$tel_label' => t('Phone'), '$email_label' => t('Email'), '$impp_label' => t('Instant messenger'), '$url_label' => t('Website'), '$adr_label' => t('Address'), '$note_label' => t('Note'), '$mobile' => t('Mobile'), '$home' => t('Home'), '$work' => t('Work'), '$other' => t('Other'), '$add_card' => t('Add Contact'), '$add_field' => t('Add Field'), '$create' => t('Create'), '$update' => t('Update'), '$delete' => t('Delete'), '$cancel' => t('Cancel'), '$po_box' => t('P.O. Box'), '$extra' => t('Additional'), '$street' => t('Street'), '$locality' => t('Locality'), '$region' => t('Region'), '$zip_code' => t('ZIP Code'), '$country' => t('Country')]); return $o; } //delete addressbook if (argc() > 3 && argv(1) === 'addressbook' && argv(2) === 'drop' && intval(argv(3))) { $id = argv(3); if (!cdav_perms($id, $addressbooks)) { return; } $carddavBackend->deleteAddressBook($id); killme(); } }
function widget_cdav() { if (!local_channel() || get_pconfig(local_channel(), 'cdav', 'enabled') != 1) { return; } $channel = \App::get_channel(); $principalUri = 'principals/' . $channel['channel_address']; if (!cdav_principal($principalUri)) { return; } if (\DBA::$dba && \DBA::$dba->connected) { $pdovars = \DBA::$dba->pdo_get(); } else { killme(); } $pdo = new \PDO($pdovars[0], $pdovars[1], $pdovars[2]); $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); require_once 'vendor/autoload.php'; $o = ''; if (argc() == 2 && argv(1) === 'calendar') { $caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo); $sabrecals = $caldavBackend->getCalendarsForUser($principalUri); //TODO: we should probably also check for permission to send stream here $local_channels = q("SELECT * FROM channel LEFT JOIN abook ON abook_xchan = channel_hash WHERE channel_system = 0 AND channel_removed = 0 AND channel_hash != '%s' AND abook_channel = %d", dbesc($channel['channel_hash']), intval($channel['channel_id'])); $sharee_options .= '<option value="">' . t('Select Channel') . '</option>' . "\r\n"; foreach ($local_channels as $local_channel) { $sharee_options .= '<option value="' . $local_channel['channel_hash'] . '">' . $local_channel['channel_name'] . '</option>' . "\r\n"; } $access_options = '<option value="3">' . t('Read-write') . '</option>' . "\r\n"; $access_options .= '<option value="2">' . t('Read-only') . '</option>' . "\r\n"; //list calendars foreach ($sabrecals as $sabrecal) { if ($sabrecal['share-access'] == 1) { $access = ''; } if ($sabrecal['share-access'] == 2) { $access = 'read'; } if ($sabrecal['share-access'] == 3) { $access = 'read-write'; } $invites = $caldavBackend->getInvites($sabrecal['id']); $json_source = '/cdav/calendar/json/' . $sabrecal['id'][0] . '/' . $sabrecal['id'][1]; $switch = get_pconfig(local_channel(), 'cdav_calendar', $sabrecal['id'][0]); $color = $sabrecal['{http://apple.com/ns/ical/}calendar-color'] ? $sabrecal['{http://apple.com/ns/ical/}calendar-color'] : '#3a87ad'; $editable = $sabrecal['share-access'] == 2 ? 'false' : 'true'; // false/true must be string since we're passing it to javascript $sharees = []; $share_displayname = []; foreach ($invites as $invite) { if (strpos($invite->href, 'mailto:') !== false) { $sharee = channelx_by_hash(substr($invite->href, 7)); $sharees[] = ['name' => $sharee['channel_name'], 'access' => $invite->access == 3 ? ' (RW)' : ' (R)', 'hash' => $sharee['channel_hash']]; } } if (!$access) { $my_calendars[] = ['ownernick' => $channel['channel_address'], 'uri' => $sabrecal['uri'], 'displayname' => $sabrecal['{DAV:}displayname'], 'calendarid' => $sabrecal['id'][0], 'instanceid' => $sabrecal['id'][1], 'json_source' => $json_source, 'color' => $color, 'editable' => $editable, 'switch' => $switch, 'sharees' => $sharees]; } else { $shared_calendars[] = ['ownernick' => $channel['channel_address'], 'uri' => $sabrecal['uri'], 'displayname' => $sabrecal['{DAV:}displayname'], 'calendarid' => $sabrecal['id'][0], 'instanceid' => $sabrecal['id'][1], 'json_source' => $json_source, 'color' => $color, 'editable' => $editable, 'switch' => $switch, 'sharer' => $sabrecal['{urn:ietf:params:xml:ns:caldav}calendar-description'], 'access' => $access]; } if (!$access || $access === 'read-write') { $writable_calendars[] = ['displayname' => !$access ? $sabrecal['{DAV:}displayname'] : $share_displayname[0], 'id' => $sabrecal['id']]; } } $o .= replace_macros(get_markup_template('cdav_widget_calendar.tpl', 'addon/cdav'), ['$my_calendars_label' => t('My Calendars'), '$my_calendars' => $my_calendars, '$shared_calendars_label' => t('Shared Calendars'), '$shared_calendars' => $shared_calendars, '$sharee_options' => $sharee_options, '$access_options' => $access_options, '$share_label' => t('Share this calendar'), '$share' => t('Share'), '$edit_label' => t('Calendar name and color'), '$edit' => t('Edit'), '$create_label' => t('Create new calendar'), '$create' => t('Create'), '$create_placeholder' => t('Calendar Name'), '$tools_label' => t('Calendar Tools'), '$import_label' => t('Import calendar'), '$import_placeholder' => t('Select a calendar to import to'), '$upload' => t('Upload'), '$writable_calendars' => $writable_calendars]); return $o; } if (argc() >= 2 && argv(1) === 'addressbook') { $carddavBackend = new \Sabre\CardDAV\Backend\PDO($pdo); $sabreabooks = $carddavBackend->getAddressBooksForUser($principalUri); //list addressbooks foreach ($sabreabooks as $sabreabook) { $addressbooks[] = ['ownernick' => $channel['channel_address'], 'uri' => $sabreabook['uri'], 'displayname' => $sabreabook['{DAV:}displayname'], 'id' => $sabreabook['id']]; } $o .= replace_macros(get_markup_template('cdav_widget_addressbook.tpl', 'addon/cdav'), ['$addressbooks_label' => t('Addressbooks'), '$addressbooks' => $addressbooks, '$edit_label' => t('Addressbook name'), '$edit' => t('Edit'), '$create_label' => t('Create new addressbook'), '$create_placeholder' => t('Addressbook Name'), '$create' => t('Create'), '$tools_label' => t('Addressbook Tools'), '$import_label' => t('Import addressbook'), '$import_placeholder' => t('Select an addressbook to import to'), '$upload' => t('Upload')]); return $o; } }