Skip to content

Commit

Permalink
Merge branch 'master' into 2.x
Browse files Browse the repository at this point in the history
# Conflicts:
#	.github/workflows/psalm.yml
#	.github/workflows/run-tests.yml
#	composer.json
#	psalm-baseline.xml
#	psalm.xml
#	src/Generators/BaseOutlook.php
#	src/Generators/Ics.php
#	src/Generators/WebOutlook.php
#	src/Link.php
#	tests/Generators/IcsGeneratorTest.php
  • Loading branch information
alies-dev committed Jan 28, 2024
2 parents 10a9d2f + 7a59395 commit b68a568
Show file tree
Hide file tree
Showing 28 changed files with 296 additions and 74 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/backward-compatibility-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
name: Backward compatibility check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: fetch tags
run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/php-cs-fixer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Fix style
uses: docker://oskarstark/php-cs-fixer-ga
with:
args: --config=.php-cs-fixer.php --allow-risky=yes

- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v4
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: Fix styling
4 changes: 2 additions & 2 deletions .github/workflows/psalm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ jobs:
name: psalm
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
php-version: '8.3'
coverage: none

- name: Cache composer dependencies
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ jobs:
fail-fast: true
matrix:
os: [ubuntu-latest]
php: [8.1, 8.2]
php: [8.1, 8.2, 8.3]
dependency-version: [prefer-lowest, prefer-stable]

name: P${{ matrix.php }} - ${{ matrix.dependency-version }} - ${{ matrix.os }}

steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/update-changelog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: main

Expand All @@ -21,7 +21,7 @@ jobs:
release-notes: ${{ github.event.release.body }}

- name: Commit updated CHANGELOG
uses: stefanzweifel/git-auto-commit-action@v4
uses: stefanzweifel/git-auto-commit-action@v5
with:
branch: main
commit_message: Update CHANGELOG
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ vendor
coverage
.phpunit.result.cache
.php-cs-fixer.cache
.phpunit.cache
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@

[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/support-ukraine.svg?t=1" />](https://supportukrainenow.org)

# Generate add to calendar links for Google, iCal and other calendar systems

[![Latest Version on Packagist](https://img.shields.io/packagist/v/spatie/calendar-links.svg?style=flat-square)](https://packagist.org/packages/spatie/calendar-links)
Expand All @@ -11,7 +8,7 @@
[![Psalm level](https://shepherd.dev/github/spatie/calendar-links/level.svg)](https://shepherd.dev/github/spatie/calendar-links)


Using this package you can generate links to add events to calendar systems. Here's a quick example:
Using this package, you can generate links to add events to calendar systems. Here's a quick example:

```php
use Spatie\CalendarLinks\Link;
Expand All @@ -25,7 +22,7 @@ Link::create(

This will output: `https://calendar.google.com/calendar/render?action=TEMPLATE&text=Birthday&dates=20180201T090000/20180201T180000&sprop=&sprop=name:`

If you follow that link (and are authenticated with Google) you'll see a screen to add the event to your calendar.
If you follow that link (and are authenticated with Google), youll see a screen to add the event to your calendar.

The package can also generate ics files that you can open in several email and calendar programs, including Microsoft Outlook, Google Calendar, and Apple Calendar.

Expand Down Expand Up @@ -70,24 +67,27 @@ echo $link->webOutlook();
// Generate a link to create an event on outlook.office.com calendar
echo $link->webOffice();

// Generate a data uri for an ics file (for iCal & Outlook)
// Generate a data URI for an ics file (for iCal & Outlook)
echo $link->ics();
echo $link->ics(['UID' => 'custom-id']); // Custom UID (to update existing events)
echo $link->ics(['URL' => 'https://my-page.com']); // Custom URL
echo $link->ics(['REMINDER' => []]); // Add the default reminder (for iCal & Outlook)
echo $link->ics(['REMINDER' => ['DESCRIPTION' => 'Remind me', 'TIME' => new \DateTime('tomorrow 12:30 UTC')]]); // Add a custom reminder
echo $link->ics([], ['format' => 'file']); // use file output; e.g. to attach ics as a file to an email.

// Generate a data URI using arbitrary generator:
echo $link->formatWith(new \Your\Generator());
```

> ⚠️ ICS download links don't work in IE and EdgeHTML-based Edge browsers, see [details](https://github.com/spatie/calendar-links/issues/71).
## Package principles

1. it should produce a small output (to keep pagesize small)
1. it should produce a small output (to keep page-size small)
2. it should be fast (no any external heavy dependencies)
3. all features should be supported by at least 2 generators (different services have different features)
3. all `Link` class features should be supported by at least 2 generators (different services have different features)

## Changelog

Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.
Please see [CHANGELOG](CHANGELOG.md) for more information.

## Testing

Expand All @@ -101,11 +101,11 @@ Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTI

## Security

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

## Postcardware

You're free to use this package (it's [MIT-licensed](LICENSE.md)), 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.
You're free to use this package (it's [MIT-licensed](LICENSE.md)), 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, Samberstraat 69D, 2060 Antwerp, Belgium.

Expand Down
18 changes: 9 additions & 9 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
{
"name": "spatie/calendar-links",
"description": "Generate add to calendar links for Google, iCal and other calendar systems",
"license": "MIT",
"keywords": [
"spatie",
"calendar-links"
],
"homepage": "https://github.com/spatie/calendar-links",
"license": "MIT",
"authors": [
{
"name": "Sebastian De Deyne",
Expand All @@ -15,14 +14,15 @@
"role": "Developer"
}
],
"homepage": "https://github.com/spatie/calendar-links",
"require": {
"php": "^8.1"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.14",
"phpunit/phpunit": "^10.0",
"spatie/phpunit-snapshot-assertions": "^5.0",
"vimeo/psalm": "^5.7.7"
"friendsofphp/php-cs-fixer": "^3.48",
"phpunit/phpunit": "^10.1",
"spatie/phpunit-snapshot-assertions": "^5.1",
"vimeo/psalm": "^5.20"
},
"autoload": {
"psr-4": {
Expand All @@ -34,14 +34,14 @@
"Spatie\\CalendarLinks\\Tests\\": "tests"
}
},
"config": {
"sort-packages": true
},
"scripts": {
"format": "vendor/bin/php-cs-fixer fix --allow-risky=yes",
"psalm": "vendor/bin/psalm --find-unused-psalm-suppress --output-format=phpstorm",
"psalm:ci": "vendor/bin/psalm --find-unused-psalm-suppress --output-format=github --shepherd",
"test": "vendor/bin/phpunit",
"test:update-snapshots": "vendor/bin/phpunit -d --update-snapshots"
},
"config": {
"sort-packages": true
}
}
25 changes: 13 additions & 12 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.0/phpunit.xsd"
bootstrap="vendor/autoload.php" backupGlobals="false"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
bootstrap="vendor/autoload.php"
backupGlobals="false"
colors="true"
cacheDirectory=".phpunit.cache"
backupStaticProperties="false">
<coverage>
<include>
<directory suffix=".php">src</directory>
</include>
</coverage>
<testsuites>
<testsuite name="unit">
<directory>tests</directory>
</testsuite>
</testsuites>
<testsuites>
<testsuite name="unit">
<directory>tests</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory suffix=".php">src</directory>
</include>
</source>
</phpunit>
2 changes: 1 addition & 1 deletion psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
errorLevel="2"
findUnusedBaselineEntry="false"
findUnusedBaselineEntry="true"
findUnusedCode="false"
findUnusedVariablesAndParams="true"
resolveFromConfigFile="true"
Expand Down
62 changes: 56 additions & 6 deletions src/Generators/Ics.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,33 @@

/**
* @see https://icalendar.org/RFC-Specifications/iCalendar-RFC-5545/
* @psalm-type IcsOptions = array{UID?: string, URL?: string, REMINDER?: array{DESCRIPTION?: string, TIME?: \DateTimeInterface}}
*/
class Ics implements Generator
{
public const FORMAT_HTML = 'html';
public const FORMAT_FILE = 'file';

/** @see https://www.php.net/manual/en/function.date.php */
protected string $dateFormat = 'Ymd';


protected string $dateTimeFormat = 'Ymd\THis\Z';

/** @var array<non-empty-string, non-empty-string> */
/** @var IcsOptions */
protected array $options = [];

/** @var array{format?: self::FORMAT_*} */
protected $presentationOptions = [];

/**
* @param array<non-empty-string, non-empty-string> $options
* @param IcsOptions $options Optional ICS properties and components
* @param array{format?: self::FORMAT_*} $presentationOptions
*/
public function __construct(array $options = [])
public function __construct(array $options = [], array $presentationOptions = [])
{
$this->options = $options;
$this->presentationOptions = $presentationOptions;
}

/** @inheritDoc */
Expand All @@ -41,8 +51,8 @@ public function generate(Link $link): string
$dateTimeFormat = $link->allDay ? $this->dateFormat : $this->dateTimeFormat;

if ($link->allDay) {
$url[] = 'DTSTAMP:'.gmdate($dateTimeFormat, $link->from->getTimestamp());
$url[] = 'DTSTART:'.gmdate($dateTimeFormat, $link->from->getTimestamp());
$url[] = 'DTSTAMP:'.$link->from->format($dateTimeFormat);
$url[] = 'DTSTART:'.$link->from->format($dateTimeFormat);
$url[] = 'DURATION:P'.(max(1, $link->from->diff($link->to)->days)).'D';
} else {
$url[] = 'DTSTAMP:'.gmdate($dateTimeFormat, $link->from->getTimestamp());
Expand All @@ -61,10 +71,19 @@ public function generate(Link $link): string
$url[] = 'URL;VALUE=URI:'.$this->options['URL'];
}

if (is_array($this->options['REMINDER'] ?? null)) {
$url = [...$url, ...$this->generateAlertComponent($link)];
}

$url[] = 'END:VEVENT';
$url[] = 'END:VCALENDAR';

return $this->buildLink($url);
$format = $this->presentationOptions['format'] ?? self::FORMAT_HTML;

return match ($format) {

Check failure on line 83 in src/Generators/Ics.php

View workflow job for this annotation

GitHub Actions / psalm

LessSpecificReturnStatement

src/Generators/Ics.php:83:16: LessSpecificReturnStatement: The type 'string' is more general than the declared return type 'non-empty-string' for Spatie\CalendarLinks\Generators\Ics::generate (see https://psalm.dev/129)
'file' => $this->buildFile($url),
default => $this->buildLink($url),

Check failure on line 85 in src/Generators/Ics.php

View workflow job for this annotation

GitHub Actions / psalm

ArgumentTypeCoercion

src/Generators/Ics.php:85:41: ArgumentTypeCoercion: Argument 1 of Spatie\CalendarLinks\Generators\Ics::buildLink expects non-empty-list<non-empty-string>, but parent type list{0: string, 1?: string, 2?: string, 3?: string, 4?: string, 5?: string, 6?: string, 7?: string, 8?: string, 9?: string, 10?: string, 11?: string, ...<string>} provided (see https://psalm.dev/193)
};
}

/**
Expand All @@ -76,6 +95,11 @@ protected function buildLink(array $propertiesAndComponents): string
return 'data:text/calendar;charset=utf8;base64,'.base64_encode(implode("\r\n", $propertiesAndComponents));
}

protected function buildFile(array $propertiesAndComponents): string
{
return implode("\r\n", $propertiesAndComponents);
}

/** @see https://tools.ietf.org/html/rfc5545.html#section-3.3.11 */
protected function escapeString(string $field): string
{
Expand All @@ -93,4 +117,30 @@ protected function generateEventUid(Link $link): string
$link->address
));
}

/**
* @param \Spatie\CalendarLinks\Link $link
* @return list<string>
*/
private function generateAlertComponent(Link $link): array
{
$description = $this->options['REMINDER']['DESCRIPTION'] ?? null;
if (! is_string($description)) {
$description = 'Reminder: '.$this->escapeString($link->title);
}

$trigger = '-PT15M';
if (($reminderTime = $this->options['REMINDER']['TIME'] ?? null) instanceof \DateTimeInterface) {
$trigger = 'VALUE=DATE-TIME:'.gmdate($this->dateTimeFormat, $reminderTime->getTimestamp());
}

$alarmComponent = [];
$alarmComponent[] = 'BEGIN:VALARM';
$alarmComponent[] = 'ACTION:DISPLAY';
$alarmComponent[] = 'DESCRIPTION:'.$description;
$alarmComponent[] = 'TRIGGER:'.$trigger;
$alarmComponent[] = 'END:VALARM';

return $alarmComponent;
}
}
2 changes: 1 addition & 1 deletion src/Generators/WebOutlook.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
class WebOutlook extends BaseOutlook
{
/** @var non-empty-string */
protected const BASE_URL = 'https://outlook.live.com/calendar/deeplink/compose?path=/calendar/action/compose&rru=addevent';
protected const BASE_URL = 'https://outlook.live.com/calendar/action/compose?path=/calendar/action/compose&rru=addevent';

/** @inheritDoc */
public function baseUrl(): string
Expand Down
Loading

0 comments on commit b68a568

Please sign in to comment.