diff --git a/mod/cal.php b/mod/cal.php deleted file mode 100644 index d5b0487a4..000000000 --- a/mod/cal.php +++ /dev/null @@ -1,253 +0,0 @@ -. - * - * The calendar module - * - * This calendar is for profile visitors and contains only the events - * of the profile owner - */ - -use Friendica\App; -use Friendica\Content\Nav; -use Friendica\Content\Widget; -use Friendica\Core\Renderer; -use Friendica\Core\System; -use Friendica\Database\DBA; -use Friendica\DI; -use Friendica\Model\Event; -use Friendica\Model\Item; -use Friendica\Model\User; -use Friendica\Module\BaseProfile; -use Friendica\Network\HTTPException; -use Friendica\Util\DateTimeFormat; -use Friendica\Util\Temporal; - -function cal_init(App $a) -{ - if (DI::config()->get('system', 'block_public') && !DI::userSession()->isAuthenticated()) { - throw new HTTPException\ForbiddenException(DI::l10n()->t('Access denied.')); - } - - if (DI::args()->getArgc() < 2) { - throw new HTTPException\ForbiddenException(DI::l10n()->t('Access denied.')); - } - - Nav::setSelected('events'); - - // if it's a json request abort here becaus we don't - // need the widget data - if (!empty(DI::args()->getArgv()[2]) && (DI::args()->getArgv()[2] === 'json')) { - return; - } - - $owner = User::getOwnerDataByNick(DI::args()->getArgv()[1]); - if (empty($owner)) { - throw new HTTPException\NotFoundException(DI::l10n()->t('User not found.')); - } - - if (empty(DI::page()['aside'])) { - DI::page()['aside'] = ''; - } - - DI::page()['aside'] .= Widget\VCard::getHTML($owner); - DI::page()['aside'] .= Widget\CalendarExport::getHTML($owner['uid']); - - return; -} - -function cal_content(App $a) -{ - $owner = User::getOwnerDataByNick(DI::args()->getArgv()[1]); - if (empty($owner)) { - throw new HTTPException\NotFoundException(DI::l10n()->t('User not found.')); - } - - Nav::setSelected('events'); - - // get the translation strings for the callendar - $i18n = Event::getStrings(); - - DI::page()->registerStylesheet('view/asset/fullcalendar/dist/fullcalendar.min.css'); - DI::page()->registerStylesheet('view/asset/fullcalendar/dist/fullcalendar.print.min.css', 'print'); - DI::page()->registerFooterScript('view/asset/moment/min/moment-with-locales.min.js'); - DI::page()->registerFooterScript('view/asset/fullcalendar/dist/fullcalendar.min.js'); - - $htpl = Renderer::getMarkupTemplate('event_head.tpl'); - DI::page()['htmlhead'] .= Renderer::replaceMacros($htpl, [ - '$module_url' => '/cal/' . $owner['nickname'], - '$modparams' => 2, - '$i18n' => $i18n, - ]); - - $mode = 'view'; - $y = 0; - $m = 0; - $ignored = (!empty($_REQUEST['ignored']) ? intval($_REQUEST['ignored']) : 0); - - // Setup permissions structures - $owner_uid = intval($owner['uid']); - - $contact_id = DI::userSession()->getRemoteContactID($owner['uid']); - - $remote_contact = $contact_id && DBA::exists('contact', ['id' => $contact_id, 'uid' => $owner['uid']]); - - $is_owner = DI::userSession()->getLocalUserId() == $owner['uid']; - - if ($owner['hidewall'] && !$is_owner && !$remote_contact) { - DI::sysmsg()->addNotice(DI::l10n()->t('Access to this profile has been restricted.')); - return; - } - - // get the permissions - $sql_perms = Item::getPermissionsSQLByUserId($owner_uid); - // we only want to have the events of the profile owner - $sql_extra = " AND `event`.`cid` = 0 " . $sql_perms; - - // get the tab navigation bar - $tabs = BaseProfile::getTabsHTML($a, 'cal', false, $owner['nickname'], $owner['hide-friends']); - - // The view mode part is similiar to /mod/events.php - if ($mode == 'view') { - $thisyear = DateTimeFormat::localNow('Y'); - $thismonth = DateTimeFormat::localNow('m'); - if (!$y) { - $y = intval($thisyear); - } - - if (!$m) { - $m = intval($thismonth); - } - - // Put some limits on dates. The PHP date functions don't seem to do so well before 1900. - // An upper limit was chosen to keep search engines from exploring links millions of years in the future. - - if ($y < 1901) { - $y = 1900; - } - - if ($y > 2099) { - $y = 2100; - } - - $nextyear = $y; - $nextmonth = $m + 1; - if ($nextmonth > 12) { - $nextmonth = 1; - $nextyear ++; - } - - $prevyear = $y; - if ($m > 1) { - $prevmonth = $m - 1; - } else { - $prevmonth = 12; - $prevyear --; - } - - $dim = Temporal::getDaysInMonth($y, $m); - $start = sprintf('%d-%d-%d %d:%d:%d', $y, $m, 1, 0, 0, 0); - $finish = sprintf('%d-%d-%d %d:%d:%d', $y, $m, $dim, 23, 59, 59); - - - if (!empty(DI::args()->getArgv()[2]) && (DI::args()->getArgv()[2] === 'json')) { - if (!empty($_GET['start'])) { - $start = $_GET['start']; - } - - if (!empty($_GET['end'])) { - $finish = $_GET['end']; - } - } - - $start = DateTimeFormat::utc($start); - $finish = DateTimeFormat::utc($finish); - - // put the event parametes in an array so we can better transmit them - $event_params = [ - 'event_id' => intval($_GET['id'] ?? 0), - 'start' => $start, - 'finish' => $finish, - 'ignore' => $ignored, - ]; - - // get events by id or by date - if ($event_params['event_id']) { - $r = Event::getListById($owner_uid, $event_params['event_id'], $sql_extra); - } else { - $r = Event::getListByDate($owner_uid, $event_params, $sql_extra); - } - - $links = []; - - if (DBA::isResult($r)) { - $r = Event::sortByDate($r); - foreach ($r as $rr) { - $j = DateTimeFormat::local($rr['start'], 'j'); - if (empty($links[$j])) { - $links[$j] = DI::baseUrl() . '/' . DI::args()->getCommand() . '#link-' . $j; - } - } - } - - // transform the event in a usable array - $events = Event::prepareListForTemplate($r); - - if (!empty(DI::args()->getArgv()[2]) && (DI::args()->getArgv()[2] === 'json')) { - System::jsonExit($events); - } - - // links: array('href', 'text', 'extra css classes', 'title') - if (!empty($_GET['id'])) { - $tpl = Renderer::getMarkupTemplate("event.tpl"); - } else { - $tpl = Renderer::getMarkupTemplate("events_js.tpl"); - } - - // Get rid of dashes in key names, Smarty3 can't handle them - foreach ($events as $key => $event) { - $event_item = []; - foreach ($event['item'] as $k => $v) { - $k = str_replace('-', '_', $k); - $event_item[$k] = $v; - } - $events[$key]['item'] = $event_item; - } - - $o = Renderer::replaceMacros($tpl, [ - '$tabs' => $tabs, - '$title' => DI::l10n()->t('Events'), - '$view' => DI::l10n()->t('View'), - '$previous' => [DI::baseUrl() . "/events/$prevyear/$prevmonth", DI::l10n()->t('Previous'), '', ''], - '$next' => [DI::baseUrl() . "/events/$nextyear/$nextmonth", DI::l10n()->t('Next'), '', ''], - '$calendar' => Temporal::getCalendarTable($y, $m, $links, ' eventcal'), - '$events' => $events, - "today" => DI::l10n()->t("today"), - "month" => DI::l10n()->t("month"), - "week" => DI::l10n()->t("week"), - "day" => DI::l10n()->t("day"), - "list" => DI::l10n()->t("list"), - ]); - - if (!empty($_GET['id'])) { - System::httpExit($o); - } - - return $o; - } -} diff --git a/mod/events.php b/mod/events.php deleted file mode 100644 index b87120672..000000000 --- a/mod/events.php +++ /dev/null @@ -1,540 +0,0 @@ -. - * - * The events module - */ - -use Friendica\App; -use Friendica\Content\Nav; -use Friendica\Content\Widget\CalendarExport; -use Friendica\Core\ACL; -use Friendica\Core\Logger; -use Friendica\Core\Protocol; -use Friendica\Core\Renderer; -use Friendica\Core\System; -use Friendica\Core\Theme; -use Friendica\Core\Worker; -use Friendica\Database\DBA; -use Friendica\DI; -use Friendica\Model\Conversation; -use Friendica\Model\Event; -use Friendica\Model\Item; -use Friendica\Model\Post; -use Friendica\Model\User; -use Friendica\Module\BaseProfile; -use Friendica\Module\Security\Login; -use Friendica\Util\DateTimeFormat; -use Friendica\Util\Strings; -use Friendica\Util\Temporal; -use Friendica\Worker\Delivery; - -function events_init(App $a) -{ - if (!DI::userSession()->getLocalUserId()) { - return; - } - - if (empty(DI::page()['aside'])) { - DI::page()['aside'] = ''; - } - - $cal_widget = CalendarExport::getHTML(DI::userSession()->getLocalUserId()); - - DI::page()['aside'] .= $cal_widget; - - return; -} - -function events_post(App $a) -{ - Logger::debug('post', ['request' => $_REQUEST]); - if (!DI::userSession()->getLocalUserId()) { - return; - } - - $event_id = !empty($_POST['event_id']) ? intval($_POST['event_id']) : 0; - $cid = !empty($_POST['cid']) ? intval($_POST['cid']) : 0; - $uid = DI::userSession()->getLocalUserId(); - - $start_text = Strings::escapeHtml($_REQUEST['start_text'] ?? ''); - $finish_text = Strings::escapeHtml($_REQUEST['finish_text'] ?? ''); - - $nofinish = intval($_POST['nofinish'] ?? 0); - - $share = intval($_POST['share'] ?? 0); - - // The default setting for the `private` field in event_store() is false, so mirror that - $private_event = false; - - $start = DBA::NULL_DATETIME; - $finish = DBA::NULL_DATETIME; - - if ($start_text) { - $start = $start_text; - } - - if ($finish_text) { - $finish = $finish_text; - } - - $start = DateTimeFormat::convert($start, 'UTC', $a->getTimeZone()); - if (!$nofinish) { - $finish = DateTimeFormat::convert($finish, 'UTC', $a->getTimeZone()); - } - - // Don't allow the event to finish before it begins. - // It won't hurt anything, but somebody will file a bug report - // and we'll waste a bunch of time responding to it. Time that - // could've been spent doing something else. - - $summary = trim($_POST['summary'] ?? ''); - $desc = trim($_POST['desc'] ?? ''); - $location = trim($_POST['location'] ?? ''); - $type = 'event'; - - $params = [ - 'summary' => $summary, - 'description' => $desc, - 'location' => $location, - 'start' => $start_text, - 'finish' => $finish_text, - 'nofinish' => $nofinish, - ]; - - $action = ($event_id == '') ? 'new' : 'event/' . $event_id; - $onerror_path = 'events/' . $action . '?' . http_build_query($params, '', '&', PHP_QUERY_RFC3986); - - if (strcmp($finish, $start) < 0 && !$nofinish) { - DI::sysmsg()->addNotice(DI::l10n()->t('Event can not end before it has started.')); - if (intval($_REQUEST['preview'])) { - System::httpExit(DI::l10n()->t('Event can not end before it has started.')); - } - DI::baseUrl()->redirect($onerror_path); - } - - if (!$summary || ($start === DBA::NULL_DATETIME)) { - DI::sysmsg()->addNotice(DI::l10n()->t('Event title and start time are required.')); - if (intval($_REQUEST['preview'])) { - System::httpExit(DI::l10n()->t('Event title and start time are required.')); - } - DI::baseUrl()->redirect($onerror_path); - } - - $self = \Friendica\Model\Contact::getPublicIdByUserId($uid); - - $aclFormatter = DI::aclFormatter(); - - if ($share) { - $user = User::getById($uid, ['allow_cid', 'allow_gid', 'deny_cid', 'deny_gid']); - if (!DBA::isResult($user)) { - return; - } - - $str_contact_allow = isset($_REQUEST['contact_allow']) ? $aclFormatter->toString($_REQUEST['contact_allow']) : $user['allow_cid'] ?? ''; - $str_group_allow = isset($_REQUEST['group_allow']) ? $aclFormatter->toString($_REQUEST['group_allow']) : $user['allow_gid'] ?? ''; - $str_contact_deny = isset($_REQUEST['contact_deny']) ? $aclFormatter->toString($_REQUEST['contact_deny']) : $user['deny_cid'] ?? ''; - $str_group_deny = isset($_REQUEST['group_deny']) ? $aclFormatter->toString($_REQUEST['group_deny']) : $user['deny_gid'] ?? ''; - - $visibility = $_REQUEST['visibility'] ?? ''; - if ($visibility === 'public') { - // The ACL selector introduced in version 2019.12 sends ACL input data even when the Public visibility is selected - $str_contact_allow = $str_group_allow = $str_contact_deny = $str_group_deny = ''; - } else if ($visibility === 'custom') { - // Since we know from the visibility parameter the item should be private, we have to prevent the empty ACL - // case that would make it public. So we always append the author's contact id to the allowed contacts. - // See https://github.com/friendica/friendica/issues/9672 - $str_contact_allow .= $aclFormatter->toString($self); - } - } else { - $str_contact_allow = $aclFormatter->toString($self); - $str_group_allow = $str_contact_deny = $str_group_deny = ''; - } - - // Make sure to set the `private` field as true. This is necessary to - // have the posts show up correctly in Diaspora if an event is created - // as visible only to self at first, but then edited to display to others. - if (strlen($str_group_allow) || strlen($str_contact_allow) || strlen($str_group_deny) || strlen($str_contact_deny)) { - $private_event = true; - } - - $datarray = [ - 'start' => $start, - 'finish' => $finish, - 'summary' => $summary, - 'desc' => $desc, - 'location' => $location, - 'type' => $type, - 'nofinish' => $nofinish, - 'uid' => $uid, - 'cid' => $cid, - 'allow_cid' => $str_contact_allow, - 'allow_gid' => $str_group_allow, - 'deny_cid' => $str_contact_deny, - 'deny_gid' => $str_group_deny, - 'private' => $private_event, - 'id' => $event_id, - ]; - - if (intval($_REQUEST['preview'])) { - System::httpExit(Event::getHTML($datarray)); - } - - $event_id = Event::store($datarray); - - $item = ['network' => Protocol::DFRN, 'protocol' => Conversation::PARCEL_DIRECT, 'direction' => Conversation::PUSH]; - $item = Event::getItemArrayForId($event_id, $item); - if (Item::insert($item)) { - $uri_id = $item['uri-id']; - } else { - $uri_id = 0; - } - - if (!$cid && $uri_id) { - Worker::add(Worker::PRIORITY_HIGH, "Notifier", Delivery::POST, (int)$uri_id, (int)$uid); - } - - DI::baseUrl()->redirect('events'); -} - -function events_content(App $a) -{ - if (!DI::userSession()->getLocalUserId()) { - DI::sysmsg()->addNotice(DI::l10n()->t('Permission denied.')); - return Login::form(); - } - - if (DI::args()->getArgc() == 1) { - $_SESSION['return_path'] = DI::args()->getCommand(); - } - - if ((DI::args()->getArgc() > 2) && (DI::args()->getArgv()[1] === 'ignore') && intval(DI::args()->getArgv()[2])) { - DBA::update('event', ['ignore' => true], ['id' => DI::args()->getArgv()[2], 'uid' => DI::userSession()->getLocalUserId()]); - } - - if ((DI::args()->getArgc() > 2) && (DI::args()->getArgv()[1] === 'unignore') && intval(DI::args()->getArgv()[2])) { - DBA::update('event', ['ignore' => false], ['id' => DI::args()->getArgv()[2], 'uid' => DI::userSession()->getLocalUserId()]); - } - - if ($a->getThemeInfoValue('events_in_profile')) { - Nav::setSelected('home'); - } else { - Nav::setSelected('events'); - } - - // get the translation strings for the callendar - $i18n = Event::getStrings(); - - DI::page()->registerStylesheet('view/asset/fullcalendar/dist/fullcalendar.min.css'); - DI::page()->registerStylesheet('view/asset/fullcalendar/dist/fullcalendar.print.min.css', 'print'); - DI::page()->registerFooterScript('view/asset/moment/min/moment-with-locales.min.js'); - DI::page()->registerFooterScript('view/asset/fullcalendar/dist/fullcalendar.min.js'); - - $htpl = Renderer::getMarkupTemplate('event_head.tpl'); - DI::page()['htmlhead'] .= Renderer::replaceMacros($htpl, [ - '$module_url' => '/events', - '$modparams' => 1, - '$i18n' => $i18n, - ]); - - $o = ''; - $tabs = ''; - // tabs - if ($a->getThemeInfoValue('events_in_profile')) { - $tabs = BaseProfile::getTabsHTML($a, 'events', true, $a->getLoggedInUserNickname(), false); - } - - $mode = 'view'; - $y = 0; - $m = 0; - $ignored = !empty($_REQUEST['ignored']) ? intval($_REQUEST['ignored']) : 0; - - if (DI::args()->getArgc() > 1) { - if (DI::args()->getArgc() > 2 && DI::args()->getArgv()[1] == 'event') { - $mode = 'edit'; - $event_id = intval(DI::args()->getArgv()[2]); - } - if (DI::args()->getArgc() > 2 && DI::args()->getArgv()[1] == 'drop') { - $mode = 'drop'; - $event_id = intval(DI::args()->getArgv()[2]); - } - if (DI::args()->getArgc() > 2 && DI::args()->getArgv()[1] == 'copy') { - $mode = 'copy'; - $event_id = intval(DI::args()->getArgv()[2]); - } - if (DI::args()->getArgv()[1] === 'new') { - $mode = 'new'; - $event_id = 0; - } - if (DI::args()->getArgc() > 2 && intval(DI::args()->getArgv()[1]) && intval(DI::args()->getArgv()[2])) { - $mode = 'view'; - $y = intval(DI::args()->getArgv()[1]); - $m = intval(DI::args()->getArgv()[2]); - } - } - - // The view mode part is similiar to /mod/cal.php - if ($mode == 'view') { - $thisyear = DateTimeFormat::localNow('Y'); - $thismonth = DateTimeFormat::localNow('m'); - if (!$y) { - $y = intval($thisyear); - } - if (!$m) { - $m = intval($thismonth); - } - - // Put some limits on dates. The PHP date functions don't seem to do so well before 1900. - // An upper limit was chosen to keep search engines from exploring links millions of years in the future. - - if ($y < 1901) { - $y = 1900; - } - if ($y > 2099) { - $y = 2100; - } - - $dim = Temporal::getDaysInMonth($y, $m); - $start = sprintf('%d-%d-%d %d:%d:%d', $y, $m, 1, 0, 0, 0); - $finish = sprintf('%d-%d-%d %d:%d:%d', $y, $m, $dim, 23, 59, 59); - - // put the event parametes in an array so we can better transmit them - $event_params = [ - 'event_id' => intval($_GET['id'] ?? 0), - 'start' => $start, - 'finish' => $finish, - 'ignore' => $ignored, - ]; - - // get events by id or by date - if ($event_params['event_id']) { - $r = Event::getListById(DI::userSession()->getLocalUserId(), $event_params['event_id']); - } else { - $r = Event::getListByDate(DI::userSession()->getLocalUserId(), $event_params); - } - - $links = []; - - if (DBA::isResult($r)) { - $r = Event::sortByDate($r); - foreach ($r as $rr) { - $j = DateTimeFormat::local($rr['start'], 'j'); - if (empty($links[$j])) { - $links[$j] = DI::baseUrl() . '/' . DI::args()->getCommand() . '#link-' . $j; - } - } - } - - $events = []; - - // transform the event in a usable array - if (DBA::isResult($r)) { - $r = Event::sortByDate($r); - $events = Event::prepareListForTemplate($r); - } - - if (!empty($_GET['id'])) { - $tpl = Renderer::getMarkupTemplate("event.tpl"); - } else { - $tpl = Renderer::getMarkupTemplate("events_js.tpl"); - } - - // Get rid of dashes in key names, Smarty3 can't handle them - foreach ($events as $key => $event) { - $event_item = []; - foreach ($event['item'] as $k => $v) { - $k = str_replace('-', '_', $k); - $event_item[$k] = $v; - } - $events[$key]['item'] = $event_item; - } - - // ACL blocks are loaded in modals in frio - DI::page()->registerFooterScript(Theme::getPathForFile('asset/typeahead.js/dist/typeahead.bundle.js')); - DI::page()->registerFooterScript(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.js')); - DI::page()->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css')); - DI::page()->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css')); - - $o = Renderer::replaceMacros($tpl, [ - '$tabs' => $tabs, - '$title' => DI::l10n()->t('Events'), - '$view' => DI::l10n()->t('View'), - '$new_event' => [DI::baseUrl() . '/events/new', DI::l10n()->t('Create New Event'), '', ''], - '$previous' => [DI::baseUrl() . '/events/$prevyear/$prevmonth', DI::l10n()->t('Previous'), '', ''], - '$next' => [DI::baseUrl() . '/events/$nextyear/$nextmonth', DI::l10n()->t('Next'), '', ''], - '$calendar' => Temporal::getCalendarTable($y, $m, $links, ' eventcal'), - - '$events' => $events, - - '$today' => DI::l10n()->t('today'), - '$month' => DI::l10n()->t('month'), - '$week' => DI::l10n()->t('week'), - '$day' => DI::l10n()->t('day'), - '$list' => DI::l10n()->t('list'), - ]); - - if (!empty($_GET['id'])) { - System::httpExit($o); - } - - return $o; - } - - if (($mode === 'edit' || $mode === 'copy') && $event_id) { - $orig_event = DBA::selectFirst('event', [], ['id' => $event_id, 'uid' => DI::userSession()->getLocalUserId()]); - } - - // Passed parameters overrides anything found in the DB - if (in_array($mode, ['edit', 'new', 'copy'])) { - $share_checked = ''; - $share_disabled = ''; - - if (empty($orig_event)) { - $orig_event = User::getById(DI::userSession()->getLocalUserId(), ['allow_cid', 'allow_gid', 'deny_cid', 'deny_gid']);; - } elseif ($orig_event['allow_cid'] !== '<' . DI::userSession()->getLocalUserId() . '>' - || $orig_event['allow_gid'] - || $orig_event['deny_cid'] - || $orig_event['deny_gid']) { - $share_checked = ' checked="checked" '; - } - - // In case of an error the browser is redirected back here, with these parameters filled in with the previous values - if (!empty($_REQUEST['nofinish'])) {$orig_event['nofinish'] = $_REQUEST['nofinish'];} - if (!empty($_REQUEST['summary'])) {$orig_event['summary'] = $_REQUEST['summary'];} - if (!empty($_REQUEST['desc'])) {$orig_event['desc'] = $_REQUEST['desc'];} - if (!empty($_REQUEST['location'])) {$orig_event['location'] = $_REQUEST['location'];} - if (!empty($_REQUEST['start'])) {$orig_event['start'] = $_REQUEST['start'];} - if (!empty($_REQUEST['finish'])) {$orig_event['finish'] = $_REQUEST['finish'];} - - $n_checked = (!empty($orig_event['nofinish']) ? ' checked="checked" ' : ''); - - $t_orig = $orig_event['summary'] ?? ''; - $d_orig = $orig_event['desc'] ?? ''; - $l_orig = $orig_event['location'] ?? ''; - $eid = $orig_event['id'] ?? 0; - $cid = $orig_event['cid'] ?? 0; - $uri = $orig_event['uri'] ?? ''; - - if ($cid || $mode === 'edit') { - $share_disabled = 'disabled="disabled"'; - } - - $sdt = $orig_event['start'] ?? 'now'; - $fdt = $orig_event['finish'] ?? 'now'; - - $syear = DateTimeFormat::local($sdt, 'Y'); - $smonth = DateTimeFormat::local($sdt, 'm'); - $sday = DateTimeFormat::local($sdt, 'd'); - - $shour = !empty($orig_event) ? DateTimeFormat::local($sdt, 'H') : '00'; - $sminute = !empty($orig_event) ? DateTimeFormat::local($sdt, 'i') : '00'; - - $fyear = DateTimeFormat::local($fdt, 'Y'); - $fmonth = DateTimeFormat::local($fdt, 'm'); - $fday = DateTimeFormat::local($fdt, 'd'); - - $fhour = !empty($orig_event) ? DateTimeFormat::local($fdt, 'H') : '00'; - $fminute = !empty($orig_event) ? DateTimeFormat::local($fdt, 'i') : '00'; - - if (!$cid && in_array($mode, ['new', 'copy'])) { - $acl = ACL::getFullSelectorHTML(DI::page(), $a->getLoggedInUserId(), false, ACL::getDefaultUserPermissions($orig_event)); - } else { - $acl = ''; - } - - // If we copy an old event, we need to remove the ID and URI - // from the original event. - if ($mode === 'copy') { - $eid = 0; - $uri = ''; - } - - $tpl = Renderer::getMarkupTemplate('event_form.tpl'); - - $o .= Renderer::replaceMacros($tpl, [ - '$post' => DI::baseUrl() . '/events', - '$eid' => $eid, - '$cid' => $cid, - '$uri' => $uri, - - '$title' => DI::l10n()->t('Event details'), - '$desc' => DI::l10n()->t('Starting date and Title are required.'), - '$s_text' => DI::l10n()->t('Event Starts:') . ' *', - '$s_dsel' => Temporal::getDateTimeField( - new DateTime(), - DateTime::createFromFormat('Y', intval($syear) + 5), - DateTime::createFromFormat('Y-m-d H:i', "$syear-$smonth-$sday $shour:$sminute"), - DI::l10n()->t('Event Starts:'), - 'start_text', - true, - true, - '', - '', - true - ), - '$n_text' => DI::l10n()->t('Finish date/time is not known or not relevant'), - '$n_checked' => $n_checked, - '$f_text' => DI::l10n()->t('Event Finishes:'), - '$f_dsel' => Temporal::getDateTimeField( - new DateTime(), - DateTime::createFromFormat('Y', intval($fyear) + 5), - DateTime::createFromFormat('Y-m-d H:i', "$fyear-$fmonth-$fday $fhour:$fminute"), - DI::l10n()->t('Event Finishes:'), - 'finish_text', - true, - true, - 'start_text' - ), - '$d_text' => DI::l10n()->t('Description:'), - '$d_orig' => $d_orig, - '$l_text' => DI::l10n()->t('Location:'), - '$l_orig' => $l_orig, - '$t_text' => DI::l10n()->t('Title:') . ' *', - '$t_orig' => $t_orig, - '$summary' => ['summary', DI::l10n()->t('Title:'), $t_orig, '', '*'], - '$sh_text' => DI::l10n()->t('Share this event'), - '$share' => ['share', DI::l10n()->t('Share this event'), $share_checked, '', $share_disabled], - '$sh_checked' => $share_checked, - '$nofinish' => ['nofinish', DI::l10n()->t('Finish date/time is not known or not relevant'), $n_checked], - '$preview' => DI::l10n()->t('Preview'), - '$acl' => $acl, - '$submit' => DI::l10n()->t('Submit'), - '$basic' => DI::l10n()->t('Basic'), - '$advanced' => DI::l10n()->t('Advanced'), - '$permissions' => DI::l10n()->t('Permissions'), - ]); - - return $o; - } - - // Remove an event from the calendar and its related items - if ($mode === 'drop' && $event_id) { - $ev = Event::getListById(DI::userSession()->getLocalUserId(), $event_id); - - // Delete only real events (no birthdays) - if (DBA::isResult($ev) && $ev[0]['type'] == 'event') { - Item::deleteForUser(['id' => $ev[0]['itemid']], DI::userSession()->getLocalUserId()); - } - - if (Post::exists(['id' => $ev[0]['itemid']])) { - DI::sysmsg()->addNotice(DI::l10n()->t('Failed to remove event')); - } - - DI::baseUrl()->redirect('events'); - } -} diff --git a/src/Content/Nav.php b/src/Content/Nav.php index 23f5dcf07..17b6412df 100644 --- a/src/Content/Nav.php +++ b/src/Content/Nav.php @@ -46,7 +46,7 @@ class Nav 'settings' => null, 'contacts' => null, 'delegation'=> null, - 'events' => null, + 'calendar' => null, 'register' => null ]; @@ -165,7 +165,7 @@ class Nav 'apps' => null, 'community' => null, 'home' => null, - 'events' => null, + 'calendar' => null, 'login' => null, 'logout' => null, 'langselector' => null, @@ -193,7 +193,7 @@ class Nav $nav['usermenu'][] = ['profile/' . $a->getLoggedInUserNickname() . '/profile', DI::l10n()->t('Profile'), '', DI::l10n()->t('Your profile page')]; $nav['usermenu'][] = ['photos/' . $a->getLoggedInUserNickname(), DI::l10n()->t('Photos'), '', DI::l10n()->t('Your photos')]; $nav['usermenu'][] = ['profile/' . $a->getLoggedInUserNickname() . '/media', DI::l10n()->t('Media'), '', DI::l10n()->t('Your postings with media')]; - $nav['usermenu'][] = ['events/', DI::l10n()->t('Events'), '', DI::l10n()->t('Your events')]; + $nav['usermenu'][] = ['calendar/', DI::l10n()->t('Calendar'), '', DI::l10n()->t('Your calendar')]; $nav['usermenu'][] = ['notes/', DI::l10n()->t('Personal notes'), '', DI::l10n()->t('Your personal notes')]; // user info @@ -257,7 +257,7 @@ class Nav } if (DI::userSession()->getLocalUserId()) { - $nav['events'] = ['events', DI::l10n()->t('Events'), '', DI::l10n()->t('Events and Calendar')]; + $nav['calendar'] = ['calendar', DI::l10n()->t('Calendar'), '', DI::l10n()->t('Calendar')]; } $nav['directory'] = [$gdirpath, DI::l10n()->t('Directory'), '', DI::l10n()->t('People directory')]; diff --git a/src/Model/Event.php b/src/Model/Event.php index e2235fa66..6f1b29a6c 100644 --- a/src/Model/Event.php +++ b/src/Model/Event.php @@ -24,15 +24,17 @@ namespace Friendica\Model; use Friendica\Content\Text\BBCode; use Friendica\Core\Hook; use Friendica\Core\Logger; -use Friendica\Core\Protocol; use Friendica\Core\Renderer; use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; +use Friendica\Network\HTTPException\NotFoundException; +use Friendica\Network\HTTPException\UnauthorizedException; use Friendica\Protocol\Activity; use Friendica\Util\DateTimeFormat; use Friendica\Util\Map; use Friendica\Util\Strings; +use Friendica\Util\Temporal; use Friendica\Util\XML; /** @@ -494,22 +496,65 @@ class Event return array_values($dates2); } + /** + * Returns the owner array of a given nickname + * Additionally, it can check if the owner array is selectable + * + * @param string $nickname + * @param bool $check + * + * @return array the owner array + * @throws NotFoundException The given nickname does not exist + * @throws UnauthorizedException The access for the given nickname is restricted + */ + public static function getOwnerForNickname(string $nickname, bool $check = true): array + { + $owner = User::getOwnerDataByNick($nickname); + if (empty($owner)) { + throw new NotFoundException(DI::l10n()->t('User not found.')); + } + + if ($check) { + $contact_id = DI::userSession()->getRemoteContactID($owner['uid']); + + $remote_contact = $contact_id && DBA::exists('contact', ['id' => $contact_id, 'uid' => $owner['uid']]); + + $is_owner = DI::userSession()->getLocalUserId() == $owner['uid']; + + if ($owner['hidewall'] && !$is_owner && !$remote_contact) { + throw new UnauthorizedException(DI::l10n()->t('Access to this profile has been restricted.')); + } + } + + return $owner; + } + /** * Get an event by its event ID. * - * @param int $owner_uid The User ID of the owner of the event - * @param int $event_id The ID of the event in the event table - * @param string $sql_extra + * @param int $owner_uid The User ID of the owner of the event + * @param int $event_id The ID of the event in the event table + * @param string|null $nickname a possible nickname to search for instead of the owner uid * @return array Query result * @throws \Exception */ - public static function getListById(int $owner_uid, int $event_id, string $sql_extra = ''): array + public static function getByIdAndUid(int $owner_uid, int $event_id, string $nickname = null): array { - $return = []; + if (!empty($nickname)) { + $owner = static::getOwnerForNickname($nickname, true); + $owner_uid = $owner['uid']; - // Ownly allow events if there is a valid owner_id. + // get the permissions + $sql_perms = Item::getPermissionsSQLByUserId($owner_uid); + // we only want to have the events of the profile owner + $sql_extra = " AND `event`.`cid` = 0 " . $sql_perms; + } else { + $sql_extra = ""; + } + + // Only allow events if there is a valid owner_id. if ($owner_uid == 0) { - return $return; + return []; } // Query for the event by event id @@ -518,34 +563,57 @@ class Event WHERE `event`.`uid` = ? AND `event`.`id` = ? $sql_extra", $owner_uid, $event_id)); - if (DBA::isResult($events)) { - $return = self::removeDuplicates($events); + if (empty($events)) { + throw new NotFoundException(DI::l10n()->t('Event not found.')); + } else { + $events = self::removeDuplicates($events); + return $events[0]; } - - return $return; } /** * Get all events in a specific time frame. * - * @param int $owner_uid The User ID of the owner of the events. - * @param array $event_params An associative array with - * int 'ignore' => - * string 'start' => Start time of the timeframe. - * string 'finish' => Finish time of the timeframe. - * - * @param string $sql_extra Additional sql conditions (e.g. permission request). + * @param int $owner_uid The User ID of the owner of the events. + * @param string|null $start Start time of the timeframe. + * @param string|null $finish Finish time of the timeframe. + * @param bool $ignore + * @param string|null $nickname * * @return array Query results. - * @throws \Exception + * @throws NotFoundException + * @throws UnauthorizedException */ - public static function getListByDate(int $owner_uid, array $event_params, string $sql_extra = ''): array + public static function getListByDate(int $owner_uid, string $start = null, string $finish = null, bool $ignore = false, string $nickname = null): array { - $return = []; + if (!empty($nickname)) { + $owner = static::getOwnerForNickname($nickname); + $owner_uid = $owner['uid']; + + // get the permissions + $sql_perms = Item::getPermissionsSQLByUserId($owner_uid); + // we only want to have the events of the profile owner + $sql_extra = " AND `event`.`cid` = 0 " . $sql_perms; + } else { + $sql_extra = ""; + } // Only allow events if there is a valid owner_id. if ($owner_uid == 0) { - return $return; + return []; + } + + if (empty($start) || empty($finish)) { + + $y = intval(DateTimeFormat::localNow('Y')); + $m = intval(DateTimeFormat::localNow('m')); + + if (empty($start)) { + $start = sprintf('%d-%d-%d %d:%d:%d', $y, $m, 1, 0, 0, 0); + } else { + $dim = Temporal::getDaysInMonth($y, $m); + $finish = sprintf('%d-%d-%d %d:%d:%d', $y, $m, $dim, 23, 59, 59); + } } // Query for the event by date. @@ -554,15 +622,12 @@ class Event WHERE `event`.`uid` = ? AND `event`.`ignore` = ? AND (`finish` >= ? OR (`nofinish` AND `start` >= ?)) AND `start` <= ? " . $sql_extra, - $owner_uid, $event_params['ignore'], - $event_params['start'], $event_params['start'], $event_params['finish'] + $owner_uid, $ignore, + $start, $start, $finish )); - if (DBA::isResult($events)) { - $return = self::removeDuplicates($events); - } - - return $return; + $events = self::removeDuplicates($events ?? []); + return self::sortByDate($events); } /** @@ -577,77 +642,86 @@ class Event { $event_list = []; - $last_date = ''; - $fmt = DI::l10n()->t('l, F j'); foreach ($event_result as $event) { - $item = Post::selectFirst(['plink', 'author-name', 'author-network', 'author-id', 'author-avatar', 'author-link', 'private', 'uri-id'], ['id' => $event['itemid']]); - if (!DBA::isResult($item)) { - // Using default values when no item had been found - $item = ['plink' => '', 'author-name' => '', 'author-avatar' => '', 'author-link' => '', 'private' => Item::PUBLIC, 'uri-id' => ($event['uri-id'] ?? 0)]; - } - - $event = array_merge($event, $item); - - $start = DateTimeFormat::local($event['start'], 'c'); - $j = DateTimeFormat::local($event['start'], 'j'); - $day = DateTimeFormat::local($event['start'], $fmt); - $day = DI::l10n()->getDay($day); - - if ($event['nofinish']) { - $end = null; - } else { - $end = DateTimeFormat::local($event['finish'], 'c'); - } - - $is_first = ($day !== $last_date); - - $last_date = $day; - - // Show edit and drop actions only if the user is the owner of the event and the event - // is a real event (no bithdays). - $edit = null; - $copy = null; - $drop = null; - if (DI::userSession()->getLocalUserId() && DI::userSession()->getLocalUserId() == $event['uid'] && $event['type'] == 'event') { - $edit = !$event['cid'] ? [DI::baseUrl() . '/events/event/' . $event['id'], DI::l10n()->t('Edit event') , '', ''] : null; - $copy = !$event['cid'] ? [DI::baseUrl() . '/events/copy/' . $event['id'] , DI::l10n()->t('Duplicate event'), '', ''] : null; - $drop = [DI::baseUrl() . '/events/drop/' . $event['id'] , DI::l10n()->t('Delete event') , '', '']; - } - - $title = BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['summary'])); - if (!$title) { - list($title, $_trash) = explode(" $event['id'], - 'start' => $start, - 'end' => $end, - 'allDay' => false, - 'title' => $title, - 'j' => $j, - 'd' => $day, - 'edit' => $edit, - 'drop' => $drop, - 'copy' => $copy, - 'is_first' => $is_first, - 'item' => $event, - 'html' => $html, - 'plink' => Item::getPlink($event), - ]; + $event_list[] = static::prepareForItem($event); } return $event_list; } + /** + * Convert an one event in an array which could be used by the events template. + * + * @param array $event Event query array. + * @return array Event array for the template. + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \ImagickException + */ + public static function prepareForItem(array $event): array + { + $fmt = DI::l10n()->t('l, F j'); + + $item = Post::selectFirst(['plink', 'author-name', 'author-network', 'author-id', 'author-avatar', 'author-link', 'private', 'uri-id'], ['id' => $event['itemid']]); + if (empty($item)) { + // Using default values when no item had been found + $item = ['plink' => '', 'author-name' => '', 'author-avatar' => '', 'author-link' => '', 'private' => Item::PUBLIC, 'uri-id' => ($event['uri-id'] ?? 0)]; + } + + $event = array_merge($event, $item); + + $start = DateTimeFormat::local($event['start'], 'c'); + $j = DateTimeFormat::local($event['start'], 'j'); + $day = DateTimeFormat::local($event['start'], $fmt); + $day = DI::l10n()->getDay($day); + + if ($event['nofinish']) { + $end = null; + } else { + $end = DateTimeFormat::local($event['finish'], 'c'); + } + + // Show edit and drop actions only if the user is the owner of the event and the event + // is a real event (no bithdays). + $edit = null; + $copy = null; + $drop = null; + if (DI::userSession()->getLocalUserId() && DI::userSession()->getLocalUserId() == $event['uid'] && $event['type'] == 'event') { + $edit = !$event['cid'] ? ['calendar/event/edit/' . $event['id'], DI::l10n()->t('Edit event') , '', ''] : null; + $copy = !$event['cid'] ? ['calendar/event/copy/' . $event['id'] , DI::l10n()->t('Duplicate event'), '', ''] : null; + $drop = ['calendar/api/delete/' . $event['id'] , DI::l10n()->t('Delete event') , '', '']; + } + + $title = BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['summary'])); + if (!$title) { + [$title, $_trash] = explode(" $event['id'], + 'start' => $start, + 'end' => $end, + 'allDay' => false, + 'title' => $title, + 'j' => $j, + 'd' => $day, + 'edit' => $edit, + 'drop' => $drop, + 'copy' => $copy, + 'item' => $event, + 'html' => $html, + 'plink' => Item::getPlink($event), + ]; + } + /** * Format event to export format (ical/csv). * @@ -1018,4 +1092,9 @@ class Event // Check if self::store() was success return (self::store($values) > 0); } + + public static function setIgnore(int $uid, int $eventId, bool $ignore = true) + { + DBA::update('event', ['ignore' => $ignore], ['id' => $eventId, 'uid' => $uid]); + } } diff --git a/src/Module/BaseNotifications.php b/src/Module/BaseNotifications.php index a011961b6..d0c0ae362 100644 --- a/src/Module/BaseNotifications.php +++ b/src/Module/BaseNotifications.php @@ -90,11 +90,11 @@ abstract class BaseNotifications extends BaseModule */ abstract public function getNotifications(); - public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IHandleUserSessions $userSession, array $server, array $parameters = []) + public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IHandleUserSessions $session, array $server, array $parameters = []) { parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters); - if (!$userSession->getLocalUserId()) { + if (!$session->getLocalUserId()) { throw new ForbiddenException($this->t('Permission denied.')); } diff --git a/src/Module/BaseProfile.php b/src/Module/BaseProfile.php index 3937fa07d..16ba224c9 100644 --- a/src/Module/BaseProfile.php +++ b/src/Module/BaseProfile.php @@ -81,23 +81,23 @@ class BaseProfile extends BaseModule // the calendar link for the full featured events calendar if ($is_owner && $a->getThemeInfoValue('events_in_profile')) { $tabs[] = [ - 'label' => DI::l10n()->t('Events'), - 'url' => DI::baseUrl() . '/events', - 'sel' => $current == 'events' ? 'active' : '', - 'title' => DI::l10n()->t('Events and Calendar'), - 'id' => 'events-tab', - 'accesskey' => 'e', + 'label' => DI::l10n()->t('Calendar'), + 'url' => DI::baseUrl() . '/calendar', + 'sel' => $current == 'calendar' ? 'active' : '', + 'title' => DI::l10n()->t('Calendar'), + 'id' => 'calendar-tab', + 'accesskey' => 'c', ]; // if the user is not the owner of the calendar we only show a calendar // with the public events of the calendar owner } elseif (!$is_owner) { $tabs[] = [ - 'label' => DI::l10n()->t('Events'), - 'url' => DI::baseUrl() . '/cal/' . $nickname, - 'sel' => $current == 'cal' ? 'active' : '', - 'title' => DI::l10n()->t('Events and Calendar'), - 'id' => 'events-tab', - 'accesskey' => 'e', + 'label' => DI::l10n()->t('Calendar'), + 'url' => DI::baseUrl() . '/calendar/show/' . $nickname, + 'sel' => $current == 'calendar' ? 'active' : '', + 'title' => DI::l10n()->t('Calendar'), + 'id' => 'calendar-tab', + 'accesskey' => 'c', ]; } diff --git a/src/Module/Calendar/Event/API.php b/src/Module/Calendar/Event/API.php new file mode 100644 index 000000000..174c0b3af --- /dev/null +++ b/src/Module/Calendar/Event/API.php @@ -0,0 +1,276 @@ +. + * + */ + +namespace Friendica\Module\Calendar\Event; + +use Friendica\App; +use Friendica\BaseModule; +use Friendica\Core\L10n; +use Friendica\Core\Protocol; +use Friendica\Core\Session\Capability\IHandleUserSessions; +use Friendica\Core\System; +use Friendica\Core\Worker; +use Friendica\Database\DBA; +use Friendica\Model\Contact; +use Friendica\Model\Conversation; +use Friendica\Model\Event; +use Friendica\Model\Item; +use Friendica\Model\Post; +use Friendica\Model\User; +use Friendica\Module\Response; +use Friendica\Navigation\SystemMessages; +use Friendica\Network\HTTPException\BadRequestException; +use Friendica\Network\HTTPException\UnauthorizedException; +use Friendica\Util\ACLFormatter; +use Friendica\Util\DateTimeFormat; +use Friendica\Util\Profiler; +use Friendica\Util\Strings; +use Friendica\Worker\Delivery; +use Psr\Log\LoggerInterface; + +/** + * Basic API class for events + * currently supports create, delete, ignore, unignore + * + * @todo: make create/update as REST-call instead of POST + */ +class API extends BaseModule +{ + const ACTION_CREATE = 'create'; + const ACTION_DELETE = 'delete'; + const ACTION_IGNORE = 'ignore'; + const ACTION_UNIGNORE = 'unignore'; + + const ALLOWED_ACTIONS = [ + self::ACTION_CREATE, + self::ACTION_DELETE, + self::ACTION_IGNORE, + self::ACTION_UNIGNORE, + ]; + + /** @var IHandleUserSessions */ + protected $session; + /** @var SystemMessages */ + protected $sysMessages; + /** @var ACLFormatter */ + protected $aclFormatter; + /** @var string */ + protected $timezone; + + public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IHandleUserSessions $session, SystemMessages $sysMessages, ACLFormatter $aclFormatter, App $app, array $server, array $parameters = []) + { + parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters); + + $this->session = $session; + $this->sysMessages = $sysMessages; + $this->aclFormatter = $aclFormatter; + $this->timezone = $app->getTimeZone(); + + if (!$this->session->getLocalUserId()) { + throw new UnauthorizedException($this->t('Permission denied.')); + } + } + + protected function post(array $request = []) + { + $this->createEvent($request); + } + + protected function rawContent(array $request = []) + { + if (empty($this->parameters['action']) || !in_array($this->parameters['action'], self::ALLOWED_ACTIONS)) { + throw new BadRequestException($this->t('Invalid Request')); + } + + // CREATE is done per POSt, so nothing to do left + if ($this->parameters['action'] === static::ACTION_CREATE) { + return; + } + + if (empty($this->parameters['id'])) { + throw new BadRequestException($this->t('Event id is missing.')); + } + + $returnPath = $request['return_path'] ?? 'calendar'; + + switch ($this->parameters['action']) { + case self::ACTION_IGNORE: + Event::setIgnore($this->session->getLocalUserId(), $this->parameters['id']); + break; + case self::ACTION_UNIGNORE: + Event::setIgnore($this->session->getLocalUserId(), $this->parameters['id'], false); + break; + case self::ACTION_DELETE: + // Remove an event from the calendar and its related items + $event = Event::getByIdAndUid($this->session->getLocalUserId(), $this->parameters['id']); + + // Delete only real events (no birthdays) + if (!empty($event) && $event['type'] == 'event') { + Item::deleteForUser(['id' => $event['itemid']], $this->session->getLocalUserId()); + } + + if (Post::exists(['id' => $event['itemid']])) { + $this->sysMessages->addNotice($this->t('Failed to remove event')); + } + break; + default: + throw new BadRequestException($this->t('Invalid Request')); + } + + $this->baseUrl->redirect($returnPath); + } + + protected function createEvent(array $request) + { + $eventId = !empty($request['event_id']) ? intval($request['event_id']) : 0; + $uid = (int)$this->session->getLocalUserId(); + $cid = !empty($request['cid']) ? intval($request['cid']) : 0; + + $strStartDateTime = Strings::escapeHtml($request['start_text'] ?? ''); + $strFinishDateTime = Strings::escapeHtml($request['finish_text'] ?? ''); + + $noFinish = intval($request['nofinish'] ?? 0); + + $share = intval($request['share'] ?? 0); + $isPreview = intval($request['preview'] ?? 0); + + $start = DateTimeFormat::convert($strStartDateTime ?? DBA::NULL_DATETIME, $this->timezone); + if (!$noFinish) { + $finish = DateTimeFormat::convert($strFinishDateTime ?? DBA::NULL_DATETIME, 'UTC', $this->timezone); + } else { + $finish = DBA::NULL_DATETIME; + } + + // Don't allow the event to finish before it begins. + // It won't hurt anything, but somebody will file a bug report, + // and we'll waste a bunch of time responding to it. Time that + // could've been spent doing something else. + + $summary = trim($request['summary'] ?? ''); + $desc = trim($request['desc'] ?? ''); + $location = trim($request['location'] ?? ''); + $type = 'event'; + + $params = [ + 'summary' => $summary, + 'description' => $desc, + 'location' => $location, + 'start' => $strStartDateTime, + 'finish' => $strFinishDateTime, + 'nofinish' => $noFinish, + ]; + + $action = empty($eventId) ? 'new' : 'edit/' . $eventId; + $redirectOnError = 'calendar/event/' . $action . '?' . http_build_query($params, '', '&', PHP_QUERY_RFC3986); + + if (strcmp($finish, $start) < 0 && !$noFinish) { + if ($isPreview) { + System::httpExit($this->t('Event can not end before it has started.')); + } else { + $this->sysMessages->addNotice($this->t('Event can not end before it has started.')); + $this->baseUrl->redirect($redirectOnError); + } + } + + if (empty($summary) || ($start === DBA::NULL_DATETIME)) { + if ($isPreview) { + System::httpExit($this->t('Event title and start time are required.')); + } else { + $this->sysMessages->addNotice($this->t('Event title and start time are required.')); + $this->baseUrl->redirect($redirectOnError); + } + } + + $self = Contact::getPublicIdByUserId($uid); + + $aclFormatter = $this->aclFormatter; + + if ($share) { + $user = User::getById($uid, ['allow_cid', 'allow_gid', 'deny_cid', 'deny_gid']); + if (empty($user)) { + $this->logger->warning('Cannot find user for an event.', ['uid' => $uid, 'event' => $eventId]); + $this->response->setStatus(500); + return; + } + + $strAclContactAllow = isset($request['contact_allow']) ? $aclFormatter->toString($request['contact_allow']) : $user['allow_cid'] ?? ''; + $strAclGroupAllow = isset($request['group_allow']) ? $aclFormatter->toString($request['group_allow']) : $user['allow_gid'] ?? ''; + $strContactDeny = isset($request['contact_deny']) ? $aclFormatter->toString($request['contact_deny']) : $user['deny_cid'] ?? ''; + $strGroupDeny = isset($request['group_deny']) ? $aclFormatter->toString($request['group_deny']) : $user['deny_gid'] ?? ''; + + $visibility = $request['visibility'] ?? ''; + if ($visibility === 'public') { + // The ACL selector introduced in version 2019.12 sends ACL input data even when the Public visibility is selected + $strAclContactAllow = $strAclGroupAllow = $strContactDeny = $strGroupDeny = ''; + } elseif ($visibility === 'custom') { + // Since we know from the visibility parameter the item should be private, we have to prevent the empty ACL + // case that would make it public. So we always append the author's contact id to the allowed contacts. + // See https://github.com/friendica/friendica/issues/9672 + $strAclContactAllow .= $aclFormatter->toString($self); + } + } else { + $strAclContactAllow = $aclFormatter->toString($self); + $strAclGroupAllow = ''; + $strContactDeny = ''; + $strGroupDeny = ''; + } + + $datarray = [ + 'start' => $start, + 'finish' => $finish, + 'summary' => $summary, + 'desc' => $desc, + 'location' => $location, + 'type' => $type, + 'nofinish' => $noFinish, + 'uid' => $uid, + 'cid' => $cid, + 'allow_cid' => $strAclContactAllow, + 'allow_gid' => $strAclGroupAllow, + 'deny_cid' => $strContactDeny, + 'deny_gid' => $strGroupDeny, + 'id' => $eventId, + ]; + + if (intval($request['preview'])) { + System::httpExit(Event::getHTML($datarray)); + } + + $eventId = Event::store($datarray); + + $newItem = Event::getItemArrayForId($eventId, [ + 'network' => Protocol::DFRN, + 'protocol' => Conversation::PARCEL_DIRECT, + 'direction' => Conversation::PUSH + ]); + if (Item::insert($newItem)) { + $uriId = (int)$newItem['uri-id']; + } else { + $uriId = 0; + } + + if (!$cid && $uriId) { + Worker::add(Worker::PRIORITY_HIGH, 'Notifier', Delivery::POST, $uriId, $uid); + } + + $this->baseUrl->redirect('calendar'); + } +} diff --git a/src/Module/Calendar/Event/Form.php b/src/Module/Calendar/Event/Form.php new file mode 100644 index 000000000..2e8dec7fa --- /dev/null +++ b/src/Module/Calendar/Event/Form.php @@ -0,0 +1,256 @@ +. + * + */ + +namespace Friendica\Module\Calendar\Event; + +use Friendica\App; +use Friendica\BaseModule; +use Friendica\Content\Widget\CalendarExport; +use Friendica\Core\ACL; +use Friendica\Core\L10n; +use Friendica\Core\Renderer; +use Friendica\Core\Session\Capability\IHandleUserSessions; +use Friendica\Model\Event as EventModel; +use Friendica\Model\User; +use Friendica\Module\Response; +use Friendica\Module\Security\Login; +use Friendica\Navigation\SystemMessages; +use Friendica\Network\HTTPException\BadRequestException; +use Friendica\Util\ACLFormatter; +use Friendica\Util\DateTimeFormat; +use Friendica\Util\Profiler; +use Friendica\Util\Temporal; +use Psr\Log\LoggerInterface; + +/** + * The editor-view of an event + */ +class Form extends BaseModule +{ + const MODE_NEW = 'new'; + const MODE_EDIT = 'edit'; + const MODE_COPY = 'copy'; + + const ALLOWED_MODES = [ + self::MODE_NEW, + self::MODE_EDIT, + self::MODE_COPY, + ]; + + /** @var IHandleUserSessions */ + protected $session; + /** @var SystemMessages */ + protected $sysMessages; + /** @var ACLFormatter */ + protected $aclFormatter; + /** @var App\Page */ + protected $page; + + public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IHandleUserSessions $session, SystemMessages $sysMessages, ACLFormatter $aclFormatter, App\Page $page, array $server, array $parameters = []) + { + parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters); + + $this->session = $session; + $this->sysMessages = $sysMessages; + $this->aclFormatter = $aclFormatter; + $this->page = $page; + } + + protected function content(array $request = []): string + { + if (empty($this->parameters['mode']) || !in_array($this->parameters['mode'], self::ALLOWED_MODES)) { + throw new BadRequestException($this->t('Invalid Request')); + } + + if (!$this->session->getLocalUserId()) { + $this->sysMessages->addNotice($this->t('Permission denied.')); + return Login::form(); + } + + $mode = $this->parameters['mode']; + + if (($mode === self::MODE_EDIT || $mode === self::MODE_COPY)) { + if (empty($this->parameters['id'])) { + throw new BadRequestException('Invalid Request'); + } + $orig_event = EventModel::getByIdAndUid($this->session->getLocalUserId(), $this->parameters['id']); + if (empty($orig_event)) { + throw new BadRequestException('Invalid Request'); + } + } + + if ($mode === self::MODE_NEW) { + $this->session->set('return_path', $this->args->getCommand()); + } + + // get the translation strings for the calendar + $i18n = EventModel::getStrings(); + + $this->page->registerStylesheet('view/asset/fullcalendar/dist/fullcalendar.min.css'); + $this->page->registerStylesheet('view/asset/fullcalendar/dist/fullcalendar.print.min.css', 'print'); + $this->page->registerFooterScript('view/asset/moment/min/moment-with-locales.min.js'); + $this->page->registerFooterScript('view/asset/fullcalendar/dist/fullcalendar.min.js'); + + $htpl = Renderer::getMarkupTemplate('calendar/calendar_head.tpl'); + + $this->page['htmlhead'] .= Renderer::replaceMacros($htpl, [ + '$calendar_api' => $this->baseUrl . '/calendar/api/get', + '$event_api' => $this->baseUrl . '/calendar/event/show', + '$modparams' => 2, + '$i18n' => $i18n, + ]); + + $share_checked = ''; + $share_disabled = ''; + + if (empty($orig_event)) { + $orig_event = User::getById($this->session->getLocalUserId(), + ['allow_cid', 'allow_gid', 'deny_cid', 'deny_gid']); + } elseif ($orig_event['allow_cid'] !== '<' . $this->session->getLocalUserId() . '>' + || $orig_event['allow_gid'] + || $orig_event['deny_cid'] + || $orig_event['deny_gid']) { + $share_checked = ' checked="checked" '; + } + + // In case of an error the browser is redirected back here, with these parameters filled in with the previous values + if (!empty($request['nofinish'])) { + $orig_event['nofinish'] = $request['nofinish']; + } + if (!empty($request['summary'])) { + $orig_event['summary'] = $request['summary']; + } + if (!empty($request['desc'])) { + $orig_event['desc'] = $request['desc']; + } + if (!empty($request['location'])) { + $orig_event['location'] = $request['location']; + } + if (!empty($request['start'])) { + $orig_event['start'] = $request['start']; + } + if (!empty($request['finish'])) { + $orig_event['finish'] = $request['finish']; + } + + $n_checked = (!empty($orig_event['nofinish']) ? ' checked="checked" ' : ''); + + $t_orig = $orig_event['summary'] ?? ''; + $d_orig = $orig_event['desc'] ?? ''; + $l_orig = $orig_event['location'] ?? ''; + $eid = $orig_event['id'] ?? 0; + $cid = $orig_event['cid'] ?? 0; + $uri = $orig_event['uri'] ?? ''; + + if ($cid || $mode === 'edit') { + $share_disabled = 'disabled="disabled"'; + } + + $sdt = $orig_event['start'] ?? 'now'; + $fdt = $orig_event['finish'] ?? 'now'; + + $syear = DateTimeFormat::local($sdt, 'Y'); + $smonth = DateTimeFormat::local($sdt, 'm'); + $sday = DateTimeFormat::local($sdt, 'd'); + + $shour = !empty($orig_event) ? DateTimeFormat::local($sdt, 'H') : '00'; + $sminute = !empty($orig_event) ? DateTimeFormat::local($sdt, 'i') : '00'; + + $fyear = DateTimeFormat::local($fdt, 'Y'); + $fmonth = DateTimeFormat::local($fdt, 'm'); + $fday = DateTimeFormat::local($fdt, 'd'); + + $fhour = !empty($orig_event) ? DateTimeFormat::local($fdt, 'H') : '00'; + $fminute = !empty($orig_event) ? DateTimeFormat::local($fdt, 'i') : '00'; + + if (!$cid && in_array($mode, [self::MODE_NEW, self::MODE_COPY])) { + $acl = ACL::getFullSelectorHTML($this->page, $this->session->getLocalUserId(), false, ACL::getDefaultUserPermissions($orig_event)); + } else { + $acl = ''; + } + + // If we copy an old event, we need to remove the ID and URI + // from the original event. + if ($mode === self::MODE_COPY) { + $eid = 0; + $uri = ''; + } + + $this->page['aside'] .= CalendarExport::getHTML($this->session->getLocalUserId()); + + $tpl = Renderer::getMarkupTemplate('calendar/event_form.tpl'); + + return Renderer::replaceMacros($tpl, [ + '$post' => 'calendar/api/create', + '$eid' => $eid, + '$cid' => $cid, + '$uri' => $uri, + + '$title' => $this->t('Event details'), + '$desc' => $this->t('Starting date and Title are required.'), + '$s_text' => $this->t('Event Starts:') . ' *', + '$s_dsel' => Temporal::getDateTimeField( + new \DateTime(), + \DateTime::createFromFormat('Y', intval($syear) + 5), + \DateTime::createFromFormat('Y-m-d H:i', "$syear-$smonth-$sday $shour:$sminute"), + $this->t('Event Starts:'), + 'start_text', + true, + true, + '', + '', + true + ), + + '$n_text' => $this->t('Finish date/time is not known or not relevant'), + '$n_checked' => $n_checked, + '$f_text' => $this->t('Event Finishes:'), + '$f_dsel' => Temporal::getDateTimeField( + new \DateTime(), + \DateTime::createFromFormat('Y', intval($fyear) + 5), + \DateTime::createFromFormat('Y-m-d H:i', "$fyear-$fmonth-$fday $fhour:$fminute"), + $this->t('Event Finishes:'), + 'finish_text', + true, + true, + 'start_text' + ), + + '$d_text' => $this->t('Description:'), + '$d_orig' => $d_orig, + '$l_text' => $this->t('Location:'), + '$l_orig' => $l_orig, + '$t_text' => $this->t('Title:') . ' *', + '$t_orig' => $t_orig, + '$summary' => ['summary', $this->t('Title:'), $t_orig, '', '*'], + '$sh_text' => $this->t('Share this event'), + '$share' => ['share', $this->t('Share this event'), $share_checked, '', $share_disabled], + '$sh_checked' => $share_checked, + '$nofinish' => ['nofinish', $this->t('Finish date/time is not known or not relevant'), $n_checked], + '$preview' => $this->t('Preview'), + '$acl' => $acl, + '$submit' => $this->t('Submit'), + '$basic' => $this->t('Basic'), + '$advanced' => $this->t('Advanced'), + '$permissions' => $this->t('Permissions'), + ]); + } +} diff --git a/src/Module/Calendar/Json.php b/src/Module/Calendar/Event/Get.php similarity index 54% rename from src/Module/Calendar/Json.php rename to src/Module/Calendar/Event/Get.php index 08481e6ac..f0824d15a 100644 --- a/src/Module/Calendar/Json.php +++ b/src/Module/Calendar/Event/Get.php @@ -19,90 +19,58 @@ * */ -namespace Friendica\Module\Calendar; +namespace Friendica\Module\Calendar\Event; +use Friendica\App; +use Friendica\Core\L10n; +use Friendica\Core\Session\Capability\IHandleUserSessions; use Friendica\Core\System; -use Friendica\Database\DBA; -use Friendica\DI; use Friendica\Model\Event; use Friendica\Model\Item; use Friendica\Model\Post; +use Friendica\Module\Response; use Friendica\Network\HTTPException; use Friendica\Util\DateTimeFormat; -use Friendica\Util\Temporal; +use Friendica\Util\Profiler; +use Psr\Log\LoggerInterface; -class Json extends \Friendica\BaseModule +/** + * GET-Controller for event + * returns the result as JSON + */ +class Get extends \Friendica\BaseModule { + /** @var IHandleUserSessions */ + protected $session; + + public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IHandleUserSessions $session, array $server, array $parameters = []) + { + parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters); + + $this->session = $session; + } + protected function rawContent(array $request = []) { - if (!DI::userSession()->getLocalUserId()) { + if (!$this->session->getLocalUserId()) { throw new HTTPException\UnauthorizedException(); } - $y = intval(DateTimeFormat::localNow('Y')); - $m = intval(DateTimeFormat::localNow('m')); - - // Put some limit on dates. The PHP date functions don't seem to do so well before 1900. - if ($y < 1901) { - $y = 1900; - } - - $dim = Temporal::getDaysInMonth($y, $m); - $start = sprintf('%d-%d-%d %d:%d:%d', $y, $m, 1, 0, 0, 0); - $finish = sprintf('%d-%d-%d %d:%d:%d', $y, $m, $dim, 23, 59, 59); - - if (!empty($request['start'])) { - $start = $request['start']; - } - - if (!empty($request['end'])) { - $finish = $request['end']; - } - - // put the event parametes in an array so we can better transmit them - $event_params = [ - 'event_id' => intval($request['id'] ?? 0), - 'start' => $start, - 'finish' => $finish, - 'ignore' => 0, - ]; - // get events by id or by date - if ($event_params['event_id']) { - $r = Event::getListById(DI::userSession()->getLocalUserId(), $event_params['event_id']); + if (!empty($request['id'])) { + $events = [Event::getByIdAndUid($this->session->getLocalUserId(), $request['id'], $this->parameters['nickname'] ?? null)]; } else { - $r = Event::getListByDate(DI::userSession()->getLocalUserId(), $event_params); + $events = Event::getListByDate($this->session->getLocalUserId(), $request['start'] ?? '', $request['end'] ?? '', false, $this->parameters['nickname'] ?? null); } - $links = []; - - if (DBA::isResult($r)) { - $r = Event::sortByDate($r); - foreach ($r as $rr) { - $j = DateTimeFormat::utc($rr['start'], 'j'); - if (empty($links[$j])) { - $links[$j] = DI::baseUrl() . '/' . DI::args()->getCommand() . '#link-' . $j; - } - } - } - - $events = []; - - // transform the event in a usable array - if (DBA::isResult($r)) { - $events = Event::sortByDate($r); - - $events = self::map($events); - } - - System::jsonExit($events); + System::jsonExit($events ? self::map($events) : []); } private static function map(array $events): array { return array_map(function ($event) { $item = Post::selectFirst(['plink', 'author-name', 'author-avatar', 'author-link', 'private', 'uri-id'], ['id' => $event['itemid']]); - if (!DBA::isResult($item)) { + if (empty($item)) { // Using default values when no item had been found $item = ['plink' => '', 'author-name' => '', 'author-avatar' => '', 'author-link' => '', 'private' => Item::PUBLIC, 'uri-id' => ($event['uri-id'] ?? 0)]; } diff --git a/src/Module/Calendar/Event/Show.php b/src/Module/Calendar/Event/Show.php new file mode 100644 index 000000000..85d4d47a7 --- /dev/null +++ b/src/Module/Calendar/Event/Show.php @@ -0,0 +1,79 @@ +. + * + */ + +namespace Friendica\Module\Calendar\Event; + +use Friendica\App; +use Friendica\BaseModule; +use Friendica\Core\L10n; +use Friendica\Core\Renderer; +use Friendica\Core\Session\Capability\IHandleUserSessions; +use Friendica\Core\System; +use Friendica\Model\Event; +use Friendica\Module\Response; +use Friendica\Network\HTTPException; +use Friendica\Util\Profiler; +use Psr\Log\LoggerInterface; + +/** + * Displays one specific event in a
container + */ +class Show extends BaseModule +{ + /** @var IHandleUserSessions */ + protected $session; + + public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IHandleUserSessions $session, array $server, array $parameters = []) + { + parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters); + + $this->session = $session; + } + + protected function rawContent(array $request = []) + { + if (!$this->session->getLocalUserId()) { + throw new HTTPException\UnauthorizedException($this->t('Permission denied.')); + } + + $event = Event::getByIdAndUid($this->session->getLocalUserId(), (int)$this->parameters['id'] ?? 0, $this->parameters['nickname'] ?? ''); + + if (empty($event)) { + throw new HTTPException\NotFoundException($this->t('Event not found.')); + } + + $tplEvent = Event::prepareForItem($event); + + $event_item = []; + foreach ($tplEvent['item'] as $k => $v) { + $event_item[str_replace('-', '_', $k)] = $v; + } + $tplEvent['item'] = $event_item; + + $tpl = Renderer::getMarkupTemplate('calendar/event.tpl'); + + $o = Renderer::replaceMacros($tpl, [ + '$event' => $tplEvent, + ]); + + System::httpExit($o); + } +} diff --git a/src/Module/Calendar/Export.php b/src/Module/Calendar/Export.php index 30a1bc44d..be47ba718 100644 --- a/src/Module/Calendar/Export.php +++ b/src/Module/Calendar/Export.php @@ -58,6 +58,10 @@ class Export extends BaseModule protected function rawContent(array $request = []) { + if (!$this->session->getLocalUserId()) { + throw new HTTPException\UnauthorizedException($this->t('Permission denied.')); + } + $owner = User::getByNickname($this->parameters['nickname'], ['uid']); if (empty($owner)) { throw new HTTPException\NotFoundException($this->t('User not found.')); @@ -78,9 +82,9 @@ class Export extends BaseModule // If it is the own calendar return to the events page // otherwise to the profile calendar page if ($this->session->getLocalUserId() === $ownerUid) { - $returnPath = 'events'; + $returnPath = 'calendar'; } else { - $returnPath = 'events/' . $this->parameters['nickname']; + $returnPath = 'calendar/show/' . $this->parameters['nickname']; } $this->baseUrl->redirect($returnPath); diff --git a/src/Module/Calendar/Show.php b/src/Module/Calendar/Show.php new file mode 100644 index 000000000..9bcc004fd --- /dev/null +++ b/src/Module/Calendar/Show.php @@ -0,0 +1,134 @@ +. + * + */ + +namespace Friendica\Module\Calendar; + +use Friendica\App; +use Friendica\BaseModule; +use Friendica\Content\Nav; +use Friendica\Content\Widget; +use Friendica\Core\L10n; +use Friendica\Core\Renderer; +use Friendica\Core\Session\Capability\IHandleUserSessions; +use Friendica\Core\Theme; +use Friendica\Model\Event; +use Friendica\Module\BaseProfile; +use Friendica\Module\Response; +use Friendica\Module\Security\Login; +use Friendica\Navigation\SystemMessages; +use Friendica\Util\Profiler; +use Psr\Log\LoggerInterface; + +class Show extends BaseModule +{ + /** @var IHandleUserSessions */ + protected $session; + /** @var SystemMessages */ + protected $sysMessages; + /** @var App\Page */ + protected $page; + /** @var App */ + protected $app; + + public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IHandleUserSessions $session, SystemMessages $sysMessages, App\Page $page, App $app, array $server, array $parameters = []) + { + parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters); + + $this->session = $session; + $this->sysMessages = $sysMessages; + $this->page = $page; + $this->app = $app; + } + + protected function content(array $request = []): string + { + if (!$this->session->getLocalUserId()) { + $this->sysMessages->addNotice($this->t('Permission denied.')); + return Login::form(); + } + + // get the translation strings for the calendar + $i18n = Event::getStrings(); + + $this->page->registerStylesheet('view/asset/fullcalendar/dist/fullcalendar.min.css'); + $this->page->registerStylesheet('view/asset/fullcalendar/dist/fullcalendar.print.min.css', 'print'); + $this->page->registerFooterScript('view/asset/moment/min/moment-with-locales.min.js'); + $this->page->registerFooterScript('view/asset/fullcalendar/dist/fullcalendar.min.js'); + + $htpl = Renderer::getMarkupTemplate('calendar/calendar_head.tpl'); + + $this->page['htmlhead'] .= Renderer::replaceMacros($htpl, [ + '$calendar_api' => 'calendar/api/get' . (!empty($this->parameters['nickname']) ? '/' . $this->parameters['nickname'] : ''), + '$event_api' => 'calendar/event/show' . (!empty($this->parameters['nickname']) ? '/' . $this->parameters['nickname'] : ''), + '$modparams' => 2, + '$i18n' => $i18n, + ]); + + $tabs = ''; + + if (empty($this->parameters['nickname'])) { + if ($this->app->getThemeInfoValue('events_in_profile')) { + Nav::setSelected('home'); + } else { + Nav::setSelected('calendar'); + } + + // tabs + if ($this->app->getThemeInfoValue('events_in_profile')) { + $tabs = BaseProfile::getTabsHTML($this->app, 'calendar', true, $this->app->getLoggedInUserNickname(), false); + } + + $this->page['aside'] .= Widget\CalendarExport::getHTML($this->session->getLocalUserId()); + } else { + $owner = Event::getOwnerForNickname($this->parameters['nickname'], true); + + Nav::setSelected('calendar'); + + // get the tab navigation bar + $tabs = BaseProfile::getTabsHTML($this->app, 'calendar', false, $owner['nickname'], $owner['hide-friends']); + + $this->page['aside'] .= Widget\VCard::getHTML($owner); + $this->page['aside'] .= Widget\CalendarExport::getHTML($owner['uid']); + } + + // ACL blocks are loaded in modals in frio + $this->page->registerFooterScript(Theme::getPathForFile('asset/typeahead.js/dist/typeahead.bundle.js')); + $this->page->registerFooterScript(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.js')); + $this->page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css')); + $this->page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css')); + + $tpl = Renderer::getMarkupTemplate("calendar/calendar.tpl"); + $o = Renderer::replaceMacros($tpl, [ + '$tabs' => $tabs, + '$title' => $this->t('Events'), + '$view' => $this->t('View'), + '$new_event' => ['calendar/event/new', $this->t('Create New Event'), '', ''], + + '$today' => $this->t('today'), + '$month' => $this->t('month'), + '$week' => $this->t('week'), + '$day' => $this->t('day'), + '$list' => $this->t('list'), + ]); + + return $o; + } +} diff --git a/src/Module/Manifest.php b/src/Module/Manifest.php index a554f7752..a19f94675 100644 --- a/src/Module/Manifest.php +++ b/src/Module/Manifest.php @@ -60,8 +60,8 @@ class Manifest extends BaseModule 'url' => '/contact' ], [ - 'name' => 'Events', - 'url' => '/events' + 'name' => 'Calendar', + 'url' => '/calendar' ] ] ]; diff --git a/src/Object/Post.php b/src/Object/Post.php index ddbc1a736..459405cdb 100644 --- a/src/Object/Post.php +++ b/src/Object/Post.php @@ -218,7 +218,7 @@ class Post if (DI::userSession()->getLocalUserId()) { if (Strings::compareLink(DI::session()->get('my_url'), $item['author-link'])) { if ($item['event-id'] != 0) { - $edpost = ['events/event/' . $item['event-id'], DI::l10n()->t('Edit')]; + $edpost = ['calendar/event/edit/' . $item['event-id'], DI::l10n()->t('Edit')]; } else { $edpost = ['editpost/' . $item['id'], DI::l10n()->t('Edit')]; } diff --git a/static/routes.config.php b/static/routes.config.php index d72302da2..773b8d253 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -365,8 +365,18 @@ return [ '/bookmarklet' => [Module\Bookmarklet::class, [R::GET]], - '/calendar/{nickname}/export[/{format}]' => [Module\Calendar\Export::class, [R::GET]], - '/calendar/json' => [Module\Calendar\Json::class, [R::GET]], + '/calendar' => [ + '[/]' => [Module\Calendar\Show::class, [R::GET ]], + '/show/{nickname}' => [Module\Calendar\Show::class, [R::GET ]], + '/export/{nickname}[/{format:csv|ical}]' => [Module\Calendar\Export::class, [R::GET ]], + '/api/{action:ignore|unignore|delete}/{id:\d+}' => [Module\Calendar\Event\API::class, [R::GET ]], + '/api/{action:create}' => [Module\Calendar\Event\API::class, [ R::POST]], + '/api/get[/{nickname}]' => [Module\Calendar\Event\Get::class, [R::GET ]], + '/event/show/{id:\d+}' => [Module\Calendar\Event\Show::class, [R::GET ]], + '/event/show/{nickname}/{id:\d+}' => [Module\Calendar\Event\Show::class, [R::GET ]], + '/event/{mode:new}' => [Module\Calendar\Event\Form::class, [R::GET ]], + '/event/{mode:edit|copy}/{id:\d+}' => [Module\Calendar\Event\Form::class, [R::GET ]], + ], '/community[/{content}]' => [Module\Conversation\Community::class, [R::GET]], diff --git a/view/lang/C/messages.po b/view/lang/C/messages.po index 7fcf6e217..359fbebd0 100644 --- a/view/lang/C/messages.po +++ b/view/lang/C/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 2022.12-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-11-07 11:21+0100\n" +"POT-Creation-Date: 2022-11-07 19:28+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,75 +18,6 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" -#: mod/cal.php:44 mod/cal.php:48 mod/redir.php:35 mod/redir.php:176 -#: src/Module/Contact/Follow.php:69 src/Module/Conversation/Community.php:193 -#: src/Module/Debug/ItemBody.php:38 src/Module/Diaspora/Receive.php:57 -#: src/Module/Item/Follow.php:41 src/Module/Item/Ignore.php:41 -#: src/Module/Item/Pin.php:41 src/Module/Item/Pin.php:56 -#: src/Module/Item/Star.php:42 -msgid "Access denied." -msgstr "" - -#: mod/cal.php:61 mod/cal.php:78 mod/photos.php:68 mod/photos.php:139 -#: mod/photos.php:793 src/Model/Profile.php:234 -#: src/Module/Calendar/Export.php:63 src/Module/Feed.php:72 -#: src/Module/HCard.php:51 src/Module/Profile/Common.php:40 -#: src/Module/Profile/Common.php:51 src/Module/Profile/Contacts.php:39 -#: src/Module/Profile/Contacts.php:49 src/Module/Profile/Media.php:38 -#: src/Module/Profile/Photos/Upload.php:87 -#: src/Module/Profile/RemoteFollow.php:71 src/Module/Profile/Status.php:58 -#: src/Module/Register.php:267 -msgid "User not found." -msgstr "" - -#: mod/cal.php:113 mod/display.php:259 src/Module/Profile/Profile.php:93 -#: src/Module/Profile/Profile.php:108 src/Module/Profile/Status.php:109 -#: src/Module/Update/Profile.php:55 -msgid "Access to this profile has been restricted." -msgstr "" - -#: mod/cal.php:234 mod/events.php:375 src/Content/Nav.php:196 -#: src/Content/Nav.php:260 src/Module/BaseProfile.php:84 -#: src/Module/BaseProfile.php:95 view/theme/frio/theme.php:241 -#: view/theme/frio/theme.php:245 -msgid "Events" -msgstr "" - -#: mod/cal.php:235 mod/events.php:376 -msgid "View" -msgstr "" - -#: mod/cal.php:236 mod/events.php:378 -msgid "Previous" -msgstr "" - -#: mod/cal.php:237 mod/events.php:379 src/Module/Install.php:214 -msgid "Next" -msgstr "" - -#: mod/cal.php:240 mod/events.php:384 src/Model/Event.php:460 -msgid "today" -msgstr "" - -#: mod/cal.php:241 mod/events.php:385 src/Model/Event.php:461 -#: src/Util/Temporal.php:341 -msgid "month" -msgstr "" - -#: mod/cal.php:242 mod/events.php:386 src/Model/Event.php:462 -#: src/Util/Temporal.php:342 -msgid "week" -msgstr "" - -#: mod/cal.php:243 mod/events.php:387 src/Model/Event.php:463 -#: src/Util/Temporal.php:343 -msgid "day" -msgstr "" - -#: mod/cal.php:244 mod/events.php:388 -msgid "list" -msgstr "" - #: mod/display.php:140 mod/photos.php:797 #: src/Module/Conversation/Community.php:187 src/Module/Directory.php:48 #: src/Module/Search/Index.php:64 @@ -97,23 +28,31 @@ msgstr "" msgid "The requested item doesn't exist or has been deleted." msgstr "" +#: mod/display.php:259 src/Model/Event.php:525 +#: src/Module/Profile/Profile.php:93 src/Module/Profile/Profile.php:108 +#: src/Module/Profile/Status.php:109 src/Module/Update/Profile.php:55 +msgid "Access to this profile has been restricted." +msgstr "" + #: mod/display.php:364 msgid "The feed for this item is unavailable." msgstr "" -#: mod/editpost.php:38 mod/events.php:218 mod/item.php:181 mod/item.php:186 -#: mod/item.php:870 mod/message.php:69 mod/message.php:114 mod/notes.php:44 +#: mod/editpost.php:38 mod/item.php:181 mod/item.php:186 mod/item.php:870 +#: mod/message.php:69 mod/message.php:114 mod/notes.php:44 #: mod/ostatus_subscribe.php:33 mod/photos.php:159 mod/photos.php:886 #: mod/repair_ostatus.php:31 mod/settings.php:40 mod/settings.php:50 #: mod/settings.php:156 src/Module/Attach.php:56 src/Module/BaseApi.php:94 -#: src/Module/BaseNotifications.php:98 src/Module/Contact/Advanced.php:60 -#: src/Module/Contact/Follow.php:86 src/Module/Contact/Follow.php:158 -#: src/Module/Contact/Suggestions.php:54 src/Module/Contact/Unfollow.php:66 -#: src/Module/Contact/Unfollow.php:80 src/Module/Contact/Unfollow.php:112 -#: src/Module/Delegation.php:118 src/Module/FollowConfirm.php:38 -#: src/Module/FriendSuggest.php:57 src/Module/Group.php:40 -#: src/Module/Group.php:83 src/Module/Invite.php:42 src/Module/Invite.php:131 -#: src/Module/Notifications/Notification.php:76 +#: src/Module/BaseNotifications.php:98 src/Module/Calendar/Event/API.php:88 +#: src/Module/Calendar/Event/Form.php:84 src/Module/Calendar/Event/Show.php:54 +#: src/Module/Calendar/Export.php:62 src/Module/Calendar/Show.php:64 +#: src/Module/Contact/Advanced.php:60 src/Module/Contact/Follow.php:86 +#: src/Module/Contact/Follow.php:158 src/Module/Contact/Suggestions.php:54 +#: src/Module/Contact/Unfollow.php:66 src/Module/Contact/Unfollow.php:80 +#: src/Module/Contact/Unfollow.php:112 src/Module/Delegation.php:118 +#: src/Module/FollowConfirm.php:38 src/Module/FriendSuggest.php:57 +#: src/Module/Group.php:40 src/Module/Group.php:83 src/Module/Invite.php:42 +#: src/Module/Invite.php:131 src/Module/Notifications/Notification.php:76 #: src/Module/Notifications/Notification.php:107 #: src/Module/Profile/Attachment/Upload.php:97 src/Module/Profile/Common.php:55 #: src/Module/Profile/Contacts.php:55 src/Module/Profile/Photos/Upload.php:108 @@ -245,9 +184,10 @@ msgstr "" msgid "Example: bob@example.com, mary@example.com" msgstr "" -#: mod/editpost.php:128 mod/events.php:514 mod/photos.php:1334 -#: mod/photos.php:1390 mod/photos.php:1464 src/Content/Conversation.php:386 -#: src/Module/Item/Compose.php:199 src/Object/Post.php:997 +#: mod/editpost.php:128 mod/photos.php:1334 mod/photos.php:1390 +#: mod/photos.php:1464 src/Content/Conversation.php:386 +#: src/Module/Calendar/Event/Form.php:247 src/Module/Item/Compose.php:199 +#: src/Object/Post.php:997 msgid "Preview" msgstr "" @@ -306,8 +246,8 @@ msgstr "" msgid "Browser" msgstr "" -#: mod/editpost.php:145 mod/events.php:519 mod/photos.php:934 -#: mod/photos.php:1288 src/Content/Conversation.php:373 +#: mod/editpost.php:145 mod/photos.php:934 mod/photos.php:1288 +#: src/Content/Conversation.php:373 src/Module/Calendar/Event/Form.php:252 msgid "Permissions" msgstr "" @@ -315,112 +255,6 @@ msgstr "" msgid "Open Compose page" msgstr "" -#: mod/events.php:124 mod/events.php:126 -msgid "Event can not end before it has started." -msgstr "" - -#: mod/events.php:132 mod/events.php:134 -msgid "Event title and start time are required." -msgstr "" - -#: mod/events.php:377 -msgid "Create New Event" -msgstr "" - -#: mod/events.php:475 src/Module/Admin/Logs/View.php:97 -msgid "Event details" -msgstr "" - -#: mod/events.php:476 -msgid "Starting date and Title are required." -msgstr "" - -#: mod/events.php:477 mod/events.php:482 -msgid "Event Starts:" -msgstr "" - -#: mod/events.php:477 mod/events.php:507 -#: src/Module/Admin/Blocklist/Server/Add.php:136 -#: src/Module/Admin/Blocklist/Server/Add.php:138 -#: src/Module/Admin/Blocklist/Server/Import.php:128 -#: src/Module/Admin/Blocklist/Server/Index.php:82 -#: src/Module/Admin/Blocklist/Server/Index.php:83 -#: src/Module/Admin/Blocklist/Server/Index.php:111 -#: src/Module/Admin/Blocklist/Server/Index.php:112 -#: src/Module/Admin/Item/Delete.php:69 src/Module/Debug/Probe.php:59 -#: src/Module/Install.php:207 src/Module/Install.php:240 -#: src/Module/Install.php:245 src/Module/Install.php:264 -#: src/Module/Install.php:275 src/Module/Install.php:280 -#: src/Module/Install.php:286 src/Module/Install.php:291 -#: src/Module/Install.php:305 src/Module/Install.php:320 -#: src/Module/Install.php:347 src/Module/Register.php:148 -#: src/Module/Security/TwoFactor/Verify.php:101 -#: src/Module/Settings/TwoFactor/Index.php:140 -#: src/Module/Settings/TwoFactor/Verify.php:154 -msgid "Required" -msgstr "" - -#: mod/events.php:490 mod/events.php:513 -msgid "Finish date/time is not known or not relevant" -msgstr "" - -#: mod/events.php:492 mod/events.php:497 -msgid "Event Finishes:" -msgstr "" - -#: mod/events.php:503 src/Module/Profile/Profile.php:171 -#: src/Module/Settings/Profile/Index.php:247 -msgid "Description:" -msgstr "" - -#: mod/events.php:505 src/Content/Widget/VCard.php:104 src/Model/Event.php:80 -#: src/Model/Event.php:107 src/Model/Event.php:469 src/Model/Event.php:918 -#: src/Model/Profile.php:373 src/Module/Contact/Profile.php:370 -#: src/Module/Directory.php:147 src/Module/Notifications/Introductions.php:187 -#: src/Module/Profile/Profile.php:193 -msgid "Location:" -msgstr "" - -#: mod/events.php:507 mod/events.php:509 -msgid "Title:" -msgstr "" - -#: mod/events.php:510 mod/events.php:511 -msgid "Share this event" -msgstr "" - -#: mod/events.php:516 mod/message.php:204 mod/message.php:359 -#: mod/photos.php:916 mod/photos.php:1020 mod/photos.php:1292 -#: mod/photos.php:1333 mod/photos.php:1389 mod/photos.php:1463 -#: src/Module/Admin/Item/Source.php:60 src/Module/Contact/Advanced.php:132 -#: src/Module/Contact/Profile.php:328 -#: src/Module/Debug/ActivityPubConversion.php:140 -#: src/Module/Debug/Babel.php:313 src/Module/Debug/Localtime.php:64 -#: src/Module/Debug/Probe.php:54 src/Module/Debug/WebFinger.php:51 -#: src/Module/Delegation.php:147 src/Module/FriendSuggest.php:145 -#: src/Module/Install.php:252 src/Module/Install.php:294 -#: src/Module/Install.php:331 src/Module/Invite.php:178 -#: src/Module/Item/Compose.php:189 src/Module/Profile/Profile.php:246 -#: src/Module/Profile/UnkMail.php:156 src/Module/Settings/Profile/Index.php:231 -#: src/Object/Post.php:986 view/theme/duepuntozero/config.php:85 -#: view/theme/frio/config.php:171 view/theme/quattro/config.php:87 -#: view/theme/vier/config.php:135 -msgid "Submit" -msgstr "" - -#: mod/events.php:517 src/Module/Profile/Profile.php:247 -msgid "Basic" -msgstr "" - -#: mod/events.php:518 src/Module/Admin/Site.php:439 src/Module/Contact.php:477 -#: src/Module/Profile/Profile.php:248 -msgid "Advanced" -msgstr "" - -#: mod/events.php:535 -msgid "Failed to remove event" -msgstr "" - #: mod/fbrowser.php:61 src/Content/Nav.php:194 src/Module/BaseProfile.php:64 #: view/theme/frio/theme.php:239 msgid "Photos" @@ -673,6 +507,25 @@ msgstr "" msgid "Your message:" msgstr "" +#: mod/message.php:204 mod/message.php:359 mod/photos.php:916 +#: mod/photos.php:1020 mod/photos.php:1292 mod/photos.php:1333 +#: mod/photos.php:1389 mod/photos.php:1463 src/Module/Admin/Item/Source.php:60 +#: src/Module/Calendar/Event/Form.php:249 src/Module/Contact/Advanced.php:132 +#: src/Module/Contact/Profile.php:328 +#: src/Module/Debug/ActivityPubConversion.php:140 +#: src/Module/Debug/Babel.php:313 src/Module/Debug/Localtime.php:64 +#: src/Module/Debug/Probe.php:54 src/Module/Debug/WebFinger.php:51 +#: src/Module/Delegation.php:147 src/Module/FriendSuggest.php:145 +#: src/Module/Install.php:252 src/Module/Install.php:294 +#: src/Module/Install.php:331 src/Module/Invite.php:178 +#: src/Module/Item/Compose.php:189 src/Module/Profile/Profile.php:246 +#: src/Module/Profile/UnkMail.php:156 src/Module/Settings/Profile/Index.php:231 +#: src/Object/Post.php:986 view/theme/duepuntozero/config.php:85 +#: view/theme/frio/config.php:171 view/theme/quattro/config.php:87 +#: view/theme/vier/config.php:135 +msgid "Submit" +msgstr "" + #: mod/message.php:225 msgid "No messages." msgstr "" @@ -781,6 +634,18 @@ msgstr "" msgid "Keep this window open until done." msgstr "" +#: mod/photos.php:68 mod/photos.php:139 mod/photos.php:793 +#: src/Model/Event.php:514 src/Model/Profile.php:234 +#: src/Module/Calendar/Export.php:67 src/Module/Feed.php:72 +#: src/Module/HCard.php:51 src/Module/Profile/Common.php:40 +#: src/Module/Profile/Common.php:51 src/Module/Profile/Contacts.php:39 +#: src/Module/Profile/Contacts.php:49 src/Module/Profile/Media.php:38 +#: src/Module/Profile/Photos/Upload.php:87 +#: src/Module/Profile/RemoteFollow.php:71 src/Module/Profile/Status.php:58 +#: src/Module/Register.php:267 +msgid "User not found." +msgstr "" + #: mod/photos.php:107 src/Module/BaseProfile.php:67 msgid "Photo Albums" msgstr "" @@ -1035,6 +900,14 @@ msgstr "" msgid "View Album" msgstr "" +#: mod/redir.php:35 mod/redir.php:176 src/Module/Contact/Follow.php:69 +#: src/Module/Conversation/Community.php:193 src/Module/Debug/ItemBody.php:38 +#: src/Module/Diaspora/Receive.php:57 src/Module/Item/Follow.php:41 +#: src/Module/Item/Ignore.php:41 src/Module/Item/Pin.php:41 +#: src/Module/Item/Pin.php:56 src/Module/Item/Star.php:42 +msgid "Access denied." +msgstr "" + #: mod/redir.php:50 mod/redir.php:103 msgid "Bad Request." msgstr "" @@ -2218,8 +2091,16 @@ msgstr "" msgid "Your postings with media" msgstr "" +#: src/Content/Nav.php:196 src/Content/Nav.php:260 +#: src/Module/BaseProfile.php:84 src/Module/BaseProfile.php:87 +#: src/Module/BaseProfile.php:95 src/Module/BaseProfile.php:98 +#: src/Module/Settings/Display.php:205 view/theme/frio/theme.php:241 +#: view/theme/frio/theme.php:245 +msgid "Calendar" +msgstr "" + #: src/Content/Nav.php:196 view/theme/frio/theme.php:241 -msgid "Your events" +msgid "Your calendar" msgstr "" #: src/Content/Nav.php:197 @@ -2296,11 +2177,6 @@ msgstr "" msgid "Conversations on this and other servers" msgstr "" -#: src/Content/Nav.php:260 src/Module/BaseProfile.php:87 -#: src/Module/BaseProfile.php:98 view/theme/frio/theme.php:245 -msgid "Events and Calendar" -msgstr "" - #: src/Content/Nav.php:263 msgid "Directory" msgstr "" @@ -2670,6 +2546,15 @@ msgstr "" msgid "Matrix:" msgstr "" +#: src/Content/Widget/VCard.php:104 src/Model/Event.php:82 +#: src/Model/Event.php:109 src/Model/Event.php:471 src/Model/Event.php:992 +#: src/Model/Profile.php:373 src/Module/Calendar/Event/Form.php:238 +#: src/Module/Contact/Profile.php:370 src/Module/Directory.php:147 +#: src/Module/Notifications/Introductions.php:187 +#: src/Module/Profile/Profile.php:193 +msgid "Location:" +msgstr "" + #: src/Content/Widget/VCard.php:107 src/Model/Profile.php:471 #: src/Module/Notifications/Introductions.php:201 msgid "Network:" @@ -3046,142 +2931,142 @@ msgstr "" msgid "Could not connect to database." msgstr "" -#: src/Core/L10n.php:403 src/Model/Event.php:428 +#: src/Core/L10n.php:403 src/Model/Event.php:430 #: src/Module/Settings/Display.php:183 msgid "Monday" msgstr "" -#: src/Core/L10n.php:403 src/Model/Event.php:429 +#: src/Core/L10n.php:403 src/Model/Event.php:431 #: src/Module/Settings/Display.php:184 msgid "Tuesday" msgstr "" -#: src/Core/L10n.php:403 src/Model/Event.php:430 +#: src/Core/L10n.php:403 src/Model/Event.php:432 #: src/Module/Settings/Display.php:185 msgid "Wednesday" msgstr "" -#: src/Core/L10n.php:403 src/Model/Event.php:431 +#: src/Core/L10n.php:403 src/Model/Event.php:433 #: src/Module/Settings/Display.php:186 msgid "Thursday" msgstr "" -#: src/Core/L10n.php:403 src/Model/Event.php:432 +#: src/Core/L10n.php:403 src/Model/Event.php:434 #: src/Module/Settings/Display.php:187 msgid "Friday" msgstr "" -#: src/Core/L10n.php:403 src/Model/Event.php:433 +#: src/Core/L10n.php:403 src/Model/Event.php:435 #: src/Module/Settings/Display.php:188 msgid "Saturday" msgstr "" -#: src/Core/L10n.php:403 src/Model/Event.php:427 +#: src/Core/L10n.php:403 src/Model/Event.php:429 #: src/Module/Settings/Display.php:182 msgid "Sunday" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:448 +#: src/Core/L10n.php:407 src/Model/Event.php:450 msgid "January" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:449 +#: src/Core/L10n.php:407 src/Model/Event.php:451 msgid "February" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:450 +#: src/Core/L10n.php:407 src/Model/Event.php:452 msgid "March" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:451 +#: src/Core/L10n.php:407 src/Model/Event.php:453 msgid "April" msgstr "" -#: src/Core/L10n.php:407 src/Core/L10n.php:426 src/Model/Event.php:439 +#: src/Core/L10n.php:407 src/Core/L10n.php:426 src/Model/Event.php:441 msgid "May" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:452 +#: src/Core/L10n.php:407 src/Model/Event.php:454 msgid "June" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:453 +#: src/Core/L10n.php:407 src/Model/Event.php:455 msgid "July" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:454 +#: src/Core/L10n.php:407 src/Model/Event.php:456 msgid "August" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:455 +#: src/Core/L10n.php:407 src/Model/Event.php:457 msgid "September" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:456 +#: src/Core/L10n.php:407 src/Model/Event.php:458 msgid "October" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:457 +#: src/Core/L10n.php:407 src/Model/Event.php:459 msgid "November" msgstr "" -#: src/Core/L10n.php:407 src/Model/Event.php:458 +#: src/Core/L10n.php:407 src/Model/Event.php:460 msgid "December" msgstr "" -#: src/Core/L10n.php:422 src/Model/Event.php:420 +#: src/Core/L10n.php:422 src/Model/Event.php:422 msgid "Mon" msgstr "" -#: src/Core/L10n.php:422 src/Model/Event.php:421 +#: src/Core/L10n.php:422 src/Model/Event.php:423 msgid "Tue" msgstr "" -#: src/Core/L10n.php:422 src/Model/Event.php:422 +#: src/Core/L10n.php:422 src/Model/Event.php:424 msgid "Wed" msgstr "" -#: src/Core/L10n.php:422 src/Model/Event.php:423 +#: src/Core/L10n.php:422 src/Model/Event.php:425 msgid "Thu" msgstr "" -#: src/Core/L10n.php:422 src/Model/Event.php:424 +#: src/Core/L10n.php:422 src/Model/Event.php:426 msgid "Fri" msgstr "" -#: src/Core/L10n.php:422 src/Model/Event.php:425 +#: src/Core/L10n.php:422 src/Model/Event.php:427 msgid "Sat" msgstr "" -#: src/Core/L10n.php:422 src/Model/Event.php:419 +#: src/Core/L10n.php:422 src/Model/Event.php:421 msgid "Sun" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:435 +#: src/Core/L10n.php:426 src/Model/Event.php:437 msgid "Jan" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:436 +#: src/Core/L10n.php:426 src/Model/Event.php:438 msgid "Feb" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:437 +#: src/Core/L10n.php:426 src/Model/Event.php:439 msgid "Mar" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:438 +#: src/Core/L10n.php:426 src/Model/Event.php:440 msgid "Apr" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:440 +#: src/Core/L10n.php:426 src/Model/Event.php:442 msgid "Jun" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:441 +#: src/Core/L10n.php:426 src/Model/Event.php:443 msgid "Jul" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:442 +#: src/Core/L10n.php:426 src/Model/Event.php:444 msgid "Aug" msgstr "" @@ -3189,15 +3074,15 @@ msgstr "" msgid "Sep" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:444 +#: src/Core/L10n.php:426 src/Model/Event.php:446 msgid "Oct" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:445 +#: src/Core/L10n.php:426 src/Model/Event.php:447 msgid "Nov" msgstr "" -#: src/Core/L10n.php:426 src/Model/Event.php:446 +#: src/Core/L10n.php:426 src/Model/Event.php:448 msgid "Dec" msgstr "" @@ -3439,74 +3324,97 @@ msgstr "" msgid "Unable to retrieve contact information." msgstr "" -#: src/Model/Event.php:52 +#: src/Model/Event.php:54 msgid "l F d, Y \\@ g:i A \\G\\M\\TP (e)" msgstr "" -#: src/Model/Event.php:73 src/Model/Event.php:90 src/Model/Event.php:467 -#: src/Model/Event.php:900 +#: src/Model/Event.php:75 src/Model/Event.php:92 src/Model/Event.php:469 +#: src/Model/Event.php:974 msgid "Starts:" msgstr "" -#: src/Model/Event.php:76 src/Model/Event.php:96 src/Model/Event.php:468 -#: src/Model/Event.php:904 +#: src/Model/Event.php:78 src/Model/Event.php:98 src/Model/Event.php:470 +#: src/Model/Event.php:978 msgid "Finishes:" msgstr "" -#: src/Model/Event.php:417 +#: src/Model/Event.php:419 msgid "all-day" msgstr "" -#: src/Model/Event.php:443 +#: src/Model/Event.php:445 msgid "Sept" msgstr "" -#: src/Model/Event.php:465 +#: src/Model/Event.php:462 src/Module/Calendar/Show.php:124 +msgid "today" +msgstr "" + +#: src/Model/Event.php:463 src/Module/Calendar/Show.php:125 +#: src/Util/Temporal.php:341 +msgid "month" +msgstr "" + +#: src/Model/Event.php:464 src/Module/Calendar/Show.php:126 +#: src/Util/Temporal.php:342 +msgid "week" +msgstr "" + +#: src/Model/Event.php:465 src/Module/Calendar/Show.php:127 +#: src/Util/Temporal.php:343 +msgid "day" +msgstr "" + +#: src/Model/Event.php:467 msgid "No events to display" msgstr "" -#: src/Model/Event.php:581 +#: src/Model/Event.php:567 src/Module/Calendar/Event/Show.php:64 +msgid "Event not found." +msgstr "" + +#: src/Model/Event.php:662 msgid "l, F j" msgstr "" -#: src/Model/Event.php:612 +#: src/Model/Event.php:689 msgid "Edit event" msgstr "" -#: src/Model/Event.php:613 +#: src/Model/Event.php:690 msgid "Duplicate event" msgstr "" -#: src/Model/Event.php:614 +#: src/Model/Event.php:691 msgid "Delete event" msgstr "" -#: src/Model/Event.php:856 src/Module/Debug/Localtime.php:38 +#: src/Model/Event.php:930 src/Module/Debug/Localtime.php:38 msgid "l F d, Y \\@ g:i A" msgstr "" -#: src/Model/Event.php:857 +#: src/Model/Event.php:931 msgid "D g:i A" msgstr "" -#: src/Model/Event.php:858 +#: src/Model/Event.php:932 msgid "g:i A" msgstr "" -#: src/Model/Event.php:919 src/Model/Event.php:921 +#: src/Model/Event.php:993 src/Model/Event.php:995 msgid "Show map" msgstr "" -#: src/Model/Event.php:920 +#: src/Model/Event.php:994 msgid "Hide map" msgstr "" -#: src/Model/Event.php:1013 +#: src/Model/Event.php:1087 #, php-format msgid "%s's birthday" msgstr "" -#: src/Model/Event.php:1014 +#: src/Model/Event.php:1088 #, php-format msgid "Happy Birthday %s" msgstr "" @@ -4354,6 +4262,27 @@ msgid "" "the protocol." msgstr "" +#: src/Module/Admin/Blocklist/Server/Add.php:136 +#: src/Module/Admin/Blocklist/Server/Add.php:138 +#: src/Module/Admin/Blocklist/Server/Import.php:128 +#: src/Module/Admin/Blocklist/Server/Index.php:82 +#: src/Module/Admin/Blocklist/Server/Index.php:83 +#: src/Module/Admin/Blocklist/Server/Index.php:111 +#: src/Module/Admin/Blocklist/Server/Index.php:112 +#: src/Module/Admin/Item/Delete.php:69 src/Module/Calendar/Event/Form.php:208 +#: src/Module/Calendar/Event/Form.php:240 src/Module/Debug/Probe.php:59 +#: src/Module/Install.php:207 src/Module/Install.php:240 +#: src/Module/Install.php:245 src/Module/Install.php:264 +#: src/Module/Install.php:275 src/Module/Install.php:280 +#: src/Module/Install.php:286 src/Module/Install.php:291 +#: src/Module/Install.php:305 src/Module/Install.php:320 +#: src/Module/Install.php:347 src/Module/Register.php:148 +#: src/Module/Security/TwoFactor/Verify.php:101 +#: src/Module/Settings/TwoFactor/Index.php:140 +#: src/Module/Settings/TwoFactor/Verify.php:154 +msgid "Required" +msgstr "" + #: src/Module/Admin/Blocklist/Server/Add.php:137 msgid "Purge server" msgstr "" @@ -4852,6 +4781,10 @@ msgstr "" msgid "Click to view details" msgstr "" +#: src/Module/Admin/Logs/View.php:97 src/Module/Calendar/Event/Form.php:206 +msgid "Event details" +msgstr "" + #: src/Module/Admin/Logs/View.php:98 msgid "Data" msgstr "" @@ -5026,6 +4959,11 @@ msgstr "" msgid "Policies" msgstr "" +#: src/Module/Admin/Site.php:439 src/Module/Calendar/Event/Form.php:251 +#: src/Module/Contact.php:477 src/Module/Profile/Profile.php:248 +msgid "Advanced" +msgstr "" + #: src/Module/Admin/Site.php:440 msgid "Auto Discovered Contact Directory" msgstr "" @@ -6583,18 +6521,93 @@ msgstr "" msgid "The post was created" msgstr "" -#: src/Module/Calendar/Export.php:73 +#: src/Module/Calendar/Event/API.php:100 src/Module/Calendar/Event/API.php:135 +#: src/Module/Calendar/Event/Form.php:80 src/Module/Calendar/Event/Show.php:58 +msgid "Invalid Request" +msgstr "" + +#: src/Module/Calendar/Event/API.php:109 +msgid "Event id is missing." +msgstr "" + +#: src/Module/Calendar/Event/API.php:131 +msgid "Failed to remove event" +msgstr "" + +#: src/Module/Calendar/Event/API.php:186 src/Module/Calendar/Event/API.php:188 +msgid "Event can not end before it has started." +msgstr "" + +#: src/Module/Calendar/Event/API.php:195 src/Module/Calendar/Event/API.php:197 +msgid "Event title and start time are required." +msgstr "" + +#: src/Module/Calendar/Event/Form.php:207 +msgid "Starting date and Title are required." +msgstr "" + +#: src/Module/Calendar/Event/Form.php:208 +#: src/Module/Calendar/Event/Form.php:213 +msgid "Event Starts:" +msgstr "" + +#: src/Module/Calendar/Event/Form.php:222 +#: src/Module/Calendar/Event/Form.php:246 +msgid "Finish date/time is not known or not relevant" +msgstr "" + +#: src/Module/Calendar/Event/Form.php:224 +#: src/Module/Calendar/Event/Form.php:229 +msgid "Event Finishes:" +msgstr "" + +#: src/Module/Calendar/Event/Form.php:236 src/Module/Profile/Profile.php:171 +#: src/Module/Settings/Profile/Index.php:247 +msgid "Description:" +msgstr "" + +#: src/Module/Calendar/Event/Form.php:240 +#: src/Module/Calendar/Event/Form.php:242 +msgid "Title:" +msgstr "" + +#: src/Module/Calendar/Event/Form.php:243 +#: src/Module/Calendar/Event/Form.php:244 +msgid "Share this event" +msgstr "" + +#: src/Module/Calendar/Event/Form.php:250 src/Module/Profile/Profile.php:247 +msgid "Basic" +msgstr "" + +#: src/Module/Calendar/Export.php:77 msgid "This calendar format is not supported" msgstr "" -#: src/Module/Calendar/Export.php:75 +#: src/Module/Calendar/Export.php:79 msgid "No exportable data found" msgstr "" -#: src/Module/Calendar/Export.php:92 +#: src/Module/Calendar/Export.php:96 msgid "calendar" msgstr "" +#: src/Module/Calendar/Show.php:120 +msgid "Events" +msgstr "" + +#: src/Module/Calendar/Show.php:121 +msgid "View" +msgstr "" + +#: src/Module/Calendar/Show.php:122 +msgid "Create New Event" +msgstr "" + +#: src/Module/Calendar/Show.php:128 +msgid "list" +msgstr "" + #: src/Module/Contact.php:88 #, php-format msgid "%d contact edited." @@ -7724,6 +7737,10 @@ msgstr "" msgid "OK" msgstr "" +#: src/Module/Install.php:214 +msgid "Next" +msgstr "" + #: src/Module/Install.php:215 msgid "Check again" msgstr "" @@ -9419,10 +9436,6 @@ msgstr "" msgid "Theme settings" msgstr "" -#: src/Module/Settings/Display.php:205 -msgid "Calendar" -msgstr "" - #: src/Module/Settings/Display.php:211 msgid "Display Theme:" msgstr "" diff --git a/view/templates/events_js.tpl b/view/templates/calendar/calendar.tpl similarity index 100% rename from view/templates/events_js.tpl rename to view/templates/calendar/calendar.tpl diff --git a/view/templates/event_head.tpl b/view/templates/calendar/calendar_head.tpl similarity index 96% rename from view/templates/event_head.tpl rename to view/templates/calendar/calendar_head.tpl index 1627bd699..50fb3fe4d 100644 --- a/view/templates/event_head.tpl +++ b/view/templates/calendar/calendar_head.tpl @@ -1,7 +1,7 @@