Skip to content

Commit c0bbb1e

Browse files
committed
added: Integrated iCal import and export features
1 parent 3e03574 commit c0bbb1e

File tree

18 files changed

+1023
-345
lines changed

18 files changed

+1023
-345
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
$calendar_type = get_input('calendar_type', 'all');
4+
$date_format = elgg_get_config('date_format', elgg_echo('input:date_format'));
5+
6+
$start_date = get_input('start_date', '');
7+
if ($start_date == '') {
8+
$start_date = time();
9+
} else {
10+
$start_date = DateTime::createFromFormat($date_format, $start_date)->getTimestamp();
11+
}
12+
13+
$end_date = get_input('end_date', '');
14+
if ($end_date == '') {
15+
$end_date = DateTime::createFromFormat('U', time())
16+
->add(DateInterval::createFromDateString('1 month'))
17+
->getTimestamp();
18+
} else {
19+
$end_date = DateTime::createFromFormat($date_format, $end_date)->getTimestamp();
20+
}
21+
22+
$region = get_input('region', '');
23+
$event_type = get_input('event_type', '');
24+
25+
$owner = get_input('owner', '');
26+
$group = get_input('group', '');
27+
28+
$options = [
29+
'types' => ['object'],
30+
'subTypes' => [Event::SUBTYPE],
31+
'metadata_name_value_pairs' => [
32+
[
33+
'name' => 'event_start',
34+
'operand' => '>=',
35+
'value' => $start_date,
36+
],
37+
[
38+
'name' => 'event_end',
39+
'operand' => '<=',
40+
'value' => $end_date,
41+
],
42+
]
43+
];
44+
45+
if ($region != '') {
46+
$options['metadata_name_value_pairs'][] = [
47+
'name' => 'region',
48+
'operand' => 'IN',
49+
'value' => $region,
50+
];
51+
}
52+
53+
if ($event_type != '') {
54+
$options['metadata_name_value_pairs'][] = [
55+
'name' => 'event_type',
56+
'operand' => 'IN',
57+
'value' => $event_type,
58+
];
59+
}
60+
61+
switch ($calendar_type) {
62+
case 'group':
63+
$options['container_guids'] = [$group];
64+
break;
65+
case 'owner':
66+
$options['owner_guids'] = [$owner];
67+
break;
68+
}
69+
70+
$events = elgg_get_entities($options);
71+
72+
use Kigkonsult\Icalcreator\Vcalendar;
73+
74+
/** @noinspection PhpUnhandledExceptionInspection */
75+
/** @noinspection PhpClassConstantAccessedViaChildClassInspection */
76+
$vcalendar = Vcalendar::factory(
77+
[
78+
Vcalendar::UNIQUE_ID => 'https://github.com/ColdTrick/event_manager',
79+
]
80+
)
81+
->setMethod(Vcalendar::PUBLISH)
82+
->setXprop(Vcalendar::X_WR_CALNAME, 'Exported event')
83+
->setXprop(Vcalendar::X_WR_CALDESC, 'Exported events from event_manager')
84+
->setXprop(Vcalendar::X_WR_RELCALID, elgg_get_site_url())
85+
->setXprop(Vcalendar::X_WR_TIMEZONE, date_default_timezone_get());
86+
87+
foreach ($events as $event) {
88+
/** @noinspection PhpUnhandledExceptionInspection */
89+
$vcalendar->setComponent($event->toVEvent());
90+
}
91+
92+
/** @noinspection PhpUnhandledExceptionInspection */
93+
return elgg_download_response($vcalendar->createCalendar(), 'export.ics');
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
use Kigkonsult\Icalcreator\Vcalendar;
4+
use Kigkonsult\Icalcreator\Vevent;
5+
6+
$calendar_type = get_input('calendar_type', 'all');
7+
8+
$owner = get_input('owner', '')[0];
9+
$group = get_input('group', '')[0];
10+
$file = elgg_get_uploaded_file('import');
11+
12+
$vcalendar = Vcalendar::factory(
13+
[
14+
Vcalendar::UNIQUE_ID => 'https://github.com/ColdTrick/event_manager',
15+
]
16+
);
17+
try {
18+
$vcalendar->parse($file->getContent());
19+
} catch (Exception $e) {
20+
return elgg_error_response('Error parsing calendar: ' . $e);
21+
}
22+
23+
$event_counter = 0;
24+
25+
/** @var Vevent $component */
26+
foreach ($vcalendar->getComponents('Vevent') as $component) {
27+
try {
28+
$event = Event::fromVEvent($component);
29+
30+
switch ($calendar_type) {
31+
case 'group':
32+
$event->setContainerGUID($group);
33+
break;
34+
case 'owner':
35+
$event->owner_guid = $owner;
36+
break;
37+
}
38+
39+
$event->save();
40+
$event_counter++;
41+
} catch (Exception $e) {
42+
return elgg_error_response(
43+
elgg_echo('event_manager:ical_direct:import:failure', [$e])
44+
);
45+
}
46+
}
47+
48+
return elgg_ok_response(
49+
'',
50+
elgg_echo('event_manager:ical_direct:import:success', [$event_counter])
51+
);

classes/ColdTrick/EventManager/Menus/Entity.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,38 @@ public static function registerEventUnsubscribe(\Elgg\Event $event): ?MenuItems
148148

149149
return $result;
150150
}
151+
152+
/**
153+
* Adds menu items to the event entity menu for exporting it to ical
154+
*
155+
* @param \Elgg\Event $event 'register', 'menu:entity'
156+
*
157+
* @return null|MenuItems
158+
*/
159+
public static function registerICalExport(\Elgg\Event $event): ?MenuItems {
160+
$entity = $event->getEntityParam();
161+
if (!$entity instanceof \Event) {
162+
return null;
163+
}
164+
165+
if (!elgg_get_plugin_setting('ical_direct', 'event_manager')) {
166+
return null;
167+
}
168+
169+
// show an ical export link
170+
$result = $event->getValue();
171+
172+
$result[] = \ElggMenuItem::factory([
173+
'name' => 'ical-export',
174+
'icon' => 'calendar-plus',
175+
'text' => elgg_echo('event_manager:ical_direct:export'),
176+
'href' => elgg_generate_url('default:object:event:export', [
177+
'guid' => $entity->guid,
178+
'view' => 'ical'
179+
]),
180+
'priority' => 300,
181+
]);
182+
183+
return $result;
184+
}
151185
}

classes/ColdTrick/EventManager/Menus/Filter.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,29 @@ public static function registerEventsList(\Elgg\Event $event): MenuItems {
7575
'priority' => 400,
7676
]);
7777
}
78+
79+
if (elgg_get_plugin_setting('ical_direct', 'event_manager')) {
80+
$result[] = \ElggMenuItem::factory([
81+
'name' => 'export-ical',
82+
'text' => elgg_echo('event_manager:ical_direct:export'),
83+
'href' => elgg_http_add_url_query_elements(elgg_generate_url('collection:ical:export'), [
84+
'list_route' => elgg_get_current_route_name(),
85+
'route_parameters' => elgg_get_current_route()->getMatchedParameters(),
86+
]),
87+
'priority' => 500,
88+
'selected' => $selected === 'export-ical',
89+
]);
90+
$result[] = \ElggMenuItem::factory([
91+
'name' => 'import-ical',
92+
'text' => elgg_echo('event_manager:ical_direct:import'),
93+
'href' => elgg_http_add_url_query_elements(elgg_generate_url('collection:ical:import'), [
94+
'list_route' => elgg_get_current_route_name(),
95+
'route_parameters' => elgg_get_current_route()->getMatchedParameters(),
96+
]),
97+
'priority' => 500,
98+
'selected' => $selected === 'import-ical',
99+
]);
100+
}
78101

79102
return $result;
80103
}

classes/Event.php

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
<?php
22

33
use Elgg\Database\Clauses\JoinClause;
4+
use Elgg\Database\Clauses\OrderByClause;
45
use Elgg\Database\Delete;
56
use Elgg\Database\QueryBuilder;
67
use Elgg\Database\Select;
8+
use Kigkonsult\Icalcreator\Vevent;
79

810
/**
911
* Event
@@ -33,6 +35,8 @@
3335
* @property bool $waiting_list_enabled is a waitling list enabled
3436
* @property string $website event website
3537
* @property bool $with_program has a program
38+
* @property mixed $starttime start time of event
39+
* @property mixed $endtime end time of event
3640
*/
3741
class Event extends \ElggObject {
3842

@@ -1105,6 +1109,28 @@ public function countAttendees(): int {
11051109
});
11061110
}
11071111

1112+
/**
1113+
* Fetches all attendees of this event
1114+
*
1115+
* @return ElggUser[]
1116+
*/
1117+
public function getAttendees(): array {
1118+
$options = [
1119+
'type' => 'user', // trigger search fields generation
1120+
'type_subtype_pairs' => [
1121+
'user' => ELGG_ENTITIES_ANY_VALUE,
1122+
'object' => [
1123+
EventRegistration::SUBTYPE,
1124+
],
1125+
],
1126+
'relationship_guid' => $this->guid,
1127+
'relationship' => 'event_attending',
1128+
'no_results' => true,
1129+
'order_by' => new OrderByClause('r.time_created', 'DESC'),
1130+
];
1131+
return elgg_get_entities($options);
1132+
}
1133+
11081134
/**
11091135
* Counts the waiters
11101136
*
@@ -1159,4 +1185,129 @@ public function getOrganizers(array $options = []): int|array {
11591185

11601186
return elgg_get_entities($options);
11611187
}
1188+
1189+
/**
1190+
* Returns the VEvent representation of this event.
1191+
*
1192+
* @return Vevent
1193+
* @throws Exception
1194+
*/
1195+
public function toVEvent(): Vevent {
1196+
$timezone = \Kigkonsult\Icalcreator\Util\DateTimeZoneFactory::factory(date_default_timezone_get());
1197+
$dtstart = DateTimeImmutable::createFromFormat('Y-m-d\\TH:i:s', $this->getStartDate('Y-m-d\\TH:i:s'), $timezone);
1198+
$dtend = DateTimeImmutable::createFromFormat('Y-m-d\\TH:i:s', $this->getEndDate('Y-m-d\\TH:i:s'), $timezone);
1199+
/** @noinspection PhpUnhandledExceptionInspection */
1200+
$vevent = Vevent::factory(
1201+
null,
1202+
$dtstart,
1203+
$dtend,
1204+
null,
1205+
$this->title
1206+
)
1207+
->setDescription($this->shortdescription)
1208+
->setDtstamp(DateTimeImmutable::createFromFormat('c', $this->getTimeCreated()))
1209+
->setComment($this->description)
1210+
->setLocation($this->location);
1211+
1212+
$organizers = $this->getOrganizers();
1213+
if (count($organizers) > 0 && isset($organizers[0]->email)) {
1214+
$vevent->setOrganizer($organizers[0]->email);
1215+
}
1216+
1217+
if ($this->countAttendees() > 0) {
1218+
foreach ($this->getAttendees() as $attendee) {
1219+
$vevent->setAttendee($attendee->email);
1220+
}
1221+
}
1222+
1223+
foreach ($this->getContacts() as $contact) {
1224+
if ($contact instanceof ElggUser && isset($contact->email)) {
1225+
$vevent->setContact($contact->email);
1226+
}
1227+
}
1228+
1229+
if ($this->website) {
1230+
$vevent->setXprop('X-WEBSITE', $this->website);
1231+
}
1232+
1233+
if ($this->region) {
1234+
$vevent->setXprop('X-PROP-REGION', $this->region);
1235+
}
1236+
1237+
if ($this->venue) {
1238+
$vevent->setXprop('X-PROP-VENUE', $this->venue);
1239+
}
1240+
1241+
if ($this->event_type) {
1242+
$vevent->setXprop('X-PROP-TYPE', $this->event_type);
1243+
}
1244+
1245+
return $vevent;
1246+
}
1247+
1248+
/**
1249+
* Generates a new event based on an iCal Vevent
1250+
*
1251+
* @param Vevent $vevent Source iCal Vevent to convert
1252+
* @return Event
1253+
* @throws Exception
1254+
*/
1255+
public static function fromVEvent(Vevent $vevent): Event {
1256+
$event = new Event();
1257+
$event->event_start = $vevent->getDtstart()->getTimestamp() + $vevent->getDtstart()->getOffset();
1258+
$event->event_end = $vevent->getDtend()->getTimestamp() + $vevent->getDtend()->getOffset();
1259+
$event->title = $vevent->getSummary();
1260+
$event->shortdescription = $vevent->getDescription();
1261+
$event->description = $vevent->getComment();
1262+
$event->location = $vevent->getLocation();
1263+
1264+
if ($vevent->isOrganizerSet()) {
1265+
$organizer = elgg_get_user_by_email($vevent->getOrganizer());
1266+
if (is_null($organizer)) {
1267+
$event->organizer = $vevent->getOrganizer();
1268+
} else {
1269+
$event->organizer_guids = [$organizer->guid];
1270+
}
1271+
}
1272+
1273+
if ($vevent->isAttendeeSet()) {
1274+
foreach ($vevent->getAllAttendee() as $attendee) {
1275+
$attendee_object = elgg_get_user_by_email($attendee);
1276+
if (!is_null($attendee_object)) {
1277+
$event->addRelationship($attendee_object->guid, 'event_attending');
1278+
}
1279+
}
1280+
}
1281+
1282+
if ($vevent->isContactSet()) {
1283+
foreach ($vevent->getAllContact() as $contact) {
1284+
$contact_object = elgg_get_user_by_email($contact);
1285+
if (!is_null($contact_object)) {
1286+
if (!is_array($event->contact_guids)) {
1287+
$event->contact_guids = [];
1288+
}
1289+
1290+
$event->contact_guids[] = $contact_object->guid;
1291+
}
1292+
}
1293+
}
1294+
1295+
if ($vevent->isXpropSet('X-WEBSITE')) {
1296+
$event->website = $vevent->getXprop('X-WEBSITE')[1];
1297+
}
1298+
1299+
if ($vevent->isXpropSet('X-PROP-REGION')) {
1300+
$event->region = $vevent->getXprop('X-PROP-REGION')[1];
1301+
}
1302+
1303+
if ($vevent->isXpropSet('X-PROP-VENUE')) {
1304+
$event->venue = $vevent->getXprop('X-PROP-VENUE')[1];
1305+
}
1306+
1307+
if ($vevent->isXpropSet('X-PROP-TYPE')) {
1308+
$event->event_type = $vevent->getXprop('X-PROP-TYPE')[1];
1309+
}
1310+
1311+
return $event;
1312+
}
11621313
}

0 commit comments

Comments
 (0)