Spaces:
Sleeping
Sleeping
Download bridge/src/WorkflowElementGenerator.php from acecalisto3/UIKitV3-Automator: direct link, hf CLI and curl.
- Browser
- Download file 27.3 kB
-
https://huggingface.co/spaces/acecalisto3/UIKitV3-Automator/resolve/main/bridge/src/WorkflowElementGenerator.php
- Command line
-
hf download hf://spaces/acecalisto3/UIKitV3-Automator/bridge/src/WorkflowElementGenerator.php
-
curl -L -o WorkflowElementGenerator.php https://huggingface.co/spaces/acecalisto3/UIKitV3-Automator/resolve/main/bridge/src/WorkflowElementGenerator.php
27.3 kB
| namespace Fyoo\FigmaBridge; | |
| /** | |
| * Compiles a declarative workflow specification into native YOOtheme Pro | |
| * compound custom elements (element.json, element.php, template.php) and can | |
| * hydrate full YOOtheme builder layout trees from domain pipeline payloads. | |
| */ | |
| class WorkflowElementGenerator | |
| { | |
| private array $spec; | |
| public function __construct(array $spec) | |
| { | |
| if (empty($spec['name'])) { | |
| throw new \InvalidArgumentException("Workflow element spec must define a 'name'"); | |
| } | |
| if (empty($spec['title'])) { | |
| throw new \InvalidArgumentException("Workflow element spec must define a 'title'"); | |
| } | |
| $this->spec = $spec; | |
| } | |
| public static function fromJson(string $json): self | |
| { | |
| $data = json_decode($json, true); | |
| if (!is_array($data)) { | |
| throw new \InvalidArgumentException("Invalid JSON in workflow element spec"); | |
| } | |
| return new self($data); | |
| } | |
| public static function fromFile(string $path): self | |
| { | |
| if (!file_exists($path)) { | |
| throw new \InvalidArgumentException("Workflow spec file not found: $path"); | |
| } | |
| return self::fromJson((string) file_get_contents($path)); | |
| } | |
| public function name(): string | |
| { | |
| return $this->spec['name']; | |
| } | |
| public function itemName(): string | |
| { | |
| return $this->spec['item']['name'] ?? ($this->name() . '_item'); | |
| } | |
| public function isContainer(): bool | |
| { | |
| return !empty($this->spec['container']); | |
| } | |
| /** | |
| * Compiles all required files for the custom element. | |
| * | |
| * @return array<string, string> relative_file_path => content | |
| */ | |
| public function compile(): array | |
| { | |
| $files = []; | |
| $name = $this->name(); | |
| // 1. Parent element | |
| $files["elements/{$name}/element.json"] = json_encode($this->compileParentSchema(), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); | |
| $files["elements/{$name}/element.php"] = $this->compileElementPhp(); | |
| $files["elements/{$name}/templates/template.php"] = $this->compileParentTemplate(); | |
| $files["elements/{$name}/templates/content.php"] = $this->compileParentContent(); | |
| $files["elements/{$name}/images/icon.svg"] = $this->defaultIconSvg(); | |
| $files["elements/{$name}/images/iconSmall.svg"] = $this->defaultIconSmallSvg(); | |
| // 2. Child item element (if container) | |
| if ($this->isContainer()) { | |
| $itemName = $this->itemName(); | |
| $files["elements/{$itemName}/element.json"] = json_encode($this->compileItemSchema(), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); | |
| $files["elements/{$itemName}/element.php"] = $this->compileItemPhp(); | |
| $files["elements/{$itemName}/templates/template.php"] = $this->compileItemTemplate(); | |
| $files["elements/{$itemName}/templates/content.php"] = $this->compileItemContent(); | |
| $files["elements/{$itemName}/images/icon.svg"] = $this->defaultIconSvg(); | |
| $files["elements/{$itemName}/images/iconSmall.svg"] = $this->defaultIconSmallSvg(); | |
| } | |
| return $files; | |
| } | |
| /** | |
| * Installs the generated workflow element directly into the yootheme_patches child theme, | |
| * maintaining 100% architectural consistency with the XS Elements ecosystem. | |
| */ | |
| public function installToPatches(string $patchesDir): array | |
| { | |
| $patchesDir = rtrim($patchesDir, '/'); | |
| if (!is_dir($patchesDir) || !file_exists($patchesDir . '/config.php')) { | |
| throw new \InvalidArgumentException("Invalid yootheme_patches directory: $patchesDir"); | |
| } | |
| $rawName = $this->name(); | |
| $cleanName = preg_replace('/^xs_/', '', $rawName); | |
| $pascalName = str_replace(' ', '', ucwords(str_replace('_', ' ', $cleanName))); | |
| $camelName = lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $rawName)))); | |
| $targetElementDir = $patchesDir . '/src/Xs/' . $pascalName . '/element'; | |
| if (!is_dir($targetElementDir)) { | |
| mkdir($targetElementDir, 0755, true); | |
| } | |
| // Write elements without the extra 'elements/' prefix | |
| $compiled = $this->compile(); | |
| $writtenFiles = []; | |
| foreach ($compiled as $relPath => $content) { | |
| // Strip 'elements/' prefix | |
| $strippedPath = preg_replace('/^elements\//', '', $relPath); | |
| $fullPath = $targetElementDir . '/' . $strippedPath; | |
| $dir = dirname($fullPath); | |
| if (!is_dir($dir)) { | |
| mkdir($dir, 0755, true); | |
| } | |
| file_put_contents($fullPath, $content); | |
| $writtenFiles[] = $fullPath; | |
| } | |
| // 1. Update patches.php | |
| $patchesFile = $patchesDir . '/patches.php'; | |
| if (file_exists($patchesFile)) { | |
| $patchesCode = (string) file_get_contents($patchesFile); | |
| if (!str_contains($patchesCode, "'{$rawName}'")) { | |
| $hook = "// XS {$this->spec['title']} builder element (child-theme integrated)\n '{$rawName}' => true,\n"; | |
| $pos = strrpos($patchesCode, '];'); | |
| if ($pos !== false) { | |
| $patchesCode = substr_replace($patchesCode, " " . $hook, $pos, 0); | |
| file_put_contents($patchesFile, $patchesCode); | |
| } | |
| } | |
| } | |
| // 2. Update config.php | |
| $configFile = $patchesDir . '/config.php'; | |
| if (file_exists($configFile)) { | |
| $configCode = (string) file_get_contents($configFile); | |
| if (!str_contains($configCode, "'{$camelName}'")) { | |
| $block = "\n if (\$isPatchEnabled(\$config, '{$camelName}', true)) {\n" | |
| . " \$builder->addTypePath(__DIR__ . '/src/Xs/{$pascalName}/element/*/element.json');\n" | |
| . " }\n"; | |
| $anchor = "if (\$isPatchEnabled(\$config, 'xsChangelog', true)) {"; | |
| $pos = strpos($configCode, $anchor); | |
| if ($pos !== false) { | |
| $configCode = substr_replace($configCode, $block, $pos, 0); | |
| file_put_contents($configFile, $configCode); | |
| } | |
| } | |
| } | |
| return [ | |
| 'name' => $rawName, | |
| 'pascalName' => $pascalName, | |
| 'camelName' => $camelName, | |
| 'targetDir' => $targetElementDir, | |
| 'files' => $writtenFiles, | |
| ]; | |
| } | |
| public function spec(): array | |
| { | |
| return $this->spec; | |
| } | |
| /** | |
| * Writes compiled elements to the destination directory. | |
| */ | |
| public function write(string $targetDir): void | |
| { | |
| $compiled = $this->compile(); | |
| foreach ($compiled as $relPath => $content) { | |
| $fullPath = rtrim($targetDir, '/') . '/' . $relPath; | |
| $dir = dirname($fullPath); | |
| if (!is_dir($dir)) { | |
| mkdir($dir, 0755, true); | |
| } | |
| file_put_contents($fullPath, $content); | |
| } | |
| } | |
| /** | |
| * Writes compiled elements and zips them into a single archive. | |
| */ | |
| public function zip(string $outDir, string $zipPath): void | |
| { | |
| $this->write($outDir); | |
| $zip = new \ZipArchive(); | |
| if ($zip->open($zipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) { | |
| throw new \RuntimeException("Cannot create zip archive at: $zipPath"); | |
| } | |
| $files = new \RecursiveIteratorIterator( | |
| new \RecursiveDirectoryIterator($outDir, \RecursiveDirectoryIterator::SKIP_DOTS), | |
| \RecursiveIteratorIterator::LEAVES_ONLY | |
| ); | |
| foreach ($files as $file) { | |
| if (!$file->isDir()) { | |
| $filePath = $file->getRealPath(); | |
| $relativePath = substr($filePath, strlen(realpath($outDir)) + 1); | |
| $zip->addFile($filePath, $relativePath); | |
| } | |
| } | |
| $zip->close(); | |
| } | |
| /** | |
| * Generates the YOOtheme builder element.json for the parent element. | |
| */ | |
| public function compileParentSchema(): array | |
| { | |
| $fields = []; | |
| $defaults = []; | |
| $tabs = []; | |
| foreach ($this->spec['tabs'] ?? [] as $tab) { | |
| $tabTitle = $tab['title'] ?? 'General'; | |
| $tabFieldNames = []; | |
| foreach ($tab['fields'] ?? [] as $f) { | |
| $fName = $f['name']; | |
| $tabFieldNames[] = $fName; | |
| $fieldDef = [ | |
| 'label' => $f['label'] ?? ucfirst(str_replace('_', ' ', $fName)), | |
| 'type' => $this->mapFieldType($f['type'] ?? 'text'), | |
| ]; | |
| if (isset($f['description'])) { | |
| $fieldDef['description'] = $f['description']; | |
| } | |
| if (isset($f['options']) && is_array($f['options'])) { | |
| $fieldDef['options'] = $f['options']; | |
| } | |
| if (isset($f['show'])) { | |
| $fieldDef['show'] = $f['show']; | |
| } | |
| $fields[$fName] = $fieldDef; | |
| if (isset($f['default'])) { | |
| $defaults[$fName] = $f['default']; | |
| } | |
| } | |
| $tabs[] = [ | |
| 'title' => $tabTitle, | |
| 'fields' => $tabFieldNames, | |
| ]; | |
| } | |
| // Append builder.advanced tab for standard YOOtheme advanced controls | |
| $tabs[] = '${builder.advanced}'; | |
| $schema = [ | |
| '@import' => './element.php', | |
| 'name' => $this->name(), | |
| 'title' => $this->spec['title'], | |
| 'group' => $this->spec['group'] ?? 'Workflow Elements', | |
| 'icon' => '${url:images/icon.svg}', | |
| 'iconSmall' => '${url:images/iconSmall.svg}', | |
| 'element' => true, | |
| 'container' => $this->isContainer(), | |
| 'width' => 500, | |
| 'defaults' => $defaults, | |
| 'templates' => [ | |
| 'render' => './templates/template.php', | |
| ], | |
| 'fields' => $fields, | |
| 'fieldset' => [ | |
| 'default' => [ | |
| 'type' => 'tabs', | |
| 'fields' => $tabs, | |
| ], | |
| ], | |
| ]; | |
| return $schema; | |
| } | |
| /** | |
| * Generates the YOOtheme builder element.json for the child item element. | |
| */ | |
| public function compileItemSchema(): array | |
| { | |
| $itemSpec = $this->spec['item'] ?? []; | |
| $itemName = $this->itemName(); | |
| $itemTitle = $itemSpec['title'] ?? ($this->spec['title'] . ' Record'); | |
| $fields = []; | |
| $defaults = []; | |
| $tabs = []; | |
| foreach ($itemSpec['tabs'] ?? [] as $tab) { | |
| $tabTitle = $tab['title'] ?? 'Record'; | |
| $tabFieldNames = []; | |
| foreach ($tab['fields'] ?? [] as $f) { | |
| $fName = $f['name']; | |
| $tabFieldNames[] = $fName; | |
| $fieldDef = [ | |
| 'label' => $f['label'] ?? ucfirst(str_replace('_', ' ', $fName)), | |
| 'type' => $this->mapFieldType($f['type'] ?? 'text'), | |
| ]; | |
| if (isset($f['description'])) { | |
| $fieldDef['description'] = $f['description']; | |
| } | |
| if (isset($f['options']) && is_array($f['options'])) { | |
| $fieldDef['options'] = $f['options']; | |
| } | |
| if (isset($f['show'])) { | |
| $fieldDef['show'] = $f['show']; | |
| } | |
| $fields[$fName] = $fieldDef; | |
| if (isset($f['default'])) { | |
| $defaults[$fName] = $f['default']; | |
| } | |
| } | |
| $tabs[] = [ | |
| 'title' => $tabTitle, | |
| 'fields' => $tabFieldNames, | |
| ]; | |
| } | |
| $tabs[] = '${builder.advanced}'; | |
| return [ | |
| '@import' => './element.php', | |
| 'name' => $itemName, | |
| 'title' => $itemTitle, | |
| 'width' => 500, | |
| 'defaults' => $defaults, | |
| 'templates' => [ | |
| 'render' => './templates/template.php', | |
| ], | |
| 'fields' => $fields, | |
| 'fieldset' => [ | |
| 'default' => [ | |
| 'type' => 'tabs', | |
| 'fields' => $tabs, | |
| ], | |
| ], | |
| ]; | |
| } | |
| /** | |
| * Hydrates an intelligent domain pipeline payload into a complete YOOtheme builder layout tree. | |
| */ | |
| public function hydrateLayout(array $payload, array $overrides = []): array | |
| { | |
| $parentProps = array_merge( | |
| $this->extractParentProps($payload), | |
| $overrides['props'] ?? [] | |
| ); | |
| $children = []; | |
| if ($this->isContainer()) { | |
| $rawItems = $payload['items'] ?? $payload['records'] ?? $payload['agenda'] ?? []; | |
| foreach ($rawItems as $rawItem) { | |
| $children[] = [ | |
| 'type' => $this->itemName(), | |
| 'props' => $this->extractItemProps($rawItem), | |
| ]; | |
| } | |
| } | |
| return [ | |
| 'type' => 'layout', | |
| 'version' => '5.0.0', | |
| 'children' => [ | |
| [ | |
| 'type' => 'section', | |
| 'props' => [ | |
| 'style' => 'default', | |
| 'width' => 'default', | |
| 'padding' => 'default', | |
| ], | |
| 'children' => [ | |
| [ | |
| 'type' => 'row', | |
| 'children' => [ | |
| [ | |
| 'type' => 'column', | |
| 'props' => ['image_position' => 'center-center'], | |
| 'children' => [ | |
| [ | |
| 'type' => $this->name(), | |
| 'props' => $parentProps, | |
| 'children' => $children, | |
| ], | |
| ], | |
| ], | |
| ], | |
| ], | |
| ], | |
| ], | |
| ], | |
| ]; | |
| } | |
| private function extractParentProps(array $payload): array | |
| { | |
| $props = []; | |
| foreach ($this->spec['tabs'] ?? [] as $tab) { | |
| foreach ($tab['fields'] ?? [] as $f) { | |
| $k = $f['name']; | |
| if (array_key_exists($k, $payload)) { | |
| $props[$k] = $payload[$k]; | |
| } elseif (isset($f['default'])) { | |
| $props[$k] = $f['default']; | |
| } | |
| } | |
| } | |
| return $props; | |
| } | |
| private function extractItemProps(array $rawItem): array | |
| { | |
| $props = []; | |
| $itemTabs = $this->spec['item']['tabs'] ?? []; | |
| foreach ($itemTabs as $tab) { | |
| foreach ($tab['fields'] ?? [] as $f) { | |
| $k = $f['name']; | |
| if (array_key_exists($k, $rawItem)) { | |
| $props[$k] = $rawItem[$k]; | |
| } elseif (isset($f['default'])) { | |
| $props[$k] = $f['default']; | |
| } | |
| } | |
| } | |
| return $props; | |
| } | |
| private function compileElementPhp(): string | |
| { | |
| return <<<'PHP' | |
| <?php | |
| namespace YOOtheme; | |
| return [ | |
| 'transforms' => [ | |
| 'render' => function ($node) { | |
| // Ensure child records are available | |
| $node->children = $node->children ?? []; | |
| return $node; | |
| }, | |
| ], | |
| ]; | |
| PHP; | |
| } | |
| private function compileItemPhp(): string | |
| { | |
| return <<<'PHP' | |
| <?php | |
| namespace YOOtheme; | |
| return [ | |
| 'transforms' => [ | |
| 'render' => function ($node) { | |
| return $node; | |
| }, | |
| ], | |
| ]; | |
| PHP; | |
| } | |
| private function compileParentTemplate(): string | |
| { | |
| return <<<'PHP' | |
| <?php | |
| defined('_JEXEC') or die; | |
| $props = $props ?? []; | |
| $children = $children ?? []; | |
| $title = htmlspecialchars((string) ($props['title'] ?? ''), ENT_QUOTES, 'UTF-8'); | |
| $summary = htmlspecialchars((string) ($props['summary'] ?? ''), ENT_QUOTES, 'UTF-8'); | |
| $mediaUrl = (string) ($props['recording_url'] ?? $props['live_url'] ?? ''); | |
| $mediaMode = (string) ($props['media_mode'] ?? 'none'); | |
| $cardStyle = (string) ($props['card_style'] ?? 'default'); | |
| $headingLevel = (string) ($props['heading_level'] ?? 'h2'); | |
| $layout = (string) ($props['layout'] ?? 'workspace'); | |
| $status = (string) ($props['meeting_status'] ?? $props['status'] ?? ''); | |
| $badgeClass = 'uk-badge'; | |
| if (in_array($status, ['live', 'active'])) { | |
| $badgeClass .= ' uk-label-danger'; | |
| } elseif (in_array($status, ['complete', 'approved'])) { | |
| $badgeClass .= ' uk-label-success'; | |
| } | |
| $elementId = 'wf-' . substr(md5($title . ($props['meeting_key'] ?? '')), 0, 8); | |
| ?> | |
| <div id="<?= $elementId ?>" class="fyoo-workflow-container uk-margin-medium-bottom"> | |
| <div class="uk-card uk-card-<?= htmlspecialchars($cardStyle, ENT_QUOTES, 'UTF-8') ?> uk-card-body uk-card-hover uk-border-rounded"> | |
| <!-- Workflow Header --> | |
| <div class="uk-flex uk-flex-between uk-flex-middle uk-flex-wrap uk-margin-small-bottom"> | |
| <div> | |
| <?php if (!empty($props['body_name'])): ?> | |
| <span class="uk-text-meta uk-text-uppercase uk-text-bold"><?= htmlspecialchars($props['body_name'], ENT_QUOTES, 'UTF-8') ?></span> | |
| <?php endif; ?> | |
| <<?= $headingLevel ?> class="uk-margin-remove-top uk-margin-small-bottom"><?= $title ?></<?= $headingLevel ?>> | |
| </div> | |
| <?php if ($status !== ''): ?> | |
| <div> | |
| <span class="<?= $badgeClass ?>"><?= htmlspecialchars(strtoupper($status), ENT_QUOTES, 'UTF-8') ?></span> | |
| </div> | |
| <?php endif; ?> | |
| </div> | |
| <?php if ($summary !== ''): ?> | |
| <p class="uk-text-muted uk-margin-small-bottom"><?= $summary ?></p> | |
| <?php endif; ?> | |
| <!-- Split Workspace Layout --> | |
| <div class="uk-grid-match uk-grid-medium uk-margin-medium-top" uk-grid> | |
| <!-- Media & Details Column --> | |
| <?php if ($mediaMode !== 'none' && $mediaUrl !== ''): ?> | |
| <div class="uk-width-2-5@m"> | |
| <div class="uk-card uk-card-default uk-card-body uk-padding-small uk-border-rounded" uk-sticky="offset: 24; bottom: #<?= $elementId ?>;"> | |
| <div class="uk-cover-container uk-height-medium uk-border-rounded"> | |
| <?php if (str_contains($mediaUrl, 'youtube.com') || str_contains($mediaUrl, 'youtu.be') || str_contains($mediaUrl, 'vimeo.com')): ?> | |
| <iframe src="<?= htmlspecialchars($mediaUrl, ENT_QUOTES, 'UTF-8') ?>" uk-cover uk-responsive frameborder="0" allowfullscreen></iframe> | |
| <?php else: ?> | |
| <video id="<?= $elementId ?>-player" class="uk-width-1-1 uk-border-rounded" controls <?= !empty($props['poster']) ? 'poster="' . htmlspecialchars($props['poster'], ENT_QUOTES, 'UTF-8') . '"' : '' ?>> | |
| <source src="<?= htmlspecialchars($mediaUrl, ENT_QUOTES, 'UTF-8') ?>" type="video/mp4"> | |
| <?php if (!empty($props['captions_url'])): ?> | |
| <track kind="captions" src="<?= htmlspecialchars($props['captions_url'], ENT_QUOTES, 'UTF-8') ?>" srclang="en" label="English" default> | |
| <?php endif; ?> | |
| </video> | |
| <?php endif; ?> | |
| </div> | |
| <?php if (!empty($props['starts_at']) || !empty($props['location'])): ?> | |
| <ul class="uk-list uk-list-divider uk-margin-small-top uk-text-small"> | |
| <?php if (!empty($props['starts_at'])): ?> | |
| <li><span uk-icon="icon: calendar; ratio: 0.8" class="uk-margin-small-right"></span><?= htmlspecialchars($props['starts_at'], ENT_QUOTES, 'UTF-8') ?></li> | |
| <?php endif; ?> | |
| <?php if (!empty($props['location'])): ?> | |
| <li><span uk-icon="icon: location; ratio: 0.8" class="uk-margin-small-right"></span><?= htmlspecialchars($props['location'], ENT_QUOTES, 'UTF-8') ?></li> | |
| <?php endif; ?> | |
| </ul> | |
| <?php endif; ?> | |
| </div> | |
| </div> | |
| <?php endif; ?> | |
| <!-- Records & Pipeline Items Column --> | |
| <div class="<?= ($mediaMode !== 'none' && $mediaUrl !== '') ? 'uk-width-3-5@m' : 'uk-width-1-1' ?>"> | |
| <?php if (!empty($props['show_filters']) && !empty($children)): ?> | |
| <div uk-filter="target: .js-workflow-filter"> | |
| <ul class="uk-subnav uk-subnav-pill uk-margin-small-bottom"> | |
| <li class="uk-active" uk-filter-control><a href="#">All</a></li> | |
| <li uk-filter-control="filter: [data-kind='agenda']"><a href="#">Agenda</a></li> | |
| <li uk-filter-control="filter: [data-kind='resource']"><a href="#">Documents</a></li> | |
| <li uk-filter-control="filter: [data-kind='chapter']"><a href="#">Chapters</a></li> | |
| </ul> | |
| <ul class="js-workflow-filter uk-list uk-list-medium"> | |
| <?php foreach ($children as $child): ?> | |
| <li><?= $builder->render($child) ?></li> | |
| <?php endforeach; ?> | |
| </ul> | |
| </div> | |
| <?php else: ?> | |
| <ul class="uk-list uk-list-medium"> | |
| <?php foreach ($children as $child): ?> | |
| <li><?= $builder->render($child) ?></li> | |
| <?php endforeach; ?> | |
| </ul> | |
| <?php endif; ?> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| PHP; | |
| } | |
| private function compileItemTemplate(): string | |
| { | |
| return <<<'PHP' | |
| <?php | |
| defined('_JEXEC') or die; | |
| $props = $props ?? []; | |
| $type = (string) ($props['record_type'] ?? 'agenda'); | |
| $title = htmlspecialchars((string) ($props['title'] ?? ''), ENT_QUOTES, 'UTF-8'); | |
| $desc = htmlspecialchars((string) ($props['description'] ?? ''), ENT_QUOTES, 'UTF-8'); | |
| $num = htmlspecialchars((string) ($props['agenda_number'] ?? ''), ENT_QUOTES, 'UTF-8'); | |
| $outcome = (string) ($props['outcome'] ?? ''); | |
| $link = (string) ($props['link'] ?? ''); | |
| $startTime = (string) ($props['start_time'] ?? ''); | |
| $speaker = (string) ($props['speaker'] ?? $props['presenter'] ?? ''); | |
| ?> | |
| <div class="uk-card uk-card-default uk-card-small uk-card-body uk-border-rounded uk-box-shadow-hover-small" data-kind="<?= htmlspecialchars($type, ENT_QUOTES, 'UTF-8') ?>"> | |
| <div class="uk-flex uk-flex-between uk-flex-middle"> | |
| <div class="uk-flex uk-flex-middle"> | |
| <?php if ($num !== ''): ?> | |
| <span class="uk-badge uk-margin-small-right"><?= $num ?></span> | |
| <?php endif; ?> | |
| <h4 class="uk-margin-remove uk-text-bold"><?= $title ?></h4> | |
| </div> | |
| <div> | |
| <?php if ($outcome !== ''): ?> | |
| <span class="uk-label uk-label-<?= ($outcome === 'approved' ? 'success' : ($outcome === 'tabled' ? 'warning' : 'muted')) ?>"><?= htmlspecialchars(strtoupper($outcome), ENT_QUOTES, 'UTF-8') ?></span> | |
| <?php endif; ?> | |
| <?php if ($startTime !== ''): ?> | |
| <span class="uk-label uk-label-primary"><span uk-icon="icon: play; ratio: 0.6"></span> <?= htmlspecialchars($startTime, ENT_QUOTES, 'UTF-8') ?></span> | |
| <?php endif; ?> | |
| </div> | |
| </div> | |
| <?php if ($desc !== ''): ?> | |
| <p class="uk-margin-small-top uk-margin-remove-bottom uk-text-small uk-text-muted"><?= $desc ?></p> | |
| <?php endif; ?> | |
| <?php if ($speaker !== ''): ?> | |
| <div class="uk-text-meta uk-margin-xsmall-top"><span uk-icon="icon: user; ratio: 0.7"></span> <?= htmlspecialchars($speaker, ENT_QUOTES, 'UTF-8') ?></div> | |
| <?php endif; ?> | |
| <?php if ($link !== ''): ?> | |
| <div class="uk-margin-small-top"> | |
| <a href="<?= htmlspecialchars($link, ENT_QUOTES, 'UTF-8') ?>" class="uk-button uk-button-default uk-button-small" target="_blank"> | |
| <span uk-icon="icon: download; ratio: 0.7"></span> View Resource | |
| </a> | |
| </div> | |
| <?php endif; ?> | |
| </div> | |
| PHP; | |
| } | |
| private function compileParentContent(): string | |
| { | |
| return <<<'PHP' | |
| <?php | |
| defined('_JEXEC') or die; | |
| $props = is_array($props ?? null) ? $props : []; | |
| $children = is_array($children ?? null) ? $children : []; | |
| $title = is_string($props['title'] ?? null) ? trim($props['title']) : ''; | |
| ?> | |
| <?php if ($title !== '') : ?> | |
| <p><strong><?= htmlspecialchars($title, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?></strong></p> | |
| <?php endif; ?> | |
| <?php if ($children !== []) : ?> | |
| <ol> | |
| <?php foreach ($children as $child) : ?> | |
| <li><?= $builder->render($child, ['element' => $props]) ?></li> | |
| <?php endforeach; ?> | |
| </ol> | |
| <?php endif; ?> | |
| PHP; | |
| } | |
| private function compileItemContent(): string | |
| { | |
| return <<<'PHP' | |
| <?php | |
| defined('_JEXEC') or die; | |
| $props = is_array($props ?? null) ? $props : []; | |
| $title = is_string($props['title'] ?? null) ? trim($props['title']) : ''; | |
| $desc = is_string($props['description'] ?? null) ? trim($props['description']) : ''; | |
| ?> | |
| <?php if ($title !== '') : ?> | |
| <strong><?= htmlspecialchars($title, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?></strong> | |
| <?php endif; ?> | |
| <?php if ($desc !== '') : ?> | |
| <p><?= htmlspecialchars($desc, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?></p> | |
| <?php endif; ?> | |
| PHP; | |
| } | |
| private function mapFieldType(string $type): string | |
| { | |
| return match ($type) { | |
| 'text' => 'text', | |
| 'textarea' => 'textarea', | |
| 'editor' => 'editor', | |
| 'select' => 'select', | |
| 'checkbox' => 'checkbox', | |
| 'color' => 'color', | |
| 'image' => 'image', | |
| 'video' => 'video', | |
| 'number' => 'number', | |
| 'range' => 'range', | |
| default => 'text', | |
| }; | |
| } | |
| private function defaultIconSvg(): string | |
| { | |
| return '<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><circle fill="none" stroke="#000" stroke-width="1.1" cx="10" cy="10" r="9"/><polygon points="8,5 14,10 8,15" fill="#000"/></svg>'; | |
| } | |
| private function defaultIconSmallSvg(): string | |
| { | |
| return '<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><circle fill="none" stroke="#000" stroke-width="1" cx="7" cy="7" r="6"/></svg>'; | |
| } | |
| } | |