Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ ICS downloads read the following entry fields when present:
| Online only | `online_url` | `online_url` | — |
| Hybrid | `location.name` | `online_url` | `location.coordinates` |

`location` must be a group. A string or other non-group value is skipped (no `LOCATION:` from it). Nested coordinates shape:
`location` must be a group. A string or other non-group value is skipped (no `LOCATION:` from it). Nested coordinates:

```php
'location' => [
Expand Down Expand Up @@ -106,7 +106,25 @@ Breaking changes for location fields:
- URL sniffing on a string `location` is gone — use `online_url` for join links
- Protected `eventUrl()` / `icsAddress()` were replaced by `icsUrl()` / `icsLocation()`

An update script (#196) migrates common legacy handles. Back up content before upgrading. Computed-value mappings and foreign (e.g. Prime) `location` shapes need a separate cutover.
**Back up content before upgrading.** The `MigrateLocationFields` update script rewrites entries in the configured events collections (all sites):

| Old | New |
|---|---|
| `address` | `location.name` |
| non-URL string `location` | `location.name` |
| URL string `location` | `online_url` |
| `link` | `online_url` |
| top-level `coordinates` | `location.coordinates` |

Skipped (logged with entry IDs — resolve by hand):

- `address` and a non-URL string `location` both set
- `link` and a URL-valued `location` both set
- `online_url` already set together with a conflicting `link` or URL-valued `location`

`location` that is already a group is left alone (not logged). A lone `link` on those entries may still move to `online_url`.

Computed-value mappings are not migrated — update those by hand.

### Single-Day Events

Expand Down
162 changes: 162 additions & 0 deletions src/UpdateScripts/MigrateLocationFields.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<?php

namespace TransformStudios\Events\UpdateScripts;

use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Statamic\Entries\Entry;
use Statamic\Facades\Entry as Entries;
use Statamic\UpdateScripts\UpdateScript;
use TransformStudios\Events\Events;

class MigrateLocationFields extends UpdateScript
{
public function shouldUpdate($newVersion, $oldVersion)
{
return $this->isUpdatingTo('7.0');
}

public function update()
{
$skipped = collect(Events::setting('collections', ['events']))
->flatMap(fn (string $collection) => Entries::query()->where('collection', $collection)->get())
->map(fn (Entry $entry) => $this->processEntry($entry))
->filter();

if ($skipped->isNotEmpty()) {
$this->console()->warn('Skipped entries (resolve by hand):');
$skipped->each(fn (string $line) => $this->console()->line(" - {$line}"));
}

$this->console()->info('Migrated event location fields for 7.0.');
}

private function filledString(array $data, string $key): bool
{
return is_string($value = Arr::get($data, $key)) && filled($value);
}

private function migrateEntry(Entry $entry): void
{
$data = $entry->data()->all();
$location = Arr::get($data, 'location');

// location is already a group from another package — leave it alone.
// Only move a leftover Events link: link: https://… → online_url: https://…
if (is_array($location)) {
if (! $this->filledString($data, 'link') || $this->filledString($data, 'online_url')) {
return;
}

$entry->set('online_url', $data['link'])->remove('link')->save();

return;
}

// Events legacy → nested group, e.g.
// address: '123 Main St' + coordinates: { lat, lng }
// → location: { name: '123 Main St', coordinates: { lat, lng } }
// location: 'City Hall' → location: { name: 'City Hall' }
// location: 'https://zoom…' alone is not a name — stripped here, becomes online_url below
$name = $this->resolveName($data);
$coordinates = $this->resolveCoordinates($data);
$changed = false;

if (! is_null($name) || ! is_null($coordinates)) {
$group = array_filter(compact('name', 'coordinates'), fn ($value) => ! is_null($value));
$entry->set('location', $group);
$changed = true;
} elseif (is_string($location)) {
$entry->remove('location');
$changed = true;
}

// link / URL-valued location / existing online_url → online_url
if (! is_null($onlineUrl = $this->resolveOnlineUrl($data)) && Arr::get($data, 'online_url') !== $onlineUrl) {
$entry->set('online_url', $onlineUrl);
$changed = true;
}

// Drop legacy top-level handles now that values live under location / online_url
foreach (['address', 'link', 'coordinates'] as $handle) {
if (array_key_exists($handle, $data)) {
$entry->remove($handle);
$changed = true;
}
}

if ($changed) {
$entry->save();
}
}

private function processEntry(Entry $entry): ?string
{
if ($reason = $this->skipReason($entry->data()->all())) {
return "{$entry->id()} ({$reason})";
}

$this->migrateEntry($entry);

return null;
}

private function resolveCoordinates(array $data): ?array
{
return is_array($value = Arr::get($data, 'coordinates')) ? $value : null;
}

private function resolveName(array $data): ?string
{
if ($this->filledString($data, 'address')) {
return $data['address'];
}

if ($this->filledString($data, 'location') && ! Str::isUrl($data['location'])) {
return $data['location'];
}

return null;
}

private function resolveOnlineUrl(array $data): ?string
{
if ($this->filledString($data, 'online_url')) {
return $data['online_url'];
}

if ($this->filledString($data, 'link')) {
return $data['link'];
}

if ($this->filledString($data, 'location') && Str::isUrl($data['location'])) {
return $data['location'];
}

return null;
}

private function skipReason(array $data): ?string
{
$hasAddress = $this->filledString($data, 'address');
$hasLink = $this->filledString($data, 'link');
$hasOnlineUrl = $this->filledString($data, 'online_url');
$isStringLocation = $this->filledString($data, 'location');
$isUrlLocation = $isStringLocation && Str::isUrl($data['location']);
$isNonUrlStringLocation = $isStringLocation && ! Str::isUrl($data['location']);

if ($hasAddress && $isNonUrlStringLocation) {
return 'address and non-URL location both set';
}

if ($hasLink && $isUrlLocation) {
return 'link and URL-valued location both set';
}

if ($hasOnlineUrl && ($hasLink || $isUrlLocation)) {
return 'online_url conflicts with link or URL-valued location';
}

return null;
}
}
Loading