kithkin/app/Http/Controllers/CalendarSettingsController.php

259 lines
8.3 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\CalendarInstance;
use App\Models\CalendarMeta;
use App\Models\Subscription;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redirect;
class CalendarSettingsController extends Controller
{
/* landing page shows the first settings pane (language/region) */
public function index()
{
return redirect()->route('calendar.settings.language');
}
/**
* Language and region
**/
/* language and region form */
public function languageForm(Request $request)
{
$user = $request->user();
$settings = (array) ($user->settings ?? []);
return $this->frame('calendar.settings.language', [
'title' => __('calendar.settings.language_region.title'),
'values' => [
'language' => $user->getSetting('app.language', app()->getLocale()),
'region' => $user->getSetting('app.region', 'US'),
'date_format' => $user->getSetting('app.date_format', 'mdy'),
'time_format' => $user->getSetting('app.time_format', '12'),
],
'options' => [
'languages' => [
'en' => 'English',
'es' => 'Spanish',
'fr' => 'French',
'de' => 'German',
'it' => 'Italian',
'pt' => 'Portuguese',
'nl' => 'Dutch',
],
'regions' => [
'US' => 'United States',
'CA' => 'Canada',
'GB' => 'United Kingdom',
'AU' => 'Australia',
'NZ' => 'New Zealand',
'IE' => 'Ireland',
'DE' => 'Germany',
'FR' => 'France',
'ES' => 'Spain',
'IT' => 'Italy',
'NL' => 'Netherlands',
],
'date_formats' => [
'mdy' => 'MM/DD/YYYY (01/15/2026)',
'dmy' => 'DD/MM/YYYY (15/01/2026)',
'ymd' => 'YYYY-MM-DD (2026-01-15)',
],
'time_formats' => [
'12' => '12-hour (1:30 PM)',
'24' => '24-hour (13:30)',
],
],
]);
}
/* handle POST from language/region pane */
public function languageStore(Request $request)
{
$data = $request->validate([
'language' => ['required', 'string', 'max:10', 'regex:/^[a-z]{2}([-_][A-Z]{2})?$/'],
'region' => ['required', 'string', 'size:2', 'regex:/^[A-Z]{2}$/'],
'date_format' => ['required', 'in:mdy,dmy,ymd'],
'time_format' => ['required', 'in:12,24'],
]);
$user = $request->user();
$user->setSettings([
'app.language' => $data['language'],
'app.region' => $data['region'],
'app.date_format' => $data['date_format'],
'app.time_format' => $data['time_format'],
]);
// apply immediately for the current request cycle going forward
app()->setLocale($data['language']);
return redirect()
->route('calendar.settings.language')
->with('toast', __('Settings saved!'));
}
/**
* Subscribe
**/
/* show “Subscribe to a calendar” form */
public function subscribeForm()
{
return $this->frame(
'calendar.settings.subscribe',
[
'title' => __('calendar.settings.subscribe.title'),
'sub' => __('calendar.settings.subscribe.subtitle'),
]);
}
/* handle POST from the subscribe form */
public function subscribeStore(Request $request)
{
$data = $request->validate([
'source' => ['required', 'url'],
'displayname' => ['nullable', 'string', 'max:100'],
'color' => ['nullable', 'regex:/^#[0-9A-F]{6}$/i'],
]);
// create the calendarsubscription and calendar_meta rows in one call via Subscription model
Subscription::createWithMeta(
$request->user(),
[
'source' => $data['source'],
'displayname' => $data['displayname'] ?: $data['source'],
'calendarcolor' => $data['color'] ?? '#1a1a1a',
// you can add 'refreshrate' => 'P1D' here if you like
]
);
return redirect()
->route('calendar.index')
->with('toast', __('Subscription added successfully!'));
}
/**
* individual calendar settings
*/
public function calendarForm(Request $request, string $calendarUri)
{
$user = $request->user();
$principalUri = $user->uri;
// this is the user's "instance" of the calendar (displayname/color/etc live here in sabre)
$instance = CalendarInstance::query()
->where('principaluri', $principalUri)
->where('uri', $calendarUri)
->firstOrFail();
// your app meta (optional row)
$meta = $instance->meta;
// if it's remote
$icsUrl = null;
if (($meta?->is_remote ?? false) && $meta?->subscription_id) {
$icsUrl = Subscription::query()
->whereKey($meta->subscription_id)
->value('source');
}
return $this->frame(
'calendar.settings.calendar',
[
'title' => __('calendar.settings.calendar.title'),
'sub' => __('calendar.settings.calendar.subtitle'),
'instance' => $instance,
'meta' => $meta,
'icsUrl' => $icsUrl,
'userTz' => $user->timezone,
]);
}
public function calendarStore(Request $request, string $calendarUri): RedirectResponse
{
$user = $request->user();
$principalUri = $user->uri;
$instance = CalendarInstance::query()
->where('principaluri', $principalUri)
->where('uri', $calendarUri)
->with('meta')
->firstOrFail();
$data = $request->validate([
'displayname' => ['required', 'string', 'max:100'],
'description' => ['nullable', 'string', 'max:500'],
'timezone' => ['nullable', 'string', 'max:64'],
'color' => ['nullable', 'regex:/^#[0-9A-F]{6}$/i'],
]);
$timezone = filled($data['timezone'] ?? null)
? $data['timezone']
: $user->timezone;
$color = $data['color'] ?? $instance->resolvedColor();
DB::transaction(function () use ($instance, $data, $timezone, $color) {
// update sabre calendar instance (dav-facing)
$instance->update([
'displayname' => $data['displayname'],
'description' => $data['description'] ?? null,
'timezone' => $timezone,
'calendarcolor' => $color,
]);
// update ui meta (kithkin-facing)
CalendarMeta::updateOrCreate(
['calendar_id' => $instance->calendarid],
[
'title' => $data['displayname'],
'color' => $color,
'color_fg' => contrast_text_color($color),
]
);
});
return Redirect::route('calendar.settings.calendars.show', $calendarUri)
->with('toast', [
'message' => __('calendar.settings.saved'),
'type' => 'success',
]);
}
/**
* content frame handler
*/
private function frame(?string $view = null, array $data = [])
{
$user = request()->user();
/* pull user's calendar instances and adapt ordering keys to calendarorder */
$calendars = CalendarInstance::query()
->forUser($user)
->withUiMeta()
->ordered()
->get();
return view('calendar.settings.index', [
'view' => $view,
'data' => $data,
'calendars' => $calendars,
'timezones' => config('timezones'),
]);
}
}