Merge branch 'calendar_merge' into 'dev'

calendar merge - initial checkin

See merge request hubzilla/core!1599
This commit is contained in:
Max Kostikov 2019-04-20 20:14:58 +02:00
commit a6a17a85f3
10 changed files with 872 additions and 52 deletions

View File

@ -906,6 +906,19 @@ class Cdav extends Controller {
$sources = '';
if(get_pconfig(local_channel(), 'cdav_calendar', 'channel_calendar')) {
$sources .= '{
id: \'channel_calendar\',
url: \'/channel_calendar/json/\',
color: \'#3a87ad\'
}, ';
}
$channel_calendars[] = [
'displayname' => $channel['channel_name'],
'id' => 'channel_calendar'
];
foreach($calendars as $calendar) {
$editable = (($calendar['share-access'] == 2) ? 'false' : 'true'); // false/true must be string since we're passing it to javascript
$color = (($calendar['{http://apple.com/ns/ical/}calendar-color']) ? $calendar['{http://apple.com/ns/ical/}calendar-color'] : '#3a87ad');
@ -939,6 +952,17 @@ class Cdav extends Controller {
$description = ['description', t('Description')];
$location = ['location', t('Location')];
require_once('include/acl_selectors.php');
$accesslist = new \Zotlabs\Access\AccessList($channel);
$perm_defaults = $accesslist->get();
//$acl = (($orig_event['event_xchan']) ? '' : populate_acl(((x($orig_event)) ? $orig_event : $perm_defaults), false, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream')));
$acl = populate_acl($perm_defaults, false, \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_stream'));
//$permissions = ((x($orig_event)) ? $orig_event : $perm_defaults);
$permissions = $perm_defaults;
$o .= replace_macros(get_markup_template('cdav_calendar.tpl'), [
'$sources' => $sources,
'$color' => $color,
@ -955,6 +979,7 @@ class Cdav extends Controller {
'$list_week' => t('List week'),
'$list_day' => t('List day'),
'$title' => $title,
'$channel_calendars' => $channel_calendars,
'$writable_calendars' => $writable_calendars,
'$dtstart' => $dtstart,
'$dtend' => $dtend,
@ -964,10 +989,19 @@ class Cdav extends Controller {
'$less' => t('Less'),
'$update' => t('Update'),
'$calendar_select_label' => t('Select calendar'),
'$calendar_optiopns_label' => [t('Channel Calendars'), t('CalDAV Calendars')],
'$delete' => t('Delete'),
'$delete_all' => t('Delete all'),
'$cancel' => t('Cancel'),
'$recurrence_warning' => t('Sorry! Editing of recurrent events is not yet implemented.')
'$recurrence_warning' => t('Sorry! Editing of recurrent events is not yet implemented.'),
'$channel_hash' => $channel['channel_hash'],
'$acl' => $acl,
'$lockstate' => (($accesslist->is_private()) ? 'lock' : 'unlock'),
'$allow_cid' => acl2json($permissions['allow_cid']),
'$allow_gid' => acl2json($permissions['allow_gid']),
'$deny_cid' => acl2json($permissions['deny_cid']),
'$deny_gid' => acl2json($permissions['deny_gid'])
]);
return $o;
@ -1054,7 +1088,7 @@ class Cdav extends Controller {
}
//enable/disable calendars
if(argc() == 5 && argv(1) === 'calendar' && argv(2) === 'switch' && intval(argv(3)) && (argv(4) == 1 || argv(4) == 0)) {
if(argc() == 5 && argv(1) === 'calendar' && argv(2) === 'switch' && argv(3) && (argv(4) == 1 || argv(4) == 0)) {
$id = argv(3);
if(! cdav_perms($id,$calendars))

View File

@ -0,0 +1,622 @@
<?php
namespace Zotlabs\Module;
require_once('include/conversation.php');
require_once('include/bbcode.php');
require_once('include/datetime.php');
require_once('include/event.php');
require_once('include/items.php');
require_once('include/html2plain.php');
class Channel_calendar extends \Zotlabs\Web\Controller {
function post() {
logger('post: ' . print_r($_REQUEST,true), LOGGER_DATA);
if(! local_channel())
return;
if(($_FILES) && array_key_exists('userfile',$_FILES) && intval($_FILES['userfile']['size'])) {
$src = $_FILES['userfile']['tmp_name'];
if($src) {
$result = parse_ical_file($src,local_channel());
if($result)
info( t('Calendar entries imported.') . EOL);
else
notice( t('No calendar entries found.') . EOL);
@unlink($src);
}
goaway(z_root() . '/channel_calendar');
}
$event_id = ((x($_POST,'event_id')) ? intval($_POST['event_id']) : 0);
$event_hash = ((x($_POST,'event_hash')) ? $_POST['event_hash'] : '');
$xchan = ((x($_POST,'xchan')) ? dbesc($_POST['xchan']) : '');
$uid = local_channel();
$start_text = escape_tags($_REQUEST['dtstart']);
$finish_text = escape_tags($_REQUEST['dtend']);
$adjust = intval($_POST['adjust']);
$nofinish = intval($_POST['nofinish']);
$timezone = ((x($_POST,'timezone_select')) ? notags(trim($_POST['timezone_select'])) : '');
$tz = (($timezone) ? $timezone : date_default_timezone_get());
$categories = escape_tags(trim($_POST['category']));
// only allow editing your own events.
if(($xchan) && ($xchan !== get_observer_hash()))
return;
if($start_text) {
$start = $start_text;
}
else {
$start = sprintf('%d-%d-%d %d:%d:0',$startyear,$startmonth,$startday,$starthour,$startminute);
}
if($finish_text) {
$finish = $finish_text;
}
else {
$finish = sprintf('%d-%d-%d %d:%d:0',$finishyear,$finishmonth,$finishday,$finishhour,$finishminute);
}
if($nofinish) {
$finish = NULL_DATE;
}
if($adjust) {
$start = datetime_convert($tz,'UTC',$start);
if(! $nofinish)
$finish = datetime_convert($tz,'UTC',$finish);
}
else {
$start = datetime_convert('UTC','UTC',$start);
if(! $nofinish)
$finish = datetime_convert('UTC','UTC',$finish);
}
// 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 = escape_tags(trim($_POST['summary']));
$desc = escape_tags(trim($_POST['desc']));
$location = escape_tags(trim($_POST['location']));
$type = escape_tags(trim($_POST['type']));
require_once('include/text.php');
linkify_tags($desc, local_channel());
linkify_tags($location, local_channel());
//$action = ($event_hash == '') ? 'new' : "event/" . $event_hash;
//fixme: this url gives a wsod if there is a linebreak detected in one of the variables ($desc or $location)
//$onerror_url = z_root() . "/events/" . $action . "?summary=$summary&description=$desc&location=$location&start=$start_text&finish=$finish_text&adjust=$adjust&nofinish=$nofinish&type=$type";
$onerror_url = z_root() . "/events";
if(strcmp($finish,$start) < 0 && !$nofinish) {
notice( t('Event can not end before it has started.') . EOL);
if(intval($_REQUEST['preview'])) {
echo( t('Unable to generate preview.'));
killme();
}
goaway($onerror_url);
}
if((! $summary) || (! $start)) {
notice( t('Event title and start time are required.') . EOL);
if(intval($_REQUEST['preview'])) {
echo( t('Unable to generate preview.'));
killme();
}
goaway($onerror_url);
}
// $share = ((intval($_POST['distr'])) ? intval($_POST['distr']) : 0);
$share = 1;
$channel = \App::get_channel();
$acl = new \Zotlabs\Access\AccessList(false);
if($event_id) {
$x = q("select * from event where id = %d and uid = %d limit 1",
intval($event_id),
intval(local_channel())
);
if(! $x) {
notice( t('Event not found.') . EOL);
if(intval($_REQUEST['preview'])) {
echo( t('Unable to generate preview.'));
killme();
}
return;
}
$acl->set($x[0]);
$created = $x[0]['created'];
$edited = datetime_convert();
if($x[0]['allow_cid'] === '<' . $channel['channel_hash'] . '>'
&& $x[0]['allow_gid'] === '' && $x[0]['deny_cid'] === '' && $x[0]['deny_gid'] === '') {
$share = false;
}
else {
$share = true;
}
}
else {
$created = $edited = datetime_convert();
if($share) {
$acl->set_from_array($_POST);
}
else {
$acl->set(array('allow_cid' => '<' . $channel['channel_hash'] . '>', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => ''));
}
}
$post_tags = array();
$channel = \App::get_channel();
$ac = $acl->get();
if(strlen($categories)) {
$cats = explode(',',$categories);
foreach($cats as $cat) {
$post_tags[] = array(
'uid' => $profile_uid,
'ttype' => TERM_CATEGORY,
'otype' => TERM_OBJ_POST,
'term' => trim($cat),
'url' => $channel['xchan_url'] . '?f=&cat=' . urlencode(trim($cat))
);
}
}
$datarray = array();
$datarray['dtstart'] = $start;
$datarray['dtend'] = $finish;
$datarray['summary'] = $summary;
$datarray['description'] = $desc;
$datarray['location'] = $location;
$datarray['etype'] = $type;
$datarray['adjust'] = $adjust;
$datarray['nofinish'] = $nofinish;
$datarray['uid'] = local_channel();
$datarray['account'] = get_account_id();
$datarray['event_xchan'] = $channel['channel_hash'];
$datarray['allow_cid'] = $ac['allow_cid'];
$datarray['allow_gid'] = $ac['allow_gid'];
$datarray['deny_cid'] = $ac['deny_cid'];
$datarray['deny_gid'] = $ac['deny_gid'];
$datarray['private'] = (($acl->is_private()) ? 1 : 0);
$datarray['id'] = $event_id;
$datarray['created'] = $created;
$datarray['edited'] = $edited;
if(intval($_REQUEST['preview'])) {
$html = format_event_html($datarray);
echo $html;
killme();
}
$event = event_store_event($datarray);
if($post_tags)
$datarray['term'] = $post_tags;
$item_id = event_store_item($datarray,$event);
if($item_id) {
$r = q("select * from item where id = %d",
intval($item_id)
);
if($r) {
xchan_query($r);
$sync_item = fetch_post_tags($r);
$z = q("select * from event where event_hash = '%s' and uid = %d limit 1",
dbesc($r[0]['resource_id']),
intval($channel['channel_id'])
);
if($z) {
build_sync_packet($channel['channel_id'],array('event_item' => array(encode_item($sync_item[0],true)),'event' => $z));
}
}
}
if($share)
\Zotlabs\Daemon\Master::Summon(array('Notifier','event',$item_id));
}
function get() {
if(argc() > 2 && argv(1) == 'ical') {
$event_id = argv(2);
require_once('include/security.php');
$sql_extra = permissions_sql(local_channel());
$r = q("select * from event where event_hash = '%s' $sql_extra limit 1",
dbesc($event_id)
);
if($r) {
header('Content-type: text/calendar');
header('content-disposition: attachment; filename="' . t('event') . '-' . $event_id . '.ics"' );
echo ical_wrapper($r);
killme();
}
else {
notice( t('Event not found.') . EOL );
return;
}
}
if(! local_channel()) {
notice( t('Permission denied.') . EOL);
return;
}
if((argc() > 2) && (argv(1) === 'ignore') && intval(argv(2))) {
$r = q("update event set dismissed = 1 where id = %d and uid = %d",
intval(argv(2)),
intval(local_channel())
);
}
if((argc() > 2) && (argv(1) === 'unignore') && intval(argv(2))) {
$r = q("update event set dismissed = 0 where id = %d and uid = %d",
intval(argv(2)),
intval(local_channel())
);
}
$channel = \App::get_channel();
$mode = 'view';
$y = 0;
$m = 0;
$ignored = ((x($_REQUEST,'ignored')) ? " and dismissed = " . intval($_REQUEST['ignored']) . " " : '');
if(argc() > 1) {
if(argc() > 2 && argv(1) === 'add') {
$mode = 'add';
$item_id = intval(argv(2));
}
if(argc() > 2 && argv(1) === 'drop') {
$mode = 'drop';
$event_id = argv(2);
}
if(argc() > 2 && intval(argv(1)) && intval(argv(2))) {
$mode = 'view';
$y = intval(argv(1));
$m = intval(argv(2));
}
if(argc() <= 2) {
$mode = 'view';
$event_id = argv(1);
}
}
if($mode === 'add') {
event_addtocal($item_id,local_channel());
killme();
}
if($mode == 'view') {
/* edit/create form */
if($event_id) {
$r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1",
dbesc($event_id),
intval(local_channel())
);
if(count($r))
$orig_event = $r[0];
}
$channel = \App::get_channel();
// Passed parameters overrides anything found in the DB
if(!x($orig_event))
$orig_event = array();
$n_checked = ((x($orig_event) && $orig_event['nofinish']) ? ' checked="checked" ' : '');
$a_checked = ((x($orig_event) && $orig_event['adjust']) ? ' checked="checked" ' : '');
$t_orig = ((x($orig_event)) ? $orig_event['summary'] : '');
$d_orig = ((x($orig_event)) ? $orig_event['description'] : '');
$l_orig = ((x($orig_event)) ? $orig_event['location'] : '');
$eid = ((x($orig_event)) ? $orig_event['id'] : 0);
$event_xchan = ((x($orig_event)) ? $orig_event['event_xchan'] : $channel['channel_hash']);
$mid = ((x($orig_event)) ? $orig_event['mid'] : '');
$sdt = ((x($orig_event)) ? $orig_event['dtstart'] : 'now');
$fdt = ((x($orig_event)) ? $orig_event['dtend'] : '+1 hour');
$tz = date_default_timezone_get();
if(x($orig_event))
$tz = (($orig_event['adjust']) ? date_default_timezone_get() : 'UTC');
$syear = datetime_convert('UTC', $tz, $sdt, 'Y');
$smonth = datetime_convert('UTC', $tz, $sdt, 'm');
$sday = datetime_convert('UTC', $tz, $sdt, 'd');
$shour = datetime_convert('UTC', $tz, $sdt, 'H');
$sminute = datetime_convert('UTC', $tz, $sdt, 'i');
$stext = datetime_convert('UTC',$tz,$sdt);
$stext = substr($stext,0,14) . "00:00";
$fyear = datetime_convert('UTC', $tz, $fdt, 'Y');
$fmonth = datetime_convert('UTC', $tz, $fdt, 'm');
$fday = datetime_convert('UTC', $tz, $fdt, 'd');
$fhour = datetime_convert('UTC', $tz, $fdt, 'H');
$fminute = datetime_convert('UTC', $tz, $fdt, 'i');
$ftext = datetime_convert('UTC',$tz,$fdt);
$ftext = substr($ftext,0,14) . "00:00";
$type = ((x($orig_event)) ? $orig_event['etype'] : 'event');
$f = get_config('system','event_input_format');
if(! $f)
$f = 'ymd';
$catsenabled = feature_enabled(local_channel(),'categories');
$category = '';
if($catsenabled && x($orig_event)){
$itm = q("select * from item where resource_type = 'event' and resource_id = '%s' and uid = %d limit 1",
dbesc($orig_event['event_hash']),
intval(local_channel())
);
$itm = fetch_post_tags($itm);
if($itm) {
$cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY);
foreach ($cats as $cat) {
if(strlen($category))
$category .= ', ';
$category .= $cat['term'];
}
}
}
$thisyear = datetime_convert('UTC',date_default_timezone_get(),'now','Y');
$thismonth = datetime_convert('UTC',date_default_timezone_get(),'now','m');
if(! $y)
$y = intval($thisyear);
if(! $m)
$m = intval($thismonth);
$export = false;
if(argc() === 4 && argv(3) === 'export')
$export = true;
// 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 = get_dim($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 (argv(1) === 'json'){
if (x($_GET,'start')) $start = $_GET['start'];
if (x($_GET,'end')) $finish = $_GET['end'];
}
$start = datetime_convert('UTC','UTC',$start);
$finish = datetime_convert('UTC','UTC',$finish);
$adjust_start = datetime_convert('UTC', date_default_timezone_get(), $start);
$adjust_finish = datetime_convert('UTC', date_default_timezone_get(), $finish);
if (x($_GET,'id')){
$r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan
from event left join item on resource_id = event_hash where resource_type = 'event' and event.uid = %d and event.id = %d limit 1",
intval(local_channel()),
intval($_GET['id'])
);
} elseif($export) {
$r = q("SELECT * from event where uid = %d
AND (( adjust = 0 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' )
OR ( adjust = 1 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' )) ",
intval(local_channel()),
dbesc($start),
dbesc($finish),
dbesc($adjust_start),
dbesc($adjust_finish)
);
}
else {
// fixed an issue with "nofinish" events not showing up in the calendar.
// There's still an issue if the finish date crosses the end of month.
// Noting this for now - it will need to be fixed here and in Friendica.
// Ultimately the finish date shouldn't be involved in the query.
$r = q("SELECT event.*, item.plink, item.item_flags, item.author_xchan, item.owner_xchan
from event left join item on event_hash = resource_id
where resource_type = 'event' and event.uid = %d and event.uid = item.uid $ignored
AND (( adjust = 0 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' )
OR ( adjust = 1 AND ( dtend >= '%s' or nofinish = 1 ) AND dtstart <= '%s' )) ",
intval(local_channel()),
dbesc($start),
dbesc($finish),
dbesc($adjust_start),
dbesc($adjust_finish)
);
}
$links = array();
if($r && ! $export) {
xchan_query($r);
$r = fetch_post_tags($r,true);
$r = sort_by_date($r);
}
if($r) {
foreach($r as $rr) {
$j = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'j') : datetime_convert('UTC','UTC',$rr['dtstart'],'j'));
if(! x($links,$j))
$links[$j] = z_root() . '/' . \App::$cmd . '#link-' . $j;
}
}
$events=array();
$last_date = '';
$fmt = t('l, F j');
if($r) {
foreach($r as $rr) {
$j = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'j') : datetime_convert('UTC','UTC',$rr['dtstart'],'j'));
$d = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], $fmt) : datetime_convert('UTC','UTC',$rr['dtstart'],$fmt));
$d = day_translate($d);
$start = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtstart'], 'c') : datetime_convert('UTC','UTC',$rr['dtstart'],'c'));
if ($rr['nofinish']){
$end = null;
} else {
$end = (($rr['adjust']) ? datetime_convert('UTC',date_default_timezone_get(),$rr['dtend'], 'c') : datetime_convert('UTC','UTC',$rr['dtend'],'c'));
// give a fake end to birthdays so they get crammed into a
// single day on the calendar
if($rr['etype'] === 'birthday')
$end = null;
}
$is_first = ($d !== $last_date);
$last_date = $d;
$edit = ((local_channel() && $rr['author_xchan'] == get_observer_hash()) ? array(z_root().'/events/'.$rr['event_hash'].'?expandform=1',t('Edit event'),'','') : false);
$drop = array(z_root().'/events/drop/'.$rr['event_hash'],t('Delete event'),'','');
$title = strip_tags(html_entity_decode(zidify_links(bbcode($rr['summary'])),ENT_QUOTES,'UTF-8'));
if(! $title) {
list($title, $_trash) = explode("<br",bbcode($rr['desc']),2);
$title = strip_tags(html_entity_decode($title,ENT_QUOTES,'UTF-8'));
}
$html = format_event_html($rr);
$rr['desc'] = zidify_links(smilies(bbcode($rr['desc'])));
$rr['description'] = htmlentities(html2plain(bbcode($rr['description'])),ENT_COMPAT,'UTF-8',false);
$rr['location'] = zidify_links(smilies(bbcode($rr['location'])));
$events[] = array(
'calendar_id' => 'channel_calendar',
'rw' => true,
'id'=>$rr['id'],
'uri' => $rr['event_hash'],
'start'=> $start,
'end' => $end,
'drop' => $drop,
'allDay' => false,
'title' => $title,
'j' => $j,
'd' => $d,
'is_editable' => $edit ? true : false,
'is_first'=>$is_first,
'item'=>$rr,
'html'=>$html,
'plink' => array($rr['plink'],t('Link to Source'),'',''),
'description' => $rr['description'],
'location' => $rr['location'],
'allow_cid' => expand_acl($rr['allow_cid']),
'allow_gid' => expand_acl($rr['allow_gid']),
'deny_cid' => expand_acl($rr['deny_cid']),
'deny_gid' => expand_acl($rr['deny_gid']),
);
}
}
if($export) {
header('Content-type: text/calendar');
header('content-disposition: attachment; filename="' . t('calendar') . '-' . $channel['channel_address'] . '.ics"' );
echo ical_wrapper($r);
killme();
}
if (\App::$argv[1] === 'json'){
json_return_and_die($events);
}
}
if($mode === 'drop' && $event_id) {
$r = q("SELECT * FROM event WHERE event_hash = '%s' AND uid = %d LIMIT 1",
dbesc($event_id),
intval(local_channel())
);
$sync_event = $r[0];
if($r) {
$r = q("delete from event where event_hash = '%s' and uid = %d",
dbesc($event_id),
intval(local_channel())
);
if($r) {
$r = q("update item set resource_type = '', resource_id = '' where resource_type = 'event' and resource_id = '%s' and uid = %d",
dbesc($event_id),
intval(local_channel())
);
$sync_event['event_deleted'] = 1;
build_sync_packet(0,array('event' => array($sync_event)));
killme();
}
notice( t('Failed to remove event' ) . EOL);
killme();
}
}
}
}

View File

@ -45,7 +45,8 @@ class Editpost extends \Zotlabs\Web\Controller {
}
if($itm[0]['resource_type'] === 'event' && $itm[0]['resource_id']) {
goaway(z_root() . '/events/' . $itm[0]['resource_id'] . '?expandform=1');
goaway(z_root() . '/cdav/calendar');
//goaway(z_root() . '/events/' . $itm[0]['resource_id'] . '?expandform=1');
}
$owner_uid = $itm[0]['uid'];

View File

@ -447,7 +447,7 @@ class Ping extends \Zotlabs\Web\Controller {
$when = day_translate(datetime_convert('UTC', (($rr['adjust']) ? date_default_timezone_get() : 'UTC'), $rr['dtstart'], $bd_format)) . (($today) ? ' ' . t('[today]') : '');
$result[] = array(
'notify_link' => z_root() . '/events', /// @FIXME this takes you to an edit page and it may not be yours, we really want to just view the single event --> '/events/event/' . $rr['event_hash'],
'notify_link' => z_root() . '/cdav/calendar', /// @FIXME this takes you to an edit page and it may not be yours, we really want to just view the single event --> '/events/event/' . $rr['event_hash'],
'name' => $rr['xchan_name'],
'addr' => $rr['xchan_addr'],
'url' => $rr['xchan_url'],

View File

@ -113,10 +113,22 @@ class Cdav {
}
}
$channel_calendars[] = [
'ownernick' => $channel['channel_address'],
'displayname' => $channel['channel_name'],
'calendarid' => 'channel_calendar',
'json_source' => '/channel_calendar/json',
'color' => '#3a87ad',
'editable' => true,
'switch' => get_pconfig(local_channel(), 'cdav_calendar', 'channel_calendar')
];
$o .= replace_macros(get_markup_template('cdav_widget_calendar.tpl'), [
'$my_calendars_label' => t('My Calendars'),
'$channel_calendars_label' => t('Channel Calendar'),
'$channel_calendars' => $channel_calendars,
'$my_calendars_label' => t('CalDAV Calendars'),
'$my_calendars' => $my_calendars,
'$shared_calendars_label' => t('Shared Calendars'),
'$shared_calendars_label' => t('Shared CalDAV Calendars'),
'$shared_calendars' => $shared_calendars,
'$sharee_options' => $sharee_options,
'$access_options' => $access_options,
@ -127,7 +139,7 @@ class Cdav {
'$create_label' => t('Create new calendar'),
'$create' => t('Create'),
'$create_placeholder' => t('Calendar Name'),
'$tools_label' => t('Calendar Tools'),
'$tools_label' => t('CalDAV Calendar Tools'),
'$import_label' => t('Import calendar'),
'$import_placeholder' => t('Select a calendar to import to'),
'$upload' => t('Upload'),

View File

@ -1284,6 +1284,10 @@ function cdav_principal($uri) {
}
function cdav_perms($needle, $haystack, $check_rw = false) {
if($needle == 'channel_calendar')
return true;
foreach ($haystack as $item) {
if($check_rw) {
if(is_array($item['id'])) {

View File

@ -942,17 +942,18 @@ a .generic-icons {
color: $font_colour;
}
.generic-icons:hover,
a .generic-icons:hover {
color: $font_colour;
}
.generic-icons-right {
font-size: 1rem;
margin-left: 0.5rem;
color: $font_colour;
}
.generic-icons:hover,
a .generic-icons:hover,
.generic-icons-right:hover,
a .generic-icons-right:hover {
color: $font_colour;
}
.generic-icons-nav {
font-size: 1rem;

View File

@ -4,6 +4,14 @@ var new_event = {};
var new_event_id = Math.random().toString(36).substring(7);
var views = {'dayGridMonth' : '{{$month}}', 'timeGridWeek' : '{{$week}}', 'timeGridDay' : '{{$day}}', 'listMonth' : '{{$list_month}}', 'listWeek' : '{{$list_week}}', 'listDay' : '{{$list_day}}'};
var event_id;
var event_uri;
var contact_allow = [];
var group_allow = [];
var contact_deny = [];
var group_deny = [];
$(document).ready(function() {
var calendarEl = document.getElementById('calendar');
calendar = new FullCalendar.Calendar(calendarEl, {
@ -45,7 +53,7 @@ $(document).ready(function() {
dtend.setHours(dtend.getHours() + 1);
}
$('#event_uri').val('');
event_uri = '';
$('#id_title').val('New event');
$('#calendar_select').val($("#calendar_select option:first").val()).attr('disabled', false);
$('#id_dtstart').val(info.date.toUTCString());
@ -64,12 +72,26 @@ $(document).ready(function() {
var event = info.event._def;
var dtstart = new Date(info.event._instance.range.start);
var dtend = new Date(info.event._instance.range.end);
if(event.extendedProps.plink) {
if(! $('#l2s').length)
$('#id_title_wrapper').prepend('<span id="l2s" class="float-right"></span>');
$('#l2s').html('<a href="' + event.extendedProps.plink[0] + '" target="_blank"><i class="fa fa-external-link"></i> ' + event.extendedProps.plink[1] + '</a>');
}
else {
$('#l2s').remove();
}
if(event.publicId == new_event_id) {
$('#calendar_select').trigger('change');
$('#event_submit').show();
event_id = 0;
$(window).scrollTop(0);
$('.section-content-tools-wrapper, #event_form_wrapper').show();
$('#recurrence_warning').hide();
$('#id_title').focus().val('');
return false;
}
@ -79,18 +101,28 @@ $(document).ready(function() {
new_event = {};
}
var calendar_id = ((event.extendedProps.calendar_id.constructor === Array) ? event.extendedProps.calendar_id[0] + ':' + event.extendedProps.calendar_id[1] : event.extendedProps.calendar_id);
if(!event.extendedProps.recurrent) {
$(window).scrollTop(0);
$('.section-content-tools-wrapper, #event_form_wrapper').show();
$('#recurrence_warning').hide();
$('#event_uri').val(event.extendedProps.uri);
event_uri = event.extendedProps.uri;
$('#id_title').val(event.title);
$('#calendar_select').val(event.extendedProps.calendar_id[0] + ':' + event.extendedProps.calendar_id[1]).attr('disabled', true);
$('#calendar_select').val(calendar_id).attr('disabled', true).trigger('change');
$('#id_dtstart').val(dtstart.toUTCString());
$('#id_dtend').val(dtend.toUTCString());
$('#id_description').val(event.extendedProps.description);
$('#id_location').val(event.extendedProps.location);
$('#event_submit').val('update_event').html('{{$update}}');
$('#dbtn-acl').addClass('d-none');
event_id = event.extendedProps.item ? event.extendedProps.item.id : 0;
contact_allow = event.extendedProps.contact_allow || [];
group_allow = event.extendedProps.group_allow || [];
contact_deny = event.extendedProps.contact_deny || [];
group_deny = event.extendedProps.group_deny || [];
if(event.extendedProps.rw) {
$('#event_delete').show();
$('#event_submit').show();
@ -100,6 +132,11 @@ $(document).ready(function() {
$('#id_dtend').attr('disabled', false);
$('#id_description').attr('disabled', false);
$('#id_location').attr('disabled', false);
if(calendar_id == 'channel_calendar' && !event.extendedProps.is_editable) {
console.log(calendar_id)
$('#event_submit').hide();
}
}
else {
$('#event_submit').hide();
@ -114,8 +151,8 @@ $(document).ready(function() {
else if(event.extendedProps.recurrent && event.extendedProps.rw) {
$('.section-content-tools-wrapper, #recurrence_warning').show();
$('#event_form_wrapper').hide();
$('#event_uri').val(event.extendedProps.uri);
$('#calendar_select').val(event.extendedProps.calendar_id[0] + ':' + event.extendedProps.calendar_id[1]).attr('disabled', true);
event_uri = event.extendedProps.uri;
$('#calendar_select').val(calendar_id).attr('disabled', true).trigger('change');
}
},
@ -193,6 +230,13 @@ $(document).ready(function() {
calendar.next();
$('#title').text(calendar.view.title);
});
$('#calendar_select').on('change', function() {
if(this.value === 'channel_calendar')
$('#dbtn-acl').removeClass('d-none');
else
$('#dbtn-acl').addClass('d-none');
});
$('.color-edit').colorpicker({ input: '.color-edit-input' });
@ -213,9 +257,16 @@ function changeView(action, viewName) {
}
function add_remove_json_source(source, color, editable, status) {
var parts = source.split('/');
var id = parts[4];
var id, parts = [];
if(source == '/channel_calendar/json')
id = 'channel_calendar'
if(! id) {
parts = source.split('/');
id = parts[4];
}
var eventSource = calendar.getEventSourceById(id);
var selector = '#calendar-btn-' + id;
@ -247,37 +298,102 @@ function updateSize() {
}
function on_submit() {
$.post( 'cdav/calendar', {
'submit': $('#event_submit').val(),
'target': $('#calendar_select').val(),
'uri': $('#event_uri').val(),
'title': $('#id_title').val(),
'dtstart': $('#id_dtstart').val(),
'dtend': $('#id_dtend').val(),
'description': $('#id_description').val(),
'location': $('#id_location').val()
})
.done(function() {
var parts = $('#calendar_select').val().split(':');
var eventSource = calendar.getEventSourceById(parts[0]);
eventSource.refetch();
reset_form();
if($('#calendar_select').val() == 'channel_calendar') {
if(new_event_id) {
$("input[name='contact_allow[]']").each(function() {
contact_allow.push($(this).val());
});
$("input[name='group_allow[]']").each(function() {
group_allow.push($(this).val());
});
$("input[name='contact_deny[]']").each(function() {
contact_deny.push($(this).val());
});
$("input[name='group_deny[]']").each(function() {
group_deny.push($(this).val());
});
}
});
$.post( 'channel_calendar', {
'event_id': event_id,
'event_hash': event_uri,
'xchan': '{{$channel_hash}}',
//'mid': mid,
'type': 'event',
'preview': 0,
'summary': $('#id_title').val(),
'dtstart': $('#id_dtstart').val(),
'dtend': $('#id_dtend').val(),
'adjust': 0,
'category': '',
'desc': $('#id_description').val(),
'location': $('#id_location').val(),
'submit': $('#event_submit').val(),
'contact_allow[]': contact_allow,
'group_allow[]': group_allow,
'contact_deny[]': contact_deny,
'group_deny[]': group_deny
/*
'submit': $('#event_submit').val(),
'target': $('#calendar_select').val(),
'uri': $('#event_uri').val(),
'title': $('#id_title').val(),
'dtstart': $('#id_dtstart').val(),
'dtend': $('#id_dtend').val(),
'description': $('#id_description').val(),
'location': $('#id_location').val()
*/
})
.done(function() {
var eventSource = calendar.getEventSourceById('channel_calendar');
eventSource.refetch();
reset_form();
});
}
else {
$.post( 'cdav/calendar', {
'submit': $('#event_submit').val(),
'target': $('#calendar_select').val(),
'uri': event_uri,
'title': $('#id_title').val(),
'dtstart': $('#id_dtstart').val(),
'dtend': $('#id_dtend').val(),
'description': $('#id_description').val(),
'location': $('#id_location').val()
})
.done(function() {
var parts = $('#calendar_select').val().split(':');
var eventSource = calendar.getEventSourceById(parts[0]);
eventSource.refetch();
reset_form();
});
}
}
function on_delete() {
$.post( 'cdav/calendar', {
'delete': 'delete',
'target': $('#calendar_select').val(),
'uri': $('#event_uri').val(),
})
.done(function() {
var parts = $('#calendar_select').val().split(':');
var eventSource = calendar.getEventSourceById(parts[0]);
eventSource.refetch();
reset_form();
});
if($('#calendar_select').val() == 'channel_calendar') {
$.get('channel_calendar/drop/' + event_uri, function() {
var eventSource = calendar.getEventSourceById('channel_calendar');
eventSource.refetch();
reset_form();
});
}
else {
$.post( 'cdav/calendar', {
'delete': 'delete',
'target': $('#calendar_select').val(),
'uri': event_uri
})
.done(function() {
var parts = $('#calendar_select').val().split(':');
var eventSource = calendar.getEventSourceById(parts[0]);
eventSource.refetch();
reset_form();
});
}
}
function reset_form() {
@ -285,7 +401,7 @@ function reset_form() {
$('#event_submit').val('');
$('#calendar_select').val('');
$('#event_uri').val('');
event_uri = '';
$('#id_title').val('');
$('#id_dtstart').val('');
$('#id_dtend').val('');
@ -311,6 +427,14 @@ function on_more() {
}
}
function exportDate() {
alert('not implemented');
console.log('not implemented');
//var moment = $('#events-calendar').fullCalendar('getDate');
//var sT = 'events/' + moment.year() + '/' + (moment.month() + 1) + '/export';
//window.location.href=sT;
}
</script>
<div class="generic-content-wrapper">
@ -350,14 +474,20 @@ function on_more() {
</div>
</div>
<div id="event_form_wrapper" style="display: none">
<form id="event_form" method="post" action="">
<input id="event_uri" type="hidden" name="uri" value="">
<form id="event_form" method="post" action="" class="acl-form" data-form_id="event_form" data-allow_cid='{{$allow_cid}}' data-allow_gid='{{$allow_gid}}' data-deny_cid='{{$deny_cid}}' data-deny_gid='{{$deny_gid}}'>
{{include file="field_input.tpl" field=$title}}
<label for="calendar_select">{{$calendar_select_label}}</label>
<select id="calendar_select" name="target" class="form-control form-group">
<optgroup label="{{$calendar_optiopns_label.0}}">
{{foreach $channel_calendars as $channel_calendar}}
<option value="channel_calendar">{{$channel_calendar.displayname}}</option>
{{/foreach}}
</optgroup>
<optgroup label="{{$calendar_optiopns_label.1}}">
{{foreach $writable_calendars as $writable_calendar}}
<option value="{{$writable_calendar.id.0}}:{{$writable_calendar.id.1}}">{{$writable_calendar.displayname}}{{if $writable_calendar.sharer}} ({{$writable_calendar.sharer}}){{/if}}</option>
{{/foreach}}
</optgroup>
</select>
<div id="more_block" style="display: none;">
{{include file="field_input.tpl" field=$dtstart}}
@ -368,6 +498,7 @@ function on_more() {
<div class="form-group">
<div class="pull-right">
<button id="event_more" type="button" class="btn btn-outline-secondary btn-sm"><i class="fa fa-caret-down"></i> {{$more}}</button>
<button id="dbtn-acl" class="btn btn-outline-secondary btn-sm d-none" type="button" data-toggle="modal" data-target="#aclModal"><i id="jot-perms-icon" class="fa fa-{{$lockstate}}"></i></button>
<button id="event_submit" type="button" value="" class="btn btn-primary btn-sm"></button>
</div>
@ -378,6 +509,7 @@ function on_more() {
<div class="clear"></div>
</div>
</form>
{{$acl}}
</div>
</div>
<div class="section-content-wrapper-np">

View File

@ -1,3 +1,17 @@
<div class="widget">
<h3>{{$channel_calendars_label}}</h3>
{{foreach $channel_calendars as $channel_calendar}}
<div id="calendar-{{$channel_calendar.calendarid}}">
<div class="ml-3{{if !$channel_calendar@last}} form-group{{/if}}">
<i id="calendar-btn-{{$channel_calendar.calendarid}}" class="fa {{if $channel_calendar.switch}}fa-calendar-check-o{{else}}fa-calendar-o{{/if}} generic-icons fakelink" onclick="add_remove_json_source('{{$channel_calendar.json_source}}', '{{$channel_calendar.color}}', {{$channel_calendar.editable}})" style="color: {{$channel_calendar.color}};"></i>{{$channel_calendar.displayname}}
<div class="float-right">
<a href="#" onclick="exportDate(); return false;"><i id="download-icon" class="fa fa-cloud-download fakelink generic-icons-right"></i></a>
</div>
</div>
</div>
{{/foreach}}
</div>
{{if $my_calendars}}
<div class="widget">
<h3>{{$my_calendars_label}}</h3>

View File

@ -354,7 +354,7 @@ var activeCommentText = '';
}
function itemAddToCal(id) {
$.get('{{$baseurl}}/events/add/' + id);
$.get('{{$baseurl}}/channel_calendar/add/' + id);
if(timer) clearTimeout(timer);
timer = setTimeout(updateInit,1000);
}