diff --git a/actions/event_manager/export/ical.php b/actions/event_manager/export/ical.php new file mode 100644 index 0000000..17afcc1 --- /dev/null +++ b/actions/event_manager/export/ical.php @@ -0,0 +1,93 @@ +getTimestamp(); +} + +$end_date = get_input('end_date', ''); +if ($end_date == '') { + $end_date = DateTime::createFromFormat('U', time()) + ->add(DateInterval::createFromDateString('1 month')) + ->getTimestamp(); +} else { + $end_date = DateTime::createFromFormat($date_format, $end_date)->getTimestamp(); +} + +$region = get_input('region', ''); +$event_type = get_input('event_type', ''); + +$owner = get_input('owner', ''); +$group = get_input('group', ''); + +$options = [ + 'types' => ['object'], + 'subTypes' => [Event::SUBTYPE], + 'metadata_name_value_pairs' => [ + [ + 'name' => 'event_start', + 'operand' => '>=', + 'value' => $start_date, + ], + [ + 'name' => 'event_end', + 'operand' => '<=', + 'value' => $end_date, + ], + ] +]; + +if ($region != '') { + $options['metadata_name_value_pairs'][] = [ + 'name' => 'region', + 'operand' => 'IN', + 'value' => $region, + ]; +} + +if ($event_type != '') { + $options['metadata_name_value_pairs'][] = [ + 'name' => 'event_type', + 'operand' => 'IN', + 'value' => $event_type, + ]; +} + +switch ($calendar_type) { + case 'group': + $options['container_guids'] = [$group]; + break; + case 'owner': + $options['owner_guids'] = [$owner]; + break; +} + +$events = elgg_get_entities($options); + +use Kigkonsult\Icalcreator\Vcalendar; + +/** @noinspection PhpUnhandledExceptionInspection */ +/** @noinspection PhpClassConstantAccessedViaChildClassInspection */ +$vcalendar = Vcalendar::factory( + [ + Vcalendar::UNIQUE_ID => 'https://github.com/ColdTrick/event_manager', + ] +) + ->setMethod(Vcalendar::PUBLISH) + ->setXprop(Vcalendar::X_WR_CALNAME, 'Exported event') + ->setXprop(Vcalendar::X_WR_CALDESC, 'Exported events from event_manager') + ->setXprop(Vcalendar::X_WR_RELCALID, elgg_get_site_url()) + ->setXprop(Vcalendar::X_WR_TIMEZONE, date_default_timezone_get()); + +foreach ($events as $event) { + /** @noinspection PhpUnhandledExceptionInspection */ + $vcalendar->setComponent($event->toVEvent()); +} + +/** @noinspection PhpUnhandledExceptionInspection */ +return elgg_download_response($vcalendar->createCalendar(), 'export.ics'); diff --git a/actions/event_manager/import/ical.php b/actions/event_manager/import/ical.php new file mode 100644 index 0000000..db79bd8 --- /dev/null +++ b/actions/event_manager/import/ical.php @@ -0,0 +1,51 @@ + 'https://github.com/ColdTrick/event_manager', + ] +); +try { + $vcalendar->parse($file->getContent()); +} catch (Exception $e) { + return elgg_error_response('Error parsing calendar: ' . $e); +} + +$event_counter = 0; + +/** @var Vevent $component */ +foreach ($vcalendar->getComponents('Vevent') as $component) { + try { + $event = Event::fromVEvent($component); + + switch ($calendar_type) { + case 'group': + $event->setContainerGUID($group); + break; + case 'owner': + $event->owner_guid = $owner; + break; + } + + $event->save(); + $event_counter++; + } catch (Exception $e) { + return elgg_error_response( + elgg_echo('event_manager:ical_direct:import:failure', [$e]) + ); + } +} + +return elgg_ok_response( + '', + elgg_echo('event_manager:ical_direct:import:success', [$event_counter]) +); diff --git a/classes/ColdTrick/EventManager/Menus/Entity.php b/classes/ColdTrick/EventManager/Menus/Entity.php index 28085dd..58acfae 100644 --- a/classes/ColdTrick/EventManager/Menus/Entity.php +++ b/classes/ColdTrick/EventManager/Menus/Entity.php @@ -148,4 +148,38 @@ public static function registerEventUnsubscribe(\Elgg\Event $event): ?MenuItems return $result; } + + /** + * Adds menu items to the event entity menu for exporting it to ical + * + * @param \Elgg\Event $event 'register', 'menu:entity' + * + * @return null|MenuItems + */ + public static function registerICalExport(\Elgg\Event $event): ?MenuItems { + $entity = $event->getEntityParam(); + if (!$entity instanceof \Event) { + return null; + } + + if (!elgg_get_plugin_setting('ical_direct', 'event_manager')) { + return null; + } + + // show an ical export link + $result = $event->getValue(); + + $result[] = \ElggMenuItem::factory([ + 'name' => 'ical-export', + 'icon' => 'calendar-plus', + 'text' => elgg_echo('event_manager:ical_direct:export'), + 'href' => elgg_generate_url('default:object:event:export', [ + 'guid' => $entity->guid, + 'view' => 'ical' + ]), + 'priority' => 300, + ]); + + return $result; + } } diff --git a/classes/ColdTrick/EventManager/Menus/Filter.php b/classes/ColdTrick/EventManager/Menus/Filter.php index bc78ce2..6514467 100644 --- a/classes/ColdTrick/EventManager/Menus/Filter.php +++ b/classes/ColdTrick/EventManager/Menus/Filter.php @@ -75,6 +75,29 @@ public static function registerEventsList(\Elgg\Event $event): MenuItems { 'priority' => 400, ]); } + + if (elgg_get_plugin_setting('ical_direct', 'event_manager')) { + $result[] = \ElggMenuItem::factory([ + 'name' => 'export-ical', + 'text' => elgg_echo('event_manager:ical_direct:export'), + 'href' => elgg_http_add_url_query_elements(elgg_generate_url('collection:ical:export'), [ + 'list_route' => elgg_get_current_route_name(), + 'route_parameters' => elgg_get_current_route()->getMatchedParameters(), + ]), + 'priority' => 500, + 'selected' => $selected === 'export-ical', + ]); + $result[] = \ElggMenuItem::factory([ + 'name' => 'import-ical', + 'text' => elgg_echo('event_manager:ical_direct:import'), + 'href' => elgg_http_add_url_query_elements(elgg_generate_url('collection:ical:import'), [ + 'list_route' => elgg_get_current_route_name(), + 'route_parameters' => elgg_get_current_route()->getMatchedParameters(), + ]), + 'priority' => 500, + 'selected' => $selected === 'import-ical', + ]); + } return $result; } diff --git a/classes/Event.php b/classes/Event.php index c9b665e..655d201 100644 --- a/classes/Event.php +++ b/classes/Event.php @@ -1,9 +1,11 @@ 'user', // trigger search fields generation + 'type_subtype_pairs' => [ + 'user' => ELGG_ENTITIES_ANY_VALUE, + 'object' => [ + EventRegistration::SUBTYPE, + ], + ], + 'relationship_guid' => $this->guid, + 'relationship' => 'event_attending', + 'no_results' => true, + 'order_by' => new OrderByClause('r.time_created', 'DESC'), + ]; + return elgg_get_entities($options); + } + /** * Counts the waiters * @@ -1159,4 +1185,129 @@ public function getOrganizers(array $options = []): int|array { return elgg_get_entities($options); } + + /** + * Returns the VEvent representation of this event. + * + * @return Vevent + * @throws Exception + */ + public function toVEvent(): Vevent { + $timezone = \Kigkonsult\Icalcreator\Util\DateTimeZoneFactory::factory(date_default_timezone_get()); + $dtstart = DateTimeImmutable::createFromFormat('Y-m-d\\TH:i:s', $this->getStartDate('Y-m-d\\TH:i:s'), $timezone); + $dtend = DateTimeImmutable::createFromFormat('Y-m-d\\TH:i:s', $this->getEndDate('Y-m-d\\TH:i:s'), $timezone); + /** @noinspection PhpUnhandledExceptionInspection */ + $vevent = Vevent::factory( + null, + $dtstart, + $dtend, + null, + $this->title + ) + ->setDescription($this->shortdescription) + ->setDtstamp(DateTimeImmutable::createFromFormat('c', $this->getTimeCreated())) + ->setComment($this->description) + ->setLocation($this->location); + + $organizers = $this->getOrganizers(); + if (count($organizers) > 0 && isset($organizers[0]->email)) { + $vevent->setOrganizer($organizers[0]->email); + } + + if ($this->countAttendees() > 0) { + foreach ($this->getAttendees() as $attendee) { + $vevent->setAttendee($attendee->email); + } + } + + foreach ($this->getContacts() as $contact) { + if ($contact instanceof ElggUser && isset($contact->email)) { + $vevent->setContact($contact->email); + } + } + + if ($this->website) { + $vevent->setXprop('X-WEBSITE', $this->website); + } + + if ($this->region) { + $vevent->setXprop('X-PROP-REGION', $this->region); + } + + if ($this->venue) { + $vevent->setXprop('X-PROP-VENUE', $this->venue); + } + + if ($this->event_type) { + $vevent->setXprop('X-PROP-TYPE', $this->event_type); + } + + return $vevent; + } + + /** + * Generates a new event based on an iCal Vevent + * + * @param Vevent $vevent Source iCal Vevent to convert + * @return Event + * @throws Exception + */ + public static function fromVEvent(Vevent $vevent): Event { + $event = new Event(); + $event->event_start = $vevent->getDtstart()->getTimestamp() + $vevent->getDtstart()->getOffset(); + $event->event_end = $vevent->getDtend()->getTimestamp() + $vevent->getDtend()->getOffset(); + $event->title = $vevent->getSummary(); + $event->shortdescription = $vevent->getDescription(); + $event->description = $vevent->getComment(); + $event->location = $vevent->getLocation(); + + if ($vevent->isOrganizerSet()) { + $organizer = elgg_get_user_by_email($vevent->getOrganizer()); + if (is_null($organizer)) { + $event->organizer = $vevent->getOrganizer(); + } else { + $event->organizer_guids = [$organizer->guid]; + } + } + + if ($vevent->isAttendeeSet()) { + foreach ($vevent->getAllAttendee() as $attendee) { + $attendee_object = elgg_get_user_by_email($attendee); + if (!is_null($attendee_object)) { + $event->addRelationship($attendee_object->guid, 'event_attending'); + } + } + } + + if ($vevent->isContactSet()) { + foreach ($vevent->getAllContact() as $contact) { + $contact_object = elgg_get_user_by_email($contact); + if (!is_null($contact_object)) { + if (!is_array($event->contact_guids)) { + $event->contact_guids = []; + } + + $event->contact_guids[] = $contact_object->guid; + } + } + } + + if ($vevent->isXpropSet('X-WEBSITE')) { + $event->website = $vevent->getXprop('X-WEBSITE')[1]; + } + + if ($vevent->isXpropSet('X-PROP-REGION')) { + $event->region = $vevent->getXprop('X-PROP-REGION')[1]; + } + + if ($vevent->isXpropSet('X-PROP-VENUE')) { + $event->venue = $vevent->getXprop('X-PROP-VENUE')[1]; + } + + if ($vevent->isXpropSet('X-PROP-TYPE')) { + $event->event_type = $vevent->getXprop('X-PROP-TYPE')[1]; + } + + return $event; + } } diff --git a/composer.json b/composer.json index fcff4d5..19926da 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,8 @@ }, "require": { "dompdf/dompdf" : "~3.1.0", - "npm-asset/fullcalendar": "~6.1.0" + "npm-asset/fullcalendar": "~6.1.0", + "kigkonsult/icalcreator": "^2.41" }, "repositories": [ { @@ -26,5 +27,8 @@ }, "conflict": { "elgg/elgg": "<6.3.1" + }, + "require-dev": { + "elgg/sniffs": "dev-master" } } diff --git a/composer.lock b/composer.lock index 127dec0..b9df162 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ed4a108af4e355cd8ac3748f5131e037", + "content-hash": "3fd57025e88f340effc374940cbd6366", "packages": [ { "name": "dompdf/dompdf", - "version": "v3.1.0", + "version": "v3.1.4", "source": { "type": "git", "url": "https://github.com/dompdf/dompdf.git", - "reference": "a51bd7a063a65499446919286fb18b518177155a" + "reference": "db712c90c5b9868df3600e64e68da62e78a34623" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dompdf/dompdf/zipball/a51bd7a063a65499446919286fb18b518177155a", - "reference": "a51bd7a063a65499446919286fb18b518177155a", + "url": "https://api.github.com/repos/dompdf/dompdf/zipball/db712c90c5b9868df3600e64e68da62e78a34623", + "reference": "db712c90c5b9868df3600e64e68da62e78a34623", "shasum": "" }, "require": { @@ -66,22 +66,22 @@ "homepage": "https://github.com/dompdf/dompdf", "support": { "issues": "https://github.com/dompdf/dompdf/issues", - "source": "https://github.com/dompdf/dompdf/tree/v3.1.0" + "source": "https://github.com/dompdf/dompdf/tree/v3.1.4" }, - "time": "2025-01-15T14:09:04+00:00" + "time": "2025-10-29T12:43:30+00:00" }, { "name": "dompdf/php-font-lib", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/dompdf/php-font-lib.git", - "reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d" + "reference": "a6e9a688a2a80016ac080b97be73d3e10c444c9a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d", - "reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d", + "url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/a6e9a688a2a80016ac080b97be73d3e10c444c9a", + "reference": "a6e9a688a2a80016ac080b97be73d3e10c444c9a", "shasum": "" }, "require": { @@ -89,7 +89,7 @@ "php": "^7.1 || ^8.0" }, "require-dev": { - "symfony/phpunit-bridge": "^3 || ^4 || ^5 || ^6" + "phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11 || ^12" }, "type": "library", "autoload": { @@ -111,31 +111,31 @@ "homepage": "https://github.com/dompdf/php-font-lib", "support": { "issues": "https://github.com/dompdf/php-font-lib/issues", - "source": "https://github.com/dompdf/php-font-lib/tree/1.0.1" + "source": "https://github.com/dompdf/php-font-lib/tree/1.0.2" }, - "time": "2024-12-02T14:37:59+00:00" + "time": "2026-01-20T14:10:26+00:00" }, { "name": "dompdf/php-svg-lib", - "version": "1.0.0", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/dompdf/php-svg-lib.git", - "reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af" + "reference": "8259ffb930817e72b1ff1caef5d226501f3dfeb1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/eb045e518185298eb6ff8d80d0d0c6b17aecd9af", - "reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af", + "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/8259ffb930817e72b1ff1caef5d226501f3dfeb1", + "reference": "8259ffb930817e72b1ff1caef5d226501f3dfeb1", "shasum": "" }, "require": { "ext-mbstring": "*", "php": "^7.1 || ^8.0", - "sabberworm/php-css-parser": "^8.4" + "sabberworm/php-css-parser": "^8.4 || ^9.0" }, "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5" + "phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11" }, "type": "library", "autoload": { @@ -157,9 +157,116 @@ "homepage": "https://github.com/dompdf/php-svg-lib", "support": { "issues": "https://github.com/dompdf/php-svg-lib/issues", - "source": "https://github.com/dompdf/php-svg-lib/tree/1.0.0" + "source": "https://github.com/dompdf/php-svg-lib/tree/1.0.2" }, - "time": "2024-04-29T13:26:35+00:00" + "time": "2026-01-02T16:01:13+00:00" + }, + { + "name": "kigkonsult/icalcreator", + "version": "v2.41.92", + "source": { + "type": "git", + "url": "https://github.com/iCalcreator/iCalcreator.git", + "reference": "a4d35d7a58c08b816dc8a7778db19f461c1429bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/iCalcreator/iCalcreator/zipball/a4d35d7a58c08b816dc8a7778db19f461c1429bd", + "reference": "a4d35d7a58c08b816dc8a7778db19f461c1429bd", + "shasum": "" + }, + "require": { + "ext-intl": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-simplexml": "*", + "ext-zlib": "*", + "php": ">=8.0" + }, + "require-dev": { + "degraciamathieu/php-arguments-detector": ">=0.5.0", + "ext-xdebug": "*", + "phpcompatibility/php-compatibility": ">=9.3.5", + "phpmd/phpmd": ">=2.13.0", + "phpstan/phpstan": ">=0.9.3", + "phpunit/phpunit": ">=6.5.13", + "squizlabs/php_codesniffer": ">=3.5.5", + "vimeo/psalm": "*" + }, + "type": "library", + "autoload": { + "files": [ + "autoload.php" + ], + "psr-4": { + "Kigkonsult\\Icalcreator\\": [ + "src/" + ] + }, + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Kjell-Inge Gustafsson", + "email": "ical@kigkonsult.se" + } + ], + "description": "iCalcreator is the PHP implementation of rfc2445/rfc5545 and rfc updates, management of calendar information", + "keywords": [ + "alarm", + "availability", + "calendar", + "daylight", + "event", + "ical", + "json", + "location", + "management", + "participant", + "resource", + "rfc2445", + "rfc5545", + "rfc5870", + "rfc6321", + "rfc6868", + "rfc7529", + "rfc7808", + "rfc7953", + "rfc7986", + "rfc9073", + "rfc9074", + "standard", + "todo", + "vCalendar", + "valarm", + "vevent", + "vfreebusy", + "vjournal", + "vtimezone", + "vtodo", + "xml" + ], + "support": { + "issues": "https://github.com/iCalcreator/iCalcreator/issues", + "source": "https://github.com/iCalcreator/iCalcreator/tree/v2.41.92" + }, + "funding": [ + { + "url": "https://paypal.me/kigkonsult", + "type": "other" + }, + { + "url": "https://www.buymeacoffee.com/kigkonsult", + "type": "other" + } + ], + "time": "2025-01-17T10:06:29+00:00" }, { "name": "masterminds/html5", @@ -230,18 +337,18 @@ }, { "name": "npm-asset/fullcalendar", - "version": "6.1.18", + "version": "6.1.20", "dist": { "type": "tar", - "url": "https://registry.npmjs.org/fullcalendar/-/fullcalendar-6.1.18.tgz" + "url": "https://registry.npmjs.org/fullcalendar/-/fullcalendar-6.1.20.tgz" }, "require": { - "npm-asset/fullcalendar--core": "~6.1.18", - "npm-asset/fullcalendar--daygrid": "~6.1.18", - "npm-asset/fullcalendar--interaction": "~6.1.18", - "npm-asset/fullcalendar--list": "~6.1.18", - "npm-asset/fullcalendar--multimonth": "~6.1.18", - "npm-asset/fullcalendar--timegrid": "~6.1.18" + "npm-asset/fullcalendar--core": "~6.1.20", + "npm-asset/fullcalendar--daygrid": "~6.1.20", + "npm-asset/fullcalendar--interaction": "~6.1.20", + "npm-asset/fullcalendar--list": "~6.1.20", + "npm-asset/fullcalendar--multimonth": "~6.1.20", + "npm-asset/fullcalendar--timegrid": "~6.1.20" }, "type": "npm-asset", "license": [ @@ -250,10 +357,10 @@ }, { "name": "npm-asset/fullcalendar--core", - "version": "6.1.19", + "version": "6.1.20", "dist": { "type": "tar", - "url": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.19.tgz" + "url": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.20.tgz" }, "require": { "npm-asset/preact": "~10.12.1" @@ -265,10 +372,10 @@ }, { "name": "npm-asset/fullcalendar--daygrid", - "version": "6.1.18", + "version": "6.1.20", "dist": { "type": "tar", - "url": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.18.tgz" + "url": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.20.tgz" }, "type": "npm-asset", "license": [ @@ -277,10 +384,10 @@ }, { "name": "npm-asset/fullcalendar--interaction", - "version": "6.1.18", + "version": "6.1.20", "dist": { "type": "tar", - "url": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-6.1.18.tgz" + "url": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-6.1.20.tgz" }, "type": "npm-asset", "license": [ @@ -289,10 +396,10 @@ }, { "name": "npm-asset/fullcalendar--list", - "version": "6.1.18", + "version": "6.1.20", "dist": { "type": "tar", - "url": "https://registry.npmjs.org/@fullcalendar/list/-/list-6.1.18.tgz" + "url": "https://registry.npmjs.org/@fullcalendar/list/-/list-6.1.20.tgz" }, "type": "npm-asset", "license": [ @@ -301,13 +408,13 @@ }, { "name": "npm-asset/fullcalendar--multimonth", - "version": "6.1.18", + "version": "6.1.20", "dist": { "type": "tar", - "url": "https://registry.npmjs.org/@fullcalendar/multimonth/-/multimonth-6.1.18.tgz" + "url": "https://registry.npmjs.org/@fullcalendar/multimonth/-/multimonth-6.1.20.tgz" }, "require": { - "npm-asset/fullcalendar--daygrid": "~6.1.18" + "npm-asset/fullcalendar--daygrid": "~6.1.20" }, "type": "npm-asset", "license": [ @@ -316,13 +423,13 @@ }, { "name": "npm-asset/fullcalendar--timegrid", - "version": "6.1.18", + "version": "6.1.20", "dist": { "type": "tar", - "url": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-6.1.18.tgz" + "url": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-6.1.20.tgz" }, "require": { - "npm-asset/fullcalendar--daygrid": "~6.1.18" + "npm-asset/fullcalendar--daygrid": "~6.1.20" }, "type": "npm-asset", "license": [ @@ -343,25 +450,33 @@ }, { "name": "sabberworm/php-css-parser", - "version": "v8.9.0", + "version": "v9.1.0", "source": { "type": "git", "url": "https://github.com/MyIntervals/PHP-CSS-Parser.git", - "reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9" + "reference": "1b363fdbdc6dd0ca0f4bf98d3a4d7f388133f1fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/d8e916507b88e389e26d4ab03c904a082aa66bb9", - "reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9", + "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/1b363fdbdc6dd0ca0f4bf98d3a4d7f388133f1fb", + "reference": "1b363fdbdc6dd0ca0f4bf98d3a4d7f388133f1fb", "shasum": "" }, "require": { "ext-iconv": "*", - "php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "^7.2.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "thecodingmachine/safe": "^1.3 || ^2.5 || ^3.3" }, "require-dev": { - "phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.41", - "rawr/cross-data-providers": "^2.0.0" + "php-parallel-lint/php-parallel-lint": "1.4.0", + "phpstan/extension-installer": "1.4.3", + "phpstan/phpstan": "1.12.28 || 2.1.25", + "phpstan/phpstan-phpunit": "1.4.2 || 2.0.7", + "phpstan/phpstan-strict-rules": "1.6.2 || 2.0.6", + "phpunit/phpunit": "8.5.46", + "rawr/phpunit-data-provider": "3.3.1", + "rector/rector": "1.2.10 || 2.1.7", + "rector/type-perfect": "1.0.0 || 2.1.0" }, "suggest": { "ext-mbstring": "for parsing UTF-8 CSS" @@ -369,7 +484,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "9.0.x-dev" + "dev-main": "9.2.x-dev" } }, "autoload": { @@ -403,15 +518,275 @@ ], "support": { "issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues", - "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.9.0" + "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v9.1.0" + }, + "time": "2025-09-14T07:37:21+00:00" + }, + { + "name": "thecodingmachine/safe", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/thecodingmachine/safe.git", + "reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/705683a25bacf0d4860c7dea4d7947bfd09eea19", + "reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpstan/phpstan": "^2", + "phpunit/phpunit": "^10", + "squizlabs/php_codesniffer": "^3.2" + }, + "type": "library", + "autoload": { + "files": [ + "lib/special_cases.php", + "generated/apache.php", + "generated/apcu.php", + "generated/array.php", + "generated/bzip2.php", + "generated/calendar.php", + "generated/classobj.php", + "generated/com.php", + "generated/cubrid.php", + "generated/curl.php", + "generated/datetime.php", + "generated/dir.php", + "generated/eio.php", + "generated/errorfunc.php", + "generated/exec.php", + "generated/fileinfo.php", + "generated/filesystem.php", + "generated/filter.php", + "generated/fpm.php", + "generated/ftp.php", + "generated/funchand.php", + "generated/gettext.php", + "generated/gmp.php", + "generated/gnupg.php", + "generated/hash.php", + "generated/ibase.php", + "generated/ibmDb2.php", + "generated/iconv.php", + "generated/image.php", + "generated/imap.php", + "generated/info.php", + "generated/inotify.php", + "generated/json.php", + "generated/ldap.php", + "generated/libxml.php", + "generated/lzf.php", + "generated/mailparse.php", + "generated/mbstring.php", + "generated/misc.php", + "generated/mysql.php", + "generated/mysqli.php", + "generated/network.php", + "generated/oci8.php", + "generated/opcache.php", + "generated/openssl.php", + "generated/outcontrol.php", + "generated/pcntl.php", + "generated/pcre.php", + "generated/pgsql.php", + "generated/posix.php", + "generated/ps.php", + "generated/pspell.php", + "generated/readline.php", + "generated/rnp.php", + "generated/rpminfo.php", + "generated/rrd.php", + "generated/sem.php", + "generated/session.php", + "generated/shmop.php", + "generated/sockets.php", + "generated/sodium.php", + "generated/solr.php", + "generated/spl.php", + "generated/sqlsrv.php", + "generated/ssdeep.php", + "generated/ssh2.php", + "generated/stream.php", + "generated/strings.php", + "generated/swoole.php", + "generated/uodbc.php", + "generated/uopz.php", + "generated/url.php", + "generated/var.php", + "generated/xdiff.php", + "generated/xml.php", + "generated/xmlrpc.php", + "generated/yaml.php", + "generated/yaz.php", + "generated/zip.php", + "generated/zlib.php" + ], + "classmap": [ + "lib/DateTime.php", + "lib/DateTimeImmutable.php", + "lib/Exceptions/", + "generated/Exceptions/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHP core functions that throw exceptions instead of returning FALSE on error", + "support": { + "issues": "https://github.com/thecodingmachine/safe/issues", + "source": "https://github.com/thecodingmachine/safe/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://github.com/OskarStark", + "type": "github" + }, + { + "url": "https://github.com/shish", + "type": "github" + }, + { + "url": "https://github.com/silasjoisten", + "type": "github" + }, + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2026-02-04T18:08:13+00:00" + } + ], + "packages-dev": [ + { + "name": "elgg/sniffs", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/Elgg/elgg-coding-standards.git", + "reference": "7d757f3fbebf7686fb75f500ffb38102e0131865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Elgg/elgg-coding-standards/zipball/7d757f3fbebf7686fb75f500ffb38102e0131865", + "reference": "7d757f3fbebf7686fb75f500ffb38102e0131865", + "shasum": "" + }, + "require": { + "squizlabs/php_codesniffer": "^3.9.0" + }, + "default-branch": true, + "type": "library", + "autoload": { + "psr-0": { + "Elgg_Sniffs_": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-only" + ], + "description": "Elgg coding standards", + "support": { + "issues": "https://github.com/Elgg/elgg-coding-standards/issues", + "source": "https://github.com/Elgg/elgg-coding-standards/tree/master" + }, + "time": "2025-06-19T09:53:40+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.13.5", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4", + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" }, - "time": "2025-07-11T13:20:48+00:00" + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "bin": [ + "bin/phpcbf", + "bin/phpcs" + ], + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-11-04T16:30:35+00:00" } ], - "packages-dev": [], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": { + "elgg/sniffs": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": {}, diff --git a/elgg-plugin.php b/elgg-plugin.php index 6cf9c67..a65397c 100644 --- a/elgg-plugin.php +++ b/elgg-plugin.php @@ -35,6 +35,7 @@ 'show_service_outlookcom' => 1, 'show_service_outlook' => 1, 'show_service_appleical' => 1, + 'ical_direct' => 1, ], 'entities' => [ [ @@ -168,6 +169,10 @@ 'path' => '/event/unsubscribe/request/{guid}', 'resource' => 'event/unsubscribe/request', ], + 'default:object:event:export' => [ + 'path' => '/event/export/{guid}', + 'resource' => 'event/export', + ], 'default:object:event:register' => [ 'path' => '/event/register/{guid}/{relation?}', 'resource' => 'event/register', @@ -225,6 +230,14 @@ 'path' => '/event', 'resource' => 'event/upcoming', ], + 'collection:ical:export' => [ + 'path' => '/event/export/ical', + 'resource' => 'event/export/ical', + ], + 'collection:ical:import' => [ + 'path' => '/event/import/ical', + 'resource' => 'event/import/ical', + ], 'default:event_manager:calendar' => [ 'path' => '/event_manager/calendar', 'controller' => \ColdTrick\EventManager\Controllers\Calendar::class, @@ -258,6 +271,9 @@ 'event_manager/day/edit' => [], 'event_manager/delete_program_item' => [], + + 'event_manager/export/ical' => [], + 'event_manager/import/ical' => [], ], 'widgets' => [ 'events' => [ @@ -338,6 +354,7 @@ '\ColdTrick\EventManager\Menus\Entity::registerAttendeeActions' => [], '\ColdTrick\EventManager\Menus\Entity::registerEventUnsubscribe' => ['priority' => 600], '\ColdTrick\EventManager\Menus\Entity::registerMailAttendees' => [], + '\ColdTrick\EventManager\Menus\Entity::registerICalExport' => [], ], 'menu:event_files' => [ '\ColdTrick\EventManager\Menus\EventFiles::registerFiles' => [], diff --git a/languages/de.php b/languages/de.php index c42dbaf..e4423dc 100644 --- a/languages/de.php +++ b/languages/de.php @@ -34,7 +34,7 @@ 'event_manager:personwaitinglist' => 'Personen auf der Warteliste', 'event_manager:peoplewaitinglist' => 'Personen auf der Warteliste', 'event_manager:registration:view:savetopdf' => 'als PDF speichern', - 'event_manager:edit:title' => ' erstelle / bearbeite Veranstaltung', + 'event_manager:edit:title' => 'Veranstaltung bearbeiten', 'event_manager:edit:upload:title' => 'Datei(en) zu deiner Veranstaltung hinzufügen', 'event_manager:edit:form:tabs:profile' => 'Profile', 'event_manager:edit:form:tabs:profile:toggle' => 'Hier können Sie weitere Veranstaltungs-Details konfigurieren', @@ -301,4 +301,39 @@ 'event_manager:addevent:mail:service:outlook' => 'Outlook', 'event_manager:addevent:mail:service:outlookcom' => 'Outlook.com', 'event_manager:addevent:mail:service:yahoo' => 'Yahoo', + + 'event_manager:settings:ical_direct' => 'Integriertes iCal', + 'event_manager:settings:ical_direct:enable' => 'Die internen iCal-Features aktivieren', + 'event_manager:settings:ical_direct:enable:help' => 'Die integrierten iCal-Features für Export und Import aktivieren', + 'event_manager:ical_direct:export' => 'iCal-Export', + 'event_manager:ical_direct:export:calendar_type' => 'Kalender', + 'event_manager:ical_direct:export:calendar_type:help' => 'Wöhle den zu exportierenden Kalender aus', + 'event_manager:ical_direct:calendar_type:all' => 'Alle', + 'event_manager:ical_direct:calendar_type:group' => 'Gruppe', + 'event_manager:ical_direct:calendar_type:owner' => 'Benutzer*in', + 'event_manager:ical_direct:export:owner' => 'Benutzer*in', + 'event_manager:ical_direct:export:owner:help' => 'Wähle die Benutzer*in aus, deren Kalender exportiert werden soll', + 'event_manager:ical_direct:export:group' => 'Guppe', + 'event_manager:ical_direct:export:group:help' => 'Wähle die Gruppe aus, deren Kalender exportiert werden soll', + 'event_manager:ical_direct:export:timespan' => 'Zeitbereich für den Export', + 'event_manager:ical_direct:export:start' => 'Startdatum', + 'event_manager:ical_direct:export:end' => 'Enddatum', + 'event_manager:ical_direct:export:region' => 'Region', + 'event_manager:ical_direct:export:region:help' => 'Wähle eine oder mehr Regionen aus, um nach diesen zu filtern', + 'event_manager:ical_direct:export:type' => 'Typ', + 'event_manager:ical_direct:export:type:help' => 'Wähle einen oder mehrere Eventtypen aus, um nach diesen zu filtern', + 'event_manager:ical_direct:export:submit' => 'Exportieren', + + 'event_manager:ical_direct:import' => 'iCal importieren', + 'event_manager:ical_direct:import:calendar_type' => 'Kalender', + 'event_manager:ical_direct:import:calendar_type:help' => 'Wähle den Zielkalender für den Import', + 'event_manager:ical_direct:import:owner' => 'Benutzer*in', + 'event_manager:ical_direct:import:owner:help' => 'Wähle die Benutzer*in in deren Kalender die Datei importiert wird', + 'event_manager:ical_direct:import:group' => 'Gruppe', + 'event_manager:ical_direct:import:group:help' => 'Wähle die Gruppe in deren Kalender die Datei importiert wird', + 'event_manager:ical_direct:import:file' => 'Import-Datei', + 'event_manager:ical_direct:import:file:help' => 'ICS-Datei für den Import', + 'event_manager:ical_direct:import:submit' => 'Importieren', + 'event_manager:ical_direct:import:success' => '%d Einträge erfolgreich importiert', + 'event_manager:ical_direct:import:failure' => 'Konnte Datei nicht importieren: %s', ); diff --git a/languages/en.php b/languages/en.php index 79dc411..a87b2fa 100644 --- a/languages/en.php +++ b/languages/en.php @@ -452,5 +452,39 @@ 'event_manager:upgrade:2023030700:title' => "Move event header images to new image location", 'event_manager:upgrade:2023030700:description' => "In Elgg 5 there is built in header image support. This migration moves old icons uploaded with events to this new location.", - + 'event_manager:settings:ical_direct' => 'Embedded iCal', + 'event_manager:settings:ical_direct:enable' => 'Enable embedded iCal features', + 'event_manager:settings:ical_direct:enable:help' => 'Enable the embeded iCal-features for export and import', + 'event_manager:ical_direct:export' => 'Export to iCal', + 'event_manager:ical_direct:export:calendar_type' => 'Calendar', + 'event_manager:ical_direct:export:calendar_type:help' => 'Select the calendar to export', + 'event_manager:ical_direct:calendar_type:all' => 'All', + 'event_manager:ical_direct:calendar_type:group' => 'Group', + 'event_manager:ical_direct:calendar_type:owner' => 'User', + 'event_manager:ical_direct:export:owner' => 'User', + 'event_manager:ical_direct:export:owner:help' => 'Select the user whose calendar will be exported', + 'event_manager:ical_direct:export:group' => 'Group', + 'event_manager:ical_direct:export:group:help' => 'Select the group which calendar will be exported', + 'event_manager:ical_direct:export:timespan' => 'Export timespan', + 'event_manager:ical_direct:export:start' => 'Start date', + 'event_manager:ical_direct:export:end' => 'End date', + 'event_manager:ical_direct:export:region' => 'Region', + 'event_manager:ical_direct:export:region:help' => 'Select one or more regions to filter for', + 'event_manager:ical_direct:export:type' => 'Type', + 'event_manager:ical_direct:export:type:help' => 'Select one or more event types to filter for', + 'event_manager:ical_direct:export:submit' => 'Export', + + 'event_manager:ical_direct:import' => 'Import iCal', + 'event_manager:ical_direct:import:calendar_type' => 'Calendar', + 'event_manager:ical_direct:import:calendar_type:help' => 'Select the calendar to import into', + 'event_manager:ical_direct:import:owner' => 'User', + 'event_manager:ical_direct:import:owner:help' => 'Select the user whose calendar will be used for the import', + 'event_manager:ical_direct:import:group' => 'Group', + 'event_manager:ical_direct:import:group:help' => 'Select the group which calendar will be user for the import', + 'event_manager:ical_direct:import:file' => 'Import file', + 'event_manager:ical_direct:import:file:help' => 'ICS file to import', + 'event_manager:ical_direct:import:submit' => 'Import', + 'event_manager:ical_direct:import:success' => 'Successfully imported %d entries', + 'event_manager:ical_direct:import:failure' => 'Could not import calendar file: %s', + ); diff --git a/views/default/forms/event_manager/export/ical.mjs b/views/default/forms/event_manager/export/ical.mjs new file mode 100644 index 0000000..9510c3d --- /dev/null +++ b/views/default/forms/event_manager/export/ical.mjs @@ -0,0 +1,70 @@ +import 'jquery'; + +var calendar_type_selector = 'select[name="calendar_type"]'; + +/** + * Toggle owner input visibility based on the value + * of the calendar type selector + * + * @param {jQuery} $calendar_type Select object + * @returns {void} + */ +function toggleOwner($calendar_type) { + var $form = $calendar_type.closest('form'); + if (!$form.length) { + return; + } + + if ($calendar_type[0].value === "owner") { + $('[name="owner"]', $form).each(function() { + $(this).prop('required', true); + $(this).closest('.elgg-field').removeClass('hidden'); + }); + + } else { + $('[name="owner"]', $form).each(function() { + $(this).prop('required', false); + $(this).closest('.elgg-field').addClass('hidden'); + }); + } +} + +$(document).on('change', calendar_type_selector, function() { + toggleOwner($(this)); +}); + +toggleOwner($(calendar_type_selector)); + + +/** + * Toggle group input visibility based on the value + * of the calendar type selector + * + * @param {jQuery} $calendar_type Select object + * @returns {void} + */ +function toggleGroup($calendar_type) { + var $form = $calendar_type.closest('form'); + if (!$form.length) { + return; + } + + if ($calendar_type[0].value === "group") { + $('[name="group"]', $form).each(function() { + $(this).prop('required', true); + $(this).closest('.elgg-field').removeClass('hidden'); + }); + + } else { + $('[name="group"]', $form).each(function() { + $(this).prop('required', false); + $(this).closest('.elgg-field').addClass('hidden'); + }); + } +} + +$(document).on('change', calendar_type_selector, function() { + toggleGroup($(this)); +}); + +toggleGroup($(calendar_type_selector)); diff --git a/views/default/forms/event_manager/export/ical.php b/views/default/forms/event_manager/export/ical.php new file mode 100644 index 0000000..99559cd --- /dev/null +++ b/views/default/forms/event_manager/export/ical.php @@ -0,0 +1,143 @@ + [ + 'text' => elgg_echo('event_manager:ical_direct:calendar_type:all'), + 'selected' => $calendar_type_selected == 'all', + 'value' => 'all', + ], + 'group' => [ + 'text' => elgg_echo('event_manager:ical_direct:calendar_type:group'), + 'selected' => $calendar_type_selected == 'group', + 'value' => 'group', + ], + 'owner' => [ + 'text' => elgg_echo('event_manager:ical_direct:calendar_type:owner'), + 'selected' => $calendar_type_selected == 'owner', + 'value' => 'owner', + ], +]; + +echo elgg_view_field([ + '#type' => 'select', + '#label' => elgg_echo('event_manager:ical_direct:export:calendar_type'), + '#help' => elgg_echo('event_manager:ical_direct:export:calendar_type:help'), + 'required' => true, + 'name' => 'calendar_type', + 'options_values' => $calendar_type_options, +]); + +$owner = ''; + +if (array_key_exists('username', $route_parameters)) { + $owner = elgg_get_user_by_username($route_parameters['username'])->getGUID(); +} + +echo elgg_view_field([ + '#type' => 'userpicker', + '#label' => elgg_echo('event_manager:ical_direct:export:owner'), + '#help' => elgg_echo('event_manager:ical_direct:export:owner:help'), + 'required' => true, + 'name' => 'owner', + 'limit' => 1, + 'values' => [$owner], +]); + + +$group = ''; + +if (array_key_exists('guid', $route_parameters)) { + $group = $route_parameters['guid']; +} + +echo elgg_view_field([ + '#type' => 'grouppicker', + '#label' => elgg_echo('event_manager:ical_direct:export:group'), + '#help' => elgg_echo('event_manager:ical_direct:export:group:help'), + 'required' => true, + 'name' => 'group', + 'limit' => 1, + 'values' => [$group], +]); + +// Filter timespan + +echo elgg_view_field([ + '#type' => 'fieldset', + 'legend' => elgg_echo('event_manager:ical_direct:export:timespan'), + 'fields' => [ + [ + '#type' => 'date', + '#label' => elgg_echo('event_manager:ical_direct:export:start'), + 'required' => true, + 'name' => 'start_date', + 'value' => time(), + ], + [ + '#type' => 'date', + '#label' => elgg_echo('event_manager:ical_direct:export:end'), + 'required' => true, + 'name' => 'end_date', + 'value' => DateTime::createFromFormat('U', time())->add(DateInterval::createFromDateString('1 month'))->getTimestamp() + ] + ] +]); + +// Filter region + +$region_settings = trim((string) elgg_get_plugin_setting('region_list', 'event_manager')); +$region_list = explode(',', $region_settings); +$region_options = array_reduce($region_list, function ($options, $value){ + $options[$value] = [ + 'text' => $value + ]; + return $options; +}, []); +echo elgg_view_field([ + '#type' => 'select', + '#label' => elgg_echo('event_manager:ical_direct:export:region'), + '#help' => elgg_echo('event_manager:ical_direct:export:region:help'), + 'required' => false, + 'multiple' => true, + 'name' => 'region', + 'options_values' => $region_options, +]); + +// Filter type + +$type_settings = trim((string) elgg_get_plugin_setting('type_list', 'event_manager')); +$type_list = explode(',', $type_settings); +$type_options = array_reduce($type_list, function ($options, $value){ + $options[$value] = [ + 'text' => $value + ]; + return $options; +}, []); +echo elgg_view_field([ + '#type' => 'select', + '#label' => elgg_echo('event_manager:ical_direct:export:type'), + '#help' => elgg_echo('event_manager:ical_direct:export:type:help'), + 'required' => false, + 'multiple' => true, + 'name' => 'event_type', + 'options_values' => $type_options, +]); + +elgg_set_form_footer( + elgg_view_field([ + '#type' => 'submit', + 'text' => elgg_echo('event_manager:ical_direct:export:submit'), + ]) +); diff --git a/views/default/forms/event_manager/import/ical.mjs b/views/default/forms/event_manager/import/ical.mjs new file mode 100644 index 0000000..9510c3d --- /dev/null +++ b/views/default/forms/event_manager/import/ical.mjs @@ -0,0 +1,70 @@ +import 'jquery'; + +var calendar_type_selector = 'select[name="calendar_type"]'; + +/** + * Toggle owner input visibility based on the value + * of the calendar type selector + * + * @param {jQuery} $calendar_type Select object + * @returns {void} + */ +function toggleOwner($calendar_type) { + var $form = $calendar_type.closest('form'); + if (!$form.length) { + return; + } + + if ($calendar_type[0].value === "owner") { + $('[name="owner"]', $form).each(function() { + $(this).prop('required', true); + $(this).closest('.elgg-field').removeClass('hidden'); + }); + + } else { + $('[name="owner"]', $form).each(function() { + $(this).prop('required', false); + $(this).closest('.elgg-field').addClass('hidden'); + }); + } +} + +$(document).on('change', calendar_type_selector, function() { + toggleOwner($(this)); +}); + +toggleOwner($(calendar_type_selector)); + + +/** + * Toggle group input visibility based on the value + * of the calendar type selector + * + * @param {jQuery} $calendar_type Select object + * @returns {void} + */ +function toggleGroup($calendar_type) { + var $form = $calendar_type.closest('form'); + if (!$form.length) { + return; + } + + if ($calendar_type[0].value === "group") { + $('[name="group"]', $form).each(function() { + $(this).prop('required', true); + $(this).closest('.elgg-field').removeClass('hidden'); + }); + + } else { + $('[name="group"]', $form).each(function() { + $(this).prop('required', false); + $(this).closest('.elgg-field').addClass('hidden'); + }); + } +} + +$(document).on('change', calendar_type_selector, function() { + toggleGroup($(this)); +}); + +toggleGroup($(calendar_type_selector)); diff --git a/views/default/forms/event_manager/import/ical.php b/views/default/forms/event_manager/import/ical.php new file mode 100644 index 0000000..37c9a89 --- /dev/null +++ b/views/default/forms/event_manager/import/ical.php @@ -0,0 +1,87 @@ + [ + 'text' => elgg_echo('event_manager:ical_direct:calendar_type:all'), + 'selected' => $calendar_type_selected == 'all', + 'value' => 'all', + ], + 'group' => [ + 'text' => elgg_echo('event_manager:ical_direct:calendar_type:group'), + 'selected' => $calendar_type_selected == 'group', + 'value' => 'group', + ], + 'owner' => [ + 'text' => elgg_echo('event_manager:ical_direct:calendar_type:owner'), + 'selected' => $calendar_type_selected == 'owner', + 'value' => 'owner', + ], +]; + +echo elgg_view_field([ + '#type' => 'select', + '#label' => elgg_echo('event_manager:ical_direct:import:calendar_type'), + '#help' => elgg_echo('event_manager:ical_direct:import:calendar_type:help'), + 'required' => true, + 'name' => 'calendar_type', + 'options_values' => $calendar_type_options, +]); + +$owner = ''; + +if (array_key_exists('username', $route_parameters)) { + $owner = elgg_get_user_by_username($route_parameters['username'])->getGUID(); +} + +echo elgg_view_field([ + '#type' => 'userpicker', + '#label' => elgg_echo('event_manager:ical_direct:import:owner'), + '#help' => elgg_echo('event_manager:ical_direct:import:owner:help'), + 'required' => true, + 'name' => 'owner', + 'limit' => 1, + 'values' => [$owner], +]); + + +$group = ''; + +if (array_key_exists('guid', $route_parameters)) { + $group = $route_parameters['guid']; +} + +echo elgg_view_field([ + '#type' => 'grouppicker', + '#label' => elgg_echo('event_manager:ical_direct:import:group'), + '#help' => elgg_echo('event_manager:ical_direct:import:group:help'), + 'required' => true, + 'name' => 'group', + 'limit' => 1, + 'values' => [$group], +]); + +echo elgg_view_field([ + '#type' => 'file', + '#label' => elgg_echo('event_manager:ical_direct:import:file'), + '#help' => elgg_echo('event_manager:ical_direct:import:file:help'), + 'name' => 'import' +]); + +elgg_set_form_footer( + elgg_view_field([ + '#type' => 'submit', + 'text' => elgg_echo('event_manager:ical_direct:import:submit'), + ]) +); diff --git a/views/default/plugins/event_manager/settings.php b/views/default/plugins/event_manager/settings.php index 59be23c..4bb0354 100644 --- a/views/default/plugins/event_manager/settings.php +++ b/views/default/plugins/event_manager/settings.php @@ -75,6 +75,17 @@ echo elgg_view_module('info', elgg_echo('event_manager:settings:maps'), $maps); +// iCal direct features +$ical_direct .= elgg_view_field([ + '#type' => 'switch', + '#label' => elgg_echo('event_manager:settings:ical_direct:enable'), + '#help' => elgg_echo('event_manager:settings:ical_direct:enable:help'), + 'name' => 'params[ical_direct]', + 'value' => $plugin->ical_direct, +]); + +echo elgg_view_module('info', elgg_echo('event_manager:settings:ical_direct'), $ical_direct); + // AddEvent options $add_event = elgg_view_field([ '#type' => 'select', diff --git a/views/default/resources/event/export/ical.php b/views/default/resources/event/export/ical.php new file mode 100644 index 0000000..e77a4c6 --- /dev/null +++ b/views/default/resources/event/export/ical.php @@ -0,0 +1,29 @@ +guid, 'group'); + elgg_group_tool_gatekeeper('event_manager'); + + elgg_push_collection_breadcrumbs('object', \Event::SUBTYPE, $page_owner); +} else { + $page_owner = null; + elgg_set_page_owner_guid(0); +} + +$content = elgg_view_form( + 'event_manager/export/ical', + [ + 'prevent_double_submit' => false + ], + [ + 'list_route' => get_input('list_route'), + 'route_parameters' => get_input('route_parameters'), + ] +); + +echo elgg_view_page(elgg_echo('event_manager:ical_direct:export'), [ + 'content' => $content, + 'filter_id' => 'events', + 'filter_value' => 'export-ical', +]); diff --git a/views/default/resources/event/import/ical.php b/views/default/resources/event/import/ical.php new file mode 100644 index 0000000..a2b2151 --- /dev/null +++ b/views/default/resources/event/import/ical.php @@ -0,0 +1,27 @@ +guid, 'group'); + elgg_group_tool_gatekeeper('event_manager'); + + elgg_push_collection_breadcrumbs('object', \Event::SUBTYPE, $page_owner); +} else { + $page_owner = null; + elgg_set_page_owner_guid(0); +} + +$content = elgg_view_form( + 'event_manager/import/ical', + [], + [ + 'list_route' => get_input('list_route'), + 'route_parameters' => get_input('route_parameters'), + ] +); + +echo elgg_view_page(elgg_echo('event_manager:ical_direct:import'), [ + 'content' => $content, + 'filter_id' => 'events', + 'filter_value' => 'import-ical', +]); diff --git a/views/ical/resources/event/export.php b/views/ical/resources/event/export.php new file mode 100644 index 0000000..133953c --- /dev/null +++ b/views/ical/resources/event/export.php @@ -0,0 +1,30 @@ + 'https://github.com/ColdTrick/event_manager', + ] +) + ->setMethod(Vcalendar::PUBLISH) + ->setXprop(Vcalendar::X_WR_CALNAME, 'Exported event') + ->setXprop(Vcalendar::X_WR_CALDESC, 'Single exported event from event_manager') + ->setXprop(Vcalendar::X_WR_RELCALID, elgg_get_site_url() . '#' . $guid) + ->setXprop(Vcalendar::X_WR_TIMEZONE, date_default_timezone_get()); + + +/** @noinspection PhpUnhandledExceptionInspection */ +$vcalendar->setComponent($event->toVEvent()); + +/** @noinspection PhpUnhandledExceptionInspection */ +echo $vcalendar->returnCalendar(true, false, false, 'export.ics');