user()->plugins->contains($this->plugin), 403); $this->blade_code = $this->plugin->render_markup; // required to render some stuff $this->configuration_template = $this->plugin->configuration_template ?? []; if ($this->plugin->render_markup_view) { try { $basePath = resource_path('views/' . str_replace('.', '/', $this->plugin->render_markup_view)); $paths = [ $basePath . '.blade.php', $basePath . '.liquid', ]; $this->view_content = null; foreach ($paths as $path) { if (file_exists($path)) { $this->view_content = file_get_contents($path); break; } } } catch (\Exception $e) { $this->view_content = null; } } else { $this->markup_code = $this->plugin->render_markup; $this->markup_language = $this->plugin->markup_language ?? 'blade'; } // Initialize screen settings from the model $this->no_bleed = (bool) ($this->plugin->no_bleed ?? false); $this->dark_mode = (bool) ($this->plugin->dark_mode ?? false); $this->fillformFields(); $this->data_payload_updated_at = $this->plugin->data_payload_updated_at; } public function fillFormFields(): void { $this->name = $this->plugin->name; $this->data_stale_minutes = $this->plugin->data_stale_minutes; $this->data_strategy = $this->plugin->data_strategy; $this->polling_url = $this->plugin->polling_url; $this->polling_verb = $this->plugin->polling_verb; $this->polling_header = $this->plugin->polling_header; $this->polling_body = $this->plugin->polling_body; $this->data_payload = json_encode($this->plugin->data_payload, JSON_PRETTY_PRINT); } public function saveMarkup(): void { abort_unless(auth()->user()->plugins->contains($this->plugin), 403); $this->validate(); $this->plugin->update([ 'render_markup' => $this->markup_code ?? null, 'markup_language' => $this->markup_language ?? null ]); } protected array $rules = [ 'name' => 'required|string|max:255', 'data_stale_minutes' => 'required|integer|min:1', 'data_strategy' => 'required|string|in:polling,webhook,static', 'polling_url' => 'required_if:data_strategy,polling|nullable', 'polling_verb' => 'required|string|in:get,post', 'polling_header' => 'nullable|string|max:255', 'polling_body' => 'nullable|string', 'data_payload' => 'required_if:data_strategy,static|nullable|json', 'markup_code' => 'nullable|string', 'markup_language' => 'nullable|string|in:blade,liquid', 'checked_devices' => 'array', 'device_playlist_names' => 'array', 'device_playlists' => 'array', 'device_weekdays' => 'array', 'device_active_from' => 'array', 'device_active_until' => 'array', 'no_bleed' => 'boolean', 'dark_mode' => 'boolean', ]; public function editSettings() { abort_unless(auth()->user()->plugins->contains($this->plugin), 403); // Custom validation for polling_url with Liquid variable resolution $this->validatePollingUrl(); $validated = $this->validate(); $validated['data_payload'] = json_decode(Arr::get($validated,'data_payload'), true); $this->plugin->update($validated); foreach ($this->configuration_template as $fieldKey => $field) { if (($field['field_type'] ?? null) !== 'multi_string') { continue; } if (!isset($this->multiValues[$fieldKey])) { continue; } $validated[$fieldKey] = implode(',', array_filter(array_map('trim', $this->multiValues[$fieldKey]))); } } protected function validatePollingUrl(): void { if ($this->data_strategy === 'polling' && !empty($this->polling_url)) { try { $resolvedUrl = $this->plugin->resolveLiquidVariables($this->polling_url); if (!filter_var($resolvedUrl, FILTER_VALIDATE_URL)) { $this->addError('polling_url', 'The polling URL must be a valid URL after resolving configuration variables.'); } } catch (\Exception $e) { $this->addError('polling_url', 'Error resolving Liquid variables: ' . $e->getMessage() . $e->getPrevious()?->getMessage()); } } } public function updateData(): void { if ($this->plugin->data_strategy === 'polling') { try { $this->plugin->updateDataPayload(); $this->data_payload = json_encode($this->plugin->data_payload, JSON_PRETTY_PRINT); $this->data_payload_updated_at = $this->plugin->data_payload_updated_at; } catch (\Exception $e) { $this->dispatch('data-update-error', message: $e->getMessage() . $e->getPrevious()?->getMessage()); } } } public function getAvailablePlugins() { return auth()->user()->plugins()->where('id', '!=', $this->plugin->id)->get(); } public function getRequiredPluginCount(): int { if ($this->mashup_layout === 'full') { return 1; } return match ($this->mashup_layout) { '1Lx1R', '1Tx1B' => 2, // Left-Right or Top-Bottom split '1Lx2R', '2Lx1R', '2Tx1B', '1Tx2B' => 3, // Two on one side, one on other '2x2' => 4, // Quadrant default => 1, }; } public function addToPlaylist() { $this->validate([ 'checked_devices' => 'required|array|min:1', 'mashup_layout' => 'required|string', 'mashup_plugins' => 'required_if:mashup_layout,1Lx1R,1Lx2R,2Lx1R,1Tx1B,2Tx1B,1Tx2B,2x2|array', ]); // Validate that each checked device has a playlist selected foreach ($this->checked_devices as $deviceId) { if (!isset($this->device_playlists[$deviceId]) || empty($this->device_playlists[$deviceId])) { $this->addError('device_playlists.' . $deviceId, 'Please select a playlist for each device.'); return; } // If creating new playlist, validate required fields if ($this->device_playlists[$deviceId] === 'new') { if (!isset($this->device_playlist_names[$deviceId]) || empty($this->device_playlist_names[$deviceId])) { $this->addError('device_playlist_names.' . $deviceId, 'Playlist name is required when creating a new playlist.'); return; } } } foreach ($this->checked_devices as $deviceId) { $playlist = null; if ($this->device_playlists[$deviceId] === 'new') { // Create new playlist $playlist = \App\Models\Playlist::create([ 'device_id' => $deviceId, 'name' => $this->device_playlist_names[$deviceId], 'weekdays' => !empty($this->device_weekdays[$deviceId] ?? null) ? $this->device_weekdays[$deviceId] : null, 'active_from' => $this->device_active_from[$deviceId] ?? null, 'active_until' => $this->device_active_until[$deviceId] ?? null, ]); } else { $playlist = \App\Models\Playlist::findOrFail($this->device_playlists[$deviceId]); } // Add plugin to playlist $maxOrder = $playlist->items()->max('order') ?? 0; if ($this->mashup_layout === 'full') { $playlist->items()->create([ 'plugin_id' => $this->plugin->id, 'order' => $maxOrder + 1, ]); } else { // Create mashup $pluginIds = array_merge([$this->plugin->id], array_map('intval', $this->mashup_plugins)); \App\Models\PlaylistItem::createMashup( $playlist, $this->mashup_layout, $pluginIds, $this->plugin->name . ' Mashup', $maxOrder + 1 ); } } $this->reset([ 'checked_devices', 'device_playlists', 'device_playlist_names', 'device_weekdays', 'device_active_from', 'device_active_until', 'mashup_layout', 'mashup_plugins' ]); Flux::modal('add-to-playlist')->close(); } public function getDevicePlaylists($deviceId) { return \App\Models\Playlist::where('device_id', $deviceId)->get(); } public function hasAnyPlaylistSelected(): bool { foreach ($this->checked_devices as $deviceId) { if (isset($this->device_playlists[$deviceId]) && !empty($this->device_playlists[$deviceId])) { return true; } } return false; } public function getConfigurationValue($key, $default = null) { return $this->configuration[$key] ?? $default; } public function renderExample(string $example) { switch ($example) { case 'layoutTitle': $markup = $this->renderLayoutWithTitleBar(); break; case 'layout': $markup = $this->renderLayoutBlank(); break; default: $markup = '
This will remove this plugin from your account.