Giter VIP home page Giter VIP logo

icalendar-generator's Introduction

Generate calendars in the iCalendar format

Latest Version on Packagist Tests Check & fix styling Psalm Total Downloads

Want to create online calendars so that you can display them on an iPhone's calendar app or in Google Calendar? This can be done by generating calendars in the iCalendar format (RFC 5545), a textual format that can be loaded by different applications.

The format of such calendars is defined in RFC 5545, which is not a pleasant reading experience. This package implements RFC 5545 and some extensions from RFC 7986 to provide you an easy to use API for creating calendars. It's not our intention to implement these RFC's entirely but to provide a straightforward API that's easy to use.

Here's an example of how to use it:

use Spatie\IcalendarGenerator\Components\Calendar;
use Spatie\IcalendarGenerator\Components\Event;

Calendar::create('Laracon online')
    ->event(Event::create('Creating calender feeds')
        ->startsAt(new DateTime('6 March 2019 15:00'))
        ->endsAt(new DateTime('6 March 2019 16:00'))
    )
    ->get();

The above code will generate this string:

BEGIN:VCALENDAR
VERSION:2.0
PRODID:spatie/icalendar-generator
NAME:Laracon online
X-WR-CALNAME:Laracon online
BEGIN:VEVENT
UID:5ef5c3f64cb2c
DTSTAMP;TZID=UTC:20200626T094630
SUMMARY:Creating calendar feeds
DTSTART:20190306T150000Z
DTEND:20190306T160000Z
DTSTAMP:20190419T135034Z
END:VEVENT
END:VCALENDAR

Support us

We invest a lot of resources into creating best in class open source packages. You can support us by buying one of our paid products.

We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on our contact page. We publish all received postcards on our virtual postcard wall.

Installation

You can install the package via composer:

composer require spatie/icalendar-generator

Upgrading

There were some substantial changes between v1 and v2 of the package. Check the upgrade guide for more information.

Usage

Here's how you can create a calendar:

$calendar = Calendar::create();

You can give a name to the calendar:

$calendar = Calendar::create('Laracon Online');

A description can be added to a calendar:

$calendar = Calendar::create()
    ->name('Laracon Online')
    ->description('Experience Laracon all around the world');

In the end, you want to convert your calendar to text so that it can be streamed or downloaded to the user. Here's how you do that:

Calendar::create('Laracon Online')->get(); // BEGIN:VCALENDAR ...

When streaming a calendar to an application, it is possible to set the calendar's refresh interval by duration in minutes. When setting this, the calendar application will check your server every time after the specified duration for changes to the calendar:

Calendar::create('Laracon Online')
    ->refreshInterval(5)
    ...

Event

An event can be created as follows. A name is not required, but a start date should always be given:

Event::create('Laracon Online')
    ->startsAt(new DateTime('6 march 2019'));

You can set the following properties on an event:

Event::create()
    ->name('Laracon Online')
    ->description('Experience Laracon all around the world')
    ->uniqueIdentifier('A unique identifier can be set here')
    ->createdAt(new DateTime('6 march 2019'))
    ->startsAt(new DateTime('6 march 2019 15:00'))
    ->endsAt(new DateTime('6 march 2019 16:00'));

Want to create an event quickly with a start and end date?

Event::create('Laracon Online')
    ->period(new DateTime('6 march 2019'), new DateTime('7 march 2019'));

You can add a location to an event a such:

Event::create()
    ->address('Kruikstraat 22, 2018 Antwerp, Belgium')
    ->addressName('Spatie HQ')
    ->coordinates(51.2343, 4.4287)
    ...

You can set the organizer of an event, the email address is required, but the name can be omitted:

Event::create()
    ->organizer('[email protected]', 'Ruben')
    ...

Attendees of an event can be added as such:

Event::create()
    ->attendee('[email protected]') // only an email address is required
    ->attendee('[email protected]', 'Brent')
    ...

You can also set the participation status of an attendee:

Event::create()
    ->attendee('[email protected]', 'Ruben', ParticipationStatus::accepted())
    ...

There are five participation statuses:

  • ParticipationStatus::accepted()
  • ParticipationStatus::declined()
  • ParticipationStatus::tentative()
  • ParticipationStatus::needs_action()
  • ParticipationStatus::delegated()

You can indicate that an attendee is required to RSVP to an event:

Event::create()
    ->attendee('[email protected]', 'Ruben', ParticipationStatus::needs_action(), requiresResponse: true)
    ...

An event can be made transparent, so it does not overlap visually with other events in a calendar:

Event::create()
    ->transparent()
    ...

It is possible to create an event that spans a full day:

Event::create()
    ->fullDay()
    ...

The status of an event can be set:

Event::create()
    ->status(EventStatus::cancelled())
    ...

There are three event statuses:

  • EventStatus::confirmed()
  • EventStatus::cancelled()
  • EventStatus::tentative()

An event can be classified(public, private, confidential) as such:

Event::create()
    ->classification(Classification::private())
    ...

You can add a url attachment as such:

Event::create()
    ->attachment('https://spatie.be/logo.svg')
    ->attachment('https://spatie.be/feed.xml', 'application/json')
    ...

You can add an embedded attachment (base64) as such:

Event::create()
    ->embeddedAttachment($file->toString())
    ->embeddedAttachment($fileString, 'application/json')
    ->embeddedAttachment($base64String, 'application/json', needsEncoding: false)
    ...

You can add an image as such:

Event::create()
    ->image('https://spatie.be/logo.svg')
    ->image('https://spatie.be/logo.svg', 'text/svg+xml')
    ->image('https://spatie.be/logo.svg', 'text/svg+xml', Display::badge())
    ...

There are four different image display types:

  • Display::badge()
  • Display::graphic()
  • Display::fullsize()
  • Display::thumbnail()

After creating your event, it should be added to a calendar. There are multiple options to do this:

// As a single event parameter
$event = Event::create('Creating calendar feeds');

Calendar::create('Laracon Online')
    ->event($event)
    ...

// As an array of events
Calendar::create('Laracon Online')
    ->event([
        Event::create('Creating calender feeds'),
        Event::create('Creating contact lists'),
    ])
    ...

// As a closure
Calendar::create('Laracon Online')
    ->event(function(Event $event){
        $event->name('Creating calender feeds');
    })
    ...

Using Carbon

You can use the popular Carbon library:

use Carbon\Carbon;

Event::create('Laracon Online')
    ->startsAt(Carbon::now())
    ...

Timezones

Events will use the timezones defined in the DateTime objects you provide. PHP always sets these timezones in a DateTime object. By default, this will be the UTC timezone, but it is possible to change this.

Just a reminder: do not use PHP's setTimezone function on a DateTime object, it will change the time according to the timezone! It is better to create a new DateTime object with a timezone as such:

new DateTime('6 march 2019 15:00', new DateTimeZone('Europe/Brussels'))

A point can be made for omitting timezones. For example, when you want to show an event at noon in the world. We define noon at 12 o'clock, but that time is relative. It is not the same for people in Belgium, Australia, or any other country in the world.

That's why you can disable timezones on events:

$starts = new DateTime('6 march 2019 12:00')

Event::create()
    ->startsAt($starts)
    ->withoutTimezone()
    ...

You can even disable timezones for a whole calendar:

Calendar::create()
   ->withoutTimezone()
    ...

Each calendar should have Timezone components describing the timezones used within your calendar. Although not all calendar clients require this, it is recommended to add these components.

Creating such Timezone components is quite complicated. That's why this package will automatically add them for you without configuration.

You can disable this behaviour as such:

Calendar::create()
   ->withoutAutoTimezoneComponents()
    ...

You can manually add timezones to a calendar if desired as such:

$timezoneEntry = TimezoneEntry::create(
    TimezoneEntryType::daylight(),
    new DateTime('23 march 2020'),
    '+00:00',
    '+02:00'
);

$timezone = Timezone::create('Europe/Brussels')
    ->entry($timezoneEntry)
    ...

Calendar::create()
    ->timezone($timezone)
    ...

More on these timezones later.

Alerts

Alerts allow calendar clients to send reminders about specific events. For example, Apple Mail on an iPhone will send users a notification about the event. An alert always belongs to an event has a description and a number of minutes before the event it will be triggered:

Event::create('Laracon Online')
    ->alertMinutesBefore(5, 'Laracon online is going to start in five minutes');

You can also trigger an alert after the event:

Event::create('Laracon Online')
    ->alertMinutesAfter(5, 'Laracon online has ended, see you next year!');

Or trigger an alert on a specific date:

Event::create('Laracon Online')
    ->alertAt(
       new DateTime('05/16/2020 12:00:00'),
       'Laracon online has ended, see you next year!'
    );

Removing timezones on a calendar or event will also remove timezones on the alert.

Repeating events

It is possible for events to repeat, for example your monthly company dinner. This can be done as such:

Event::create('Laracon Online')
    ->repeatOn(new DateTime('05/16/2020 12:00:00'));

And you can also repeat the event on a set of dates:

Event::create('Laracon Online')
    ->repeatOn([new DateTime('05/16/2020 12:00:00'), new DateTime('08/13/2020 15:00:00')]);

Recurrence rules

Recurrence rules or RRule's in short, make it possible to add a repeating event in your calendar by describing when it repeats within an RRule. First, we have to create an RRule:

$rrule = RRule::frequency(RecurrenceFrequency::daily());

This rule describes an event that will be repeated daily. You can also set the frequency to secondly, minutely, hourly, weekly, monthly or yearly.

The RRULE can be added to an event as such:

Event::create('Laracon Online')
    ->rrule(RRule::frequency(RecurrenceFrequency::monthly()));

It is possible to finetune the RRule to your personal taste; let's have a look!

A RRule can start from a certain point in time:

$rrule = RRule::frequency(RecurrenceFrequency::daily())->starting(new DateTime('now'));

And stop at a certain point:

$rrule = RRule::frequency(RecurrenceFrequency::daily())->until(new DateTime('now'));

It can only be repeated for a few times, 10 times for example:

$rrule = RRule::frequency(RecurrenceFrequency::daily())->times(10);

The interval of the repetition can be changed:

$rrule = RRule::frequency(RecurrenceFrequency::daily())->interval(2);

When this event starts on Monday, for example, the next repetition of this event will not occur on Tuesday but Wednesday. You can do the same for all the frequencies.

It is also possible to repeat the event on a specific weekday:

$rrule = RRule::frequency(RecurrenceFrequency::monthly())->onWeekDay(
   RecurrenceDay::friday()
);

Or on a specific weekday of a week in the month:

$rrule = RRule::frequency(RecurrenceFrequency::monthly())->onWeekDay(
   RecurrenceDay::friday(), 3
);

Or on the last weekday of a month:

$rrule = RRule::frequency(RecurrenceFrequency::monthly())->onWeekDay(
   RecurrenceDay::sunday(), -1
);

You can repeat on a specific day in the month:

$rrule = RRule::frequency(RecurrenceFrequency::monthly())->onMonthDay(16);

It is even possible to give an array of days in the month:

$rrule = RRule::frequency(RecurrenceFrequency::monthly())->onMonthDay(
   [5, 10, 15, 20]
);

Repeating can be done for certain months (for example only in the second quarter):

$rrule = RRule::frequency(RecurrenceFrequency::monthly())->onMonth(
   [RecurrenceMonth::april(), RecurrenceMonth::may(), RecurrenceMonth::june()]
);

Or just on one month only:

$rrule = RRule::frequency(RecurrenceFrequency::monthly())->onMonth(
   RecurrenceMonth::october()
);

It is possible to set the day when the week starts:

$rrule = RRule::frequency(RecurrenceFrequency::monthly())->weekStartsOn(
   ReccurenceDay::monday()
);

You can provide a specific date on which an event won't be repeated:

Event::create('Laracon Online')
    ->rrule(RRule::frequency(RecurrenceFrequency::daily()))
    ->doNotRepeatOn(new DateTime('05/16/2020 12:00:00'));

It is also possible to give an array of dates on which the event won't be repeated:

Event::create('Laracon Online')
    ->rrule(RRule::frequency(RecurrenceFrequency::daily()))
    ->doNotRepeatOn([new DateTime('05/16/2020 12:00:00'), new DateTime('08/13/2020 15:00:00')]);

Alternatively you can add RRules as a string:

Event::create('SymfonyCon')
    ->rruleAsString('FREQ=DAILY;INTERVAL=1');

If you add RRules as a string the timezones included in DTSTART and UNTIL are unknown to the package as the string is never parsed and evaluated. If they are known you can add DTSTART and UNTIL separately to help the package discover the timezones:

Event::create('SymfonyCon')
    ->rruleAsString(
        'DTSTART=20231207T090000Z;FREQ=DAILY;INTERVAL=1;UNTIL=20231208T090000Z',
        new DateTime('7 december 2023 09:00:00', new DateTimeZone('UTC')),
        new DateTime('8 december 2023 09:00:00', new DateTimeZone('UTC'))
    );

Use with Laravel

You can use Laravel Responses to stream to calendar applications:

$calendar = Calendar::create('Laracon Online');

return response($calendar->get())
    ->header('Content-Type', 'text/calendar; charset=utf-8');

If you want to add the possibility for users to download a calendar and import it into a calendar application:

$calendar = Calendar::create('Laracon Online');

return response($calendar->get(), 200, [
   'Content-Type' => 'text/calendar; charset=utf-8',
   'Content-Disposition' => 'attachment; filename="my-awesome-calendar.ics"',
]);

Crafting Timezones

If you want to craft timezone components yourself, you're in the right place, although we advise you to read the section on timezones from the RFC first.

You can create a timezone as such:

$timezone = Timezone::create('Europe/Brussels');

It is possible to provide the last modified date:

$timezone = Timezone::create('Europe/Brussels')
    ->lastModified(new DateTime('16 may 2020 12:00:00'));

Or add an url with more information about the timezone:

$timezone = Timezone::create('Europe/Brussels')
    ->url('https://spatie.be');

A timezone consists of multiple entries where the time of the timezone changed relative to UTC, such entry can be constructed for standard or daylight time:

$entry = TimezoneEntry::create(
    TimezoneEntryType::standard(),
    new DateTime('16 may 2020 12:00:00'),
    '+00:00',
    '+02:00'
);

Firstly you provide the type of entry (standard or daylight). Then a DateTime when the time changes. Lastly, an offset relative to UTC from before the change and an offset relative to UTC after the change.

It is also possible to give this entry a name and description:

$entry = TimezoneEntry::create(...)
    ->name('Europe - Brussels')
    ->description('Belgian timezones ftw!');

An RRule for the entry can be given as such:

$entry = TimezoneEntry::create(...)
    ->rrule(RRule::frequency(RecurrenceFrequency::daily()));

In the end you can add an entry to a timezone:

$timezone = Timezone::create('Europe/Brussels')
   ->entry($timezoneEntry);

Or even add multiple entries:

$timezone = Timezone::create('Europe/Brussels')
   ->entry([$timezoneEntryOne, $timezoneEntryTwo]);

Now we've constructed our timezone it is time(👀) to add this timezone to our calendar:

$calendar = Calendar::create('Calendar with timezones')
   ->timezone($timezone);

It is also possible to add multiple timezones:

$calendar = Calendar::create('Calendar with timezones')
   ->timezone([$timezoneOne, $timezoneTwo]);

Extending the package

We try to keep this package as straightforward as possible. That's why a lot of properties and subcomponents from the RFC are not included in this package. We've made it possible to add other properties or subcomponents to each component if you might need something not included in the package. But be careful! From this moment, you're on your own correctly implementing the RFC's.

Appending properties

You can add a new property to a component like this:

Calendar::create()
    ->appendProperty(
        TextProperty::create('ORGANIZER', '[email protected]')
    )
    ...

Here we've added a TextProperty , and this is a default key-value property type with a text as value. You can also use one of the default properties included in the package or create your own by extending the Property class.

Sometimes a property can have some additional parameters, these are key-value entries and can be added to properties as such:

$property = TextProperty::create('ORGANIZER', '[email protected]')
    ->addParameter(Parameter::create('CN', 'RUBEN VAN ASSCHE'));

Calendar::create()
    ->appendProperty($property)
    ...

Appending subcomponents

A subcomponent can be appended as such:

Calendar::create()
    ->appendSubComponent(
        Event::create('Extending icalendar-generator')
    )
    ...

It is possible to create your subcomponents by extending the Component class.

Testing

composer test

Alternatives

We strive for a simple and easy to use API. Want something more? Then check out this package by Markus Poerschke.

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security

If you've found a bug regarding security please mail [email protected] instead of using the issue tracker.

Postcardware

You're free to use this package, but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.

Our address is: Spatie, Kruikstraat 22, box 12, 2018 Antwerp, Belgium.

We publish all received postcards on our company website.

Credits

License

The MIT License (MIT). Please see License File for more information.

icalendar-generator's People

Contributors

adrianmrn avatar alexmanase avatar ankurk91 avatar anteprimorac avatar benkinder avatar cweiske avatar davwheat avatar devfrey avatar edalzell avatar enaah avatar freekmurze avatar htto avatar jozeflambrecht avatar justijndepover avatar lucared avatar maartenpaauw avatar mammutalex avatar mansoorkhan96 avatar michaelkaefer avatar nhedger avatar nikow13 avatar nsurlsession0 avatar patinthehat avatar pschocke avatar ricuss avatar rubenvanassche avatar sammoffat avatar stephpy avatar wells avatar wouterbrouwers avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

icalendar-generator's Issues

`endsAt` needs to be provided in order to use `fullDay()` method

Hi Spatie people!

First of all, thank you for providing such a great libraries and tools for all of us developers out there, especially for this library that is really powerful and easy to use.

I updated this library in my project to 2.1.0 version that was recently released and found one issue.

Namely, previously, if you wanted to create an Event for full day, you weren't required to specify an end date of that event, you could just put fullDay() after startsAt() and that would mean that given event will take a full day on a given startsAt() date. Unfortunately, after updating to 2.1.0 version, that is not possible, and you need to specify the endsAt() as well, if you want to use fullDay().

If you don't specify it, creation of Event fails in Event.php file at line 343. Is this intentional behavior for some reason, or it shouldn't work like that? It makes more sense to me that you don't have to specify the end date, as start date + full day properties are enough to construct full day event.

Following code displays that (was working in version 1 and doesn't work in version 2):

use Spatie\IcalendarGenerator\Components\Calendar;
use Spatie\IcalendarGenerator\Components\Event;

Calendar::create('Laracon online')
    ->event(Event::create('Creating calender feeds')
        ->startsAt(new DateTime('6 March 2019 15:00'))
        ->fullDay()
    )
    ->get();

Thank you for taking look at this.

Timezone offset invalid for Google Calendar

In the exported ICS string, the timezone offsets are displayed as:

TZOFFSETFROM:0200

Google won't accept the ICS file. When I change it manually to:

TZOFFSETFROM:+0200

It is working. I think positive offsets should be prefixed with a + sign.

Apple Calendar Event Synching Issue on iPhone

0

I am using the package spatie/icalendar-generator with Laravel to add event in Apple calendar

Version Details:
Spatie Verion = 1.5
Laravel Version = 7.24

By Using this package I am able create .ics file for adding event in calendar like Google, Outlook and Apple. On Google and Outlook the .ics file is working fine and adding the event in external calendar.

On MacBook 15 .ics file is working properly and synching the event in calendar but In iPhone 7+ ( 14.4.2) my .ics file not working properly and not adding event in iPhone calendar. It just showing the event details when we click on the .ics file.

Not an issue - just congrats!

This has to be one of cleanest iCalendar generator that exists on the web.
Congratulations on your project - I will speak about it everywhere!

Please release new Tag with fixed EventStatus

Hello,

thank you for this package, it comes in very handy with my current project and I appreciate your work - but I've got a small issue I'd like to complain about: The EventStatus class in the tag 1.0.0 is non-conforming with RFC 5545 and this was already fixed in the master branch. Please release a new tag with this fix so I don't have to include this package as "dev-master".

Thanks

ALARM component TRIGGER does not work with recurring events

Hi,

I have an recurring event that looks like

BEGIN:VCALENDAR
VERSION:2.0
PRODID:product-1-stage
NAME:product-1
X-WR-CALNAME:product-1

BEGIN:VEVENT
UID:955fa5f486d429d9eaed0ede1cb447d2c63cd5d7
SUMMARY:Test event
DESCRIPTION:laravel meetup 
LOCATION:new delhi
DTSTART:20200103T035200
DTEND:20200103T045200
DTSTAMP:20191226T025458
TRANSP:OPAQUE
CLASS:PUBLIC
RRULE:FREQ=WEEKLY;UNTIL=20220103T235959;BYDAY=MO,TU,WE,TH,FR

BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Test event
TRIGGER;VALUE=DATE-TIME:20200103T025200
END:VALARM

END:VEVENT

END:VCALENDAR

Notice the TRIGGER property has hard coded date time,
When we try to import this in MacOS Calendar app, all of imported events shows same alert date_time.

But if we change the TRIGGER property to like PT{$minutes}M

TRIGGER:-PT60M

It works as expected.
Now it show alert 60 minutes before each of imported event.

Let me know your thoughts.

Feature to reply calendar through outlook/gmail/etc

Currently I can send an email with the ICS attachment and the email clients will automatically detect the event and ask if you want to add it to your calendar (to be clear, it's not going to be a subscription calendar, but a fixed event based on the current ICS file), but I'm trying to make the reply function to work, like the accepted, declined, tentative...

Is there a way to the ICS file tells that it should run a request to an specific page OR the only way is to capture an email, sanitize the reply option, update the database and shoot another email to the participants with the updated ICS?

[Edit] I think it's part of the RFC 6638 (link below).
https://icalendar.org/CalDAV-Scheduling-RFC-6638/b-7-example-attendee-declining-an-instance-of-a-recurring-event.html

no new calendar

i used the calendar class and the event class and but no new calendar is generated and the event is added to the default calendar , i don't know if i should use webcal
note : i am using iphone

Ideas for V2

  • Default to using Timezones and use UTC as a default (see #22)
  • Add support for recurrence rules
  • Make the package PHP 7.4
  • Remove names array from property types and use multiple property types for aliases

Events with timezones are not valid

Generating events with timezones generate this code

DTSTART;TZID=+01:00:20200211T170003
DTEND;TZID=+01:00:20200214T100026
DTSTAMP;TZID=Europe/Berlin:20200821T172102

which iCalendar Validator shows they are not valid. Removing TZID from DTSTART and DTEND would be valid:

DTSTART:20200211T170003
DTEND:20200214T100026
DTSTAMP;TZID=Europe/Berlin:20200821T172102

DTSTAMP format

It looks like some calendars do not understand DTSTAMP with timezone.
DTSTAMP;TZID=UTC:20200626T094630
For example Yandex don't like it and event not appears in calendar.
Is it possible some how control DTSTAMP format?

Outlook calendar import generates date error

When we are trying to import the calendar into Outlook we are presented with the following errors:

Task 'Internet Calendar Subscriptions' reported error (0x00040020) : 'The VEVENT, "Custom Event", defined near line 24, contains a floating DTEND. Outlook supports floating time for all-day events only. Double-click to open this item.'

$calendar = Calendar::create('Assessments Calendar')
            ->refreshInterval(5)

$event = Event::create('Custom Event')
            ->description('Random description')
            ->startsAt(Carbon::now())
            ->endsAt(Carbon::now()->addHour())
            ->address('Random Address', 'Venue location')
            ->coordinates(1, 1);

$calendar->event($event)
 ->get();

When adding timezones to the calendar it fixes this but it creates some issues with the hours for us so we are not using it.

Also, thank you for the other fix :)

Edit: This was encountered on Outlook on Windows 10

X-ADDRESS: comma's need to be escaped

Hi,

We noticed that Google Calendar does not accept the generated ICS-file when X-ADDRESS is not escaped. It is accepted by Apple Calendar though.

Currently, this package generates this, which is not accepted by Google Calendar:
X-APPLE-STRUCTURED-LOCATION;VALUE=URI;X-ADDRESS=Samberstraat 69D, 2060 Antwerp, Belgium;X-APPLE-RADIUS=72;X-TITLE=Spatie:51.2343;4.4287

The following is accepted by both Google Calendar and Apple Calendar:
X-APPLE-STRUCTURED-LOCATION;VALUE=URI;X-ADDRESS=Samberstraat 69D\, 2060 Antwerp \, Belgium;X-APPLE-RADIUS=72;X-TITLE=Spatie:51.2343;4.4287

I'm willing to create a PR of course, but I'm not sure where to look exactly. Any hints are welcome!

Cheers

GEO property issue

Hi,

The GEO (->coordinates) property is now a comma but that is not working.

This works well:
GEO: Latitude;Longitude

Creating event with GPS but no address name generates error

When trying to create an event with an address string but no name, and coordinates, the calendar generates error.

Event::resolveLocationProperties() sends nullable parameter $this->addressName, but the Parameter::create() method does not accept NULL as the second parameter.

Not supporting for outlook and enable to edit alert minute before property

We have integrated this plugin into our application facing the below issues:

  1. The alertMinutesBefore() property is not working for us. default it takes 10 minutes alert, not able to change it to 30 minutes.
  2. We are adding the Location URL for the meeting but the where property won't be reflecting in a google calendar.
  3. Not support this .ics file for outlook calendar.
  4. Need action property is not working.

I am eager to receive your feedback as soon as possible.

Below is the example of the .ics file for reference

BEGIN:VCALENDAR
VERSION:2.0
PRODID:spatie/icalendar-generator
NAME:Interview With: Global Compass
X-WR-CALNAME:Interview With: Global Compass
LOCATION:https://us02web.zoom.us/j/85041344729
TRANSP:OPAQUE
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=
true:mailto:[email protected]
BEGIN:VTIMEZONE
TZID:Asia/Calcutta
BEGIN:STANDARD
DTSTART:20200508T164636
TZOFFSETFROM:+0500
TZOFFSETTO:+0500
END:STANDARD
END:VTIMEZONE
BEGIN:VTIMEZONE
TZID:UTC
BEGIN:STANDARD
DTSTART:20200508T111636
TZOFFSETFROM:+0000
TZOFFSETTO:+0000
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
UID:60193494bdb93
DTSTAMP;TZID=UTC:20210203T000000
SUMMARY:Reymundo Goyette's Interview for Developer Position with Global Com
pass
DESCRIPTION:Reymundo Goyette's Interview for Developer Position with Global
Compass
DTSTART;TZID=Asia/Calcutta:20210203T120000
DTEND;TZID=Asia/Calcutta:20210203T130000
ORGANIZER;CN=Nikhil Adgokar:MAILTO:[email protected]
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Interview With: Global Compass is going to start in 30 minutes
TRIGGER:-PT30M
END:VALARM
END:VEVENT
END:VCALENDAR

Laravel attach to Illuminate\Notifications\Messages\MailMessage

So I have created a Calendar with an event and I want to send it as an attachement of an email, actually my code works but it creates an .ics file at the root of public/ folder and I don't want to have hundred of calendar.ics files on my root. Do I do it in the right way ?

$calendar = Calendar::create()
    ->event(function(Event $event){
        $event->name('Event');
        $event->description('Experience Laracon all around the world');
        $event->period(Carbon::parse($this->event->datetime), Carbon::parse($this->event->datetime)->addHour());
});

$filename = "calendar-event-" . $this->event->date . ".ics";
header("text/calendar");
file_put_contents($filename, $calendar->get());

return (new MailMessage)
    >greeting(__('email.participe.greeting', ['username' => $this->user->username]))
    ->line(__('email.participe.line', ['date' => $this->event->date_format]))
     ->action(__('email.participe.button'), route('events.show', ['date' => $this->event->date]))
      >attach($filename, array('mime' => "text/calendar"));

Invalid TZID value or missing VTIMEZONE component (Europe/Paris)

Hello,

I have this event :

$event = Calendar::create()
	->event(Event::create("Your booking")
		->startsAt($this->shopBooking->date_start->timezone('Europe/Paris'))
		->endsAt($this->shopBooking->date_end->timezone('Europe/Paris'))
		->createdAt($this->shopBooking->created_at->timezone('Europe/Paris'))
		->address($this->shopBooking->parking->parkingAddress->address .', '. $this->shopBooking->parking->parkingAddress->city)
		->withTimezone()
	);

But when I want to open the .ics file into Outlook, it still have 2 hours more than the original date.

In the file, I have :

DTSTART;TZID=Europe/Paris:20201021T183037
DTEND;TZID=Europe/Paris:20201021T191507

But I have 20:30 & 21:15 on my Outlook event. And when I try to validate the .ics file, I have these 2 errors :

Invalid TZID value or missing VTIMEZONE component (Europe/Paris) near line # 4
Reference: 3.2.19. Time Zone Identifier
Invalid TZID value or missing VTIMEZONE component (Europe/Paris) near line # 4
Reference: 3.2.19. Time Zone Identifier

How can I have to correct timezone ?

URL into EVENT

Hello,

I'd like to know if it's possible to add an URL to an EVENT ?

Thanks for your help,
Denis

fullDay() function does not produce DTEND;VALUE=DATE:YYYYMMDD

get() does not produce 'DTSTART;VALUE=DATE' when fullday() called.
I want to have 'DTEND;VALUE=DATE' format for date value.
Is the current behavior correct?

code

 Event::create('test')
                    ->startsAt(new DateTime('6 june 2021'), false)
                    ->fullDay()

result

BEGIN:VCALENDAR
VERSION:2.0
PRODID:spatie/icalendar-generator
NAME:xxxx
X-WR-CALNAME:xxxxxx
BEGIN:VEVENT
DTSTAMP;TZID=Asia/Tokyo:20210604T181822
SUMMARY:test
DTSTART:20210606
DTEND:20210606
END:VEVENT
END:VCALENDAR

expect

BEGIN:VCALENDAR
VERSION:2.0
PRODID:spatie/icalendar-generator
NAME:xxxx
X-WR-CALNAME:xxxxxx
BEGIN:VEVENT
DTSTAMP;TZID=Asia/Tokyo:20210604T181822
SUMMARY:test
DESCRIPTION:6
DTSTART;VALUE=DATE:20210606
DTEND;VALUE=DATE:20210606
END:VEVENT
END:VCALENDAR

ICS Download Error

error occurs Method Illuminate\Http\Response::download when using this code

return response($calendar->get())
  ->header('Content-Type', 'text/calendar')
  ->header('charset', 'utf-8')
 ->download('my-awesome-calendar.ics')

use this code instead:

return response($calendar->get(), 200, [
   'Content-Type' => 'text/calendar',
   'Content-Disposition' => 'attachment; filename="my-awesome-calendar.ics"',
    'charset' => 'utf-8',
  ]);

alertMinutesBefore

I try below:

$event = Event::create()
                      ->name('Laracon Online')
                      ->description('Experience Laracon all around the world')
                      ->uniqueIdentifier('123')
                      ->location('Antwerp')
                      ->alertMinutesBefore(10, 'Laracon online is going to start in five mintutes')
                      ->createdAt(new DateTime('16 september 2019', new DateTimeZone('Australia/Sydney')))
                      ->startsAt(new DateTime('16 september 2019 15:00', new DateTimeZone('Australia/Sydney')))
                      ->withTimezone()
                      ->endsAt(new DateTime('16 september 2019 16:00', new DateTimeZone('Australia/Sydney')));

I get:
ErrorException (E_NOTICE)
Undefined variable: 10

I tried to change the code to below:

public function alertMinutesBefore(int $minutes, string $message = null): Event
    {
        $trigger = new DateTime($this->starts);

        $this->alerts[] = Alert::create(
            //$trigger->add(new DateInterval('PT${'.$minutes.'}M')),
            $trigger->add(new DateInterval("PT".$minutes."M")),
            $message ?? $this->name
        );
        return $this;
    }

This kind returns:

BEGIN:VCALENDAR
VERSION:2.0
PRODID:spatie/icalendar-generator
NAME:Laracon Online
X-WR-CALNAME:Laracon Online
BEGIN:VEVENT
UID:123
SUMMARY:Laracon Online
DESCRIPTION:Experience Laracon all around the world
LOCATION:Antwerp
DTSTART;TZID=Australia/Sydney:20190916T150000
DTEND;TZID=Australia/Sydney:20190916T160000
DTSTAMP;TZID=Australia/Sydney:20190916T000000
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Laracon online is going to start in five mintutes
TRIGGER;VALUE=DATE-TIME:20190908 //this value is not getting excepted it needs something like 20190916T000000
END:VALARM
END:VEVENT
END:VCALENDAR

Can you please help
Thanks

Can't import calendar into Google Calendar

Hi,
Thanks for this great package!
I'm trying to create a calendar stream for my application, but I can't import it in Google Calendar.
I tried using the simple example in the README file and it's not working, but I have no error messages whatsoever.

It's working in my calendar app (Gnome Calendar), and the calendar passes the test here : Validator.
I feel like it's linked to timezones but I'm not sure.

Thanks for your input!

Timezone '+01:00' does not work

✏️ Describe the bug
Hello, I have issue with timezones from PostgreSQL. Returned date has timezone '+01:00' instead of 'Europe/Prague', generated iCal using this DateTime instance does not work.

↪️ To Reproduce
Provide us a test like this one which shows the problem:

$dtzA = new DateTimeZone('Europe/Prague');	// This works ok!
$dtzB = new DateTimeZone('+01:00');		// I get this from PostgreSQL using doctrine/orm and it does not work.

$startDate = new DateTime('now', $dtzA);
$endDate = new DateTime('+2 hours', $dtzB);

$cal = Calendar::create('Test');
$cal->event(Event::create('Hello, World!')
	->startsAt($startDate)
	->endsAt($endDate)
);
BEGIN:VCALENDAR
VERSION:2.0
PRODID:spatie/icalendar-generator
NAME:Test
X-WR-CALNAME:Test
BEGIN:VTIMEZONE
TZID:Europe/Prague
BEGIN:STANDARD
DTSTART:20231029T030000
TZOFFSETFROM:+0200
TZOFFSETTO:+0100
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:20240331T010000
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VTIMEZONE
TZID:+01:00
BEGIN:STANDARD
DTSTART:20230601T111243
TZOFFSETFROM:+0000
TZOFFSETTO:+0000
END:STANDARD
END:VTIMEZONE
BEGIN:VTIMEZONE
TZID:UTC
BEGIN:STANDARD
DTSTART:20230601T081243
TZOFFSETFROM:+0000
TZOFFSETTO:+0000
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
UID:65dc47fbe95ee
DTSTAMP:20240226T081243Z
SUMMARY:Hello\, World!
DTSTART;TZID=Europe/Prague:20240226T091243
DTEND;TZID=+01:00:20240226T111243
END:VEVENT
END:VCALENDAR

obrazek
Output on: https://icalvalidator.com/index.html

Assertions aren't required, a simple dump or dd statement of what's going wrong is good enough 😄

✅ Expected behavior
I would expect that TZ +01:00 would work the same way as if I set Europe/Prague.

🖥️ Versions

iCalendar generator: 2.6.0
PHP: 8.3.2

PhpDoc return types

Hello,

Analysing my codebase with Psalm after using ths package, I ran into issues where I was warned of UndefinedMethods.

I raised that with the psalm team here: vimeo/psalm#4263

Something like this causes issues:

        $event = Event::create()
            ->uniqueIdentifier($uid)
            ->startsAt($start)
            ->endsAt($end)
            ->name($summary)
            ->description($description)
            ->appendProperty(TextPropertyType::create('URL', $activity_url))
            ->attendee($member['email'], null, $participationStatus);

        if ($address) {
            $event->address(address);
        }

        if ($lat && $lng) {
            $event->coordinates($lat, $lng);
        }

		$calendar->appendSubComponent($event);

Essentially all because appendProperty() is typed to return Component, and so the calls that follow (attendee(), address(), coordinates()) all fail as those are methods of Event, not of Component.

There are two solutions to the issue:

  • move appendProperty() to be the last statement before $calendar->appendSubComponent($event)
  • add static as a return type to most fluent calls.

PHP8 will introduce static as an actual return type - which solves the issue - but in lower versions, the only solution is really to provide this in phpdoc alongside the Component use in the method signature.

So as an example:

    public function appendProperty(PropertyType $property): Component
    {
        $this->appendedProperties[] = $property;

        return $this;
    }

Would simply become:

	/**
	 * @return static
	 */
    public function appendProperty(PropertyType $property): Component
    {
        $this->appendedProperties[] = $property;

        return $this;
    }

bottom line: if submit a PR for that, would this be considered?

From my tests, this applies to Psalm and Phpstan. I haven't tested how Phan and others consider it. For PhpStorm's built-in analysis, it doesn't need the @return static return type, but it doesn't break anything either.

Popup does not open in the native Android app

On android phones a file is generated and the download begins, and then when opening the downloaded file offers to record the event in the Google calendar. Is it possible to immediately open a popup for writing to the calendar, as it is implemented on the iPhone?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.