--- name: create-theme-from-site description: > Create a new Chamilo color theme (colors + logo) derived from an external website's actual branding, given a theme name and the site's URL — or, if given a local path to a logo image instead, derived from that logo's own dominant colors. Mirrors exactly what an admin can do manually through /admin/configuration/colors, done entirely via the Chamilo REST API. Use when the user wants a new theme "based on" / "matching" a given website or a given logo file, or runs /create-theme-from-site. --- # Create Theme From Site Given a **name** (e.g. `beeznest2`) and a **source** — either a **website URL** (e.g. `https://beeznest.com`) or a **local path to a logo image** (e.g. `/home/user/logos/acme.svg`) — create a new Chamilo `ColorTheme`, activate it as the platform's default theme, and attach a logo — all through the same API the admin Vue UI (`/admin/configuration/colors`) uses. No direct database or filesystem writes for the theme itself: every step from Step 5 onward is a real HTTP call against the running Chamilo instance, which is what keeps this safe and correct (it reuses tested code paths — entity validation, slug generation, `colors.css` rendering, image sanitization — instead of reinventing them). --- ## Step 0: Confirm inputs and scope, and detect the mode Ask for whichever of these are missing: - **Theme name** → becomes the `ColorTheme.title`; the theme's folder `slug` is auto-generated from it server-side (Gedmo slug) — never guess or derive the slug yourself, always read it back from the API response. - **Source** → either a website URL, or a local path to a logo image file. - **Activate immediately?** Setting a theme "active" makes it the platform's default look for every user on this access URL, immediately. Default to yes only if the user has clearly asked for it (as in "set it as default theme"); otherwise create the theme and ask before activating. **Detect which mode applies** from the source itself rather than asking: an `http(s)://` URL is `site` mode, a readable local file with an `image/*` MIME type is `logo` mode, and anything else is reported back to the user. - **`site` mode** → follow Steps 1–3 as written below (scrape the site's CSS for brand colors, then locate its logo separately). - **`logo` mode** → skip Steps 1–3 entirely and follow **Step 1–3 (logo mode)** just below instead: the given file supplies *both* the colors and the logo. Then continue at Step 4 as normal — everything from there on is identical regardless of mode. --- ## Step 1–3 (logo mode): derive colors and logo from a local image file Use this instead of Steps 1–3 when `MODE=logo`. ### Extract the dominant colors If the file is an **SVG**, try the cheap, exact route first — grep the source for literal fill colors, which are the real authored brand hex values with no quantization or anti-aliasing noise to filter out: ```bash grep -oE '(fill|stop-color)\s*[:=]\s*"?#[0-9A-Fa-f]{6}' logo.svg | grep -oE '#[0-9A-Fa-f]{6}' | sort | uniq -c | sort -rn ``` If that yields nothing usable (no literal hex fills — e.g. the SVG uses `currentColor` or CSS classes instead), or the file is a raster format (PNG/JPG/WebP), fall back to a histogram of the rasterized image. Flatten onto an implausible fill color first (bright magenta) so transparent- background pixels are unambiguously distinguishable from genuine logo pixels — including any real white, black, or near-white/black elements the logo itself actually draws, which must **not** be discarded as "background": ```bash convert logo.svg -background "#FF00FF" -flatten -resize 150x150 -colors 12 -depth 8 histogram:info:- \ | sed -E 's/^\s*([0-9]+):.*(#[0-9A-Fa-f]{6}).*/\1 \2/' \ | grep -vi '#FF00FF' \ | sort -rn ``` (`convert` rasterizes SVGs automatically via its delegate library — the same command works unchanged for PNG/JPG input.) Either way, you now have a frequency-ranked list of `count #RRGGBB` pairs. Map them the same way Step 2 would, minus the "grep usage context" part (there's no CSS to check a selector against here — frequency in the logo itself *is* the signal): - **primary** — the most frequent genuine color (excluding the magenta background marker). - **secondary** — the next most frequent, clearly distinct color. - **tertiary** — a third distinct color if the logo has one; if the logo is genuinely two-tone, fall back to a dark neutral (e.g. `35 35 35`) rather than forcing a weak third color into the role. Continue to **Step 4** using these three as the primary/secondary base colors (same downstream harmonization of success/info/warning/danger applies unchanged). ### Prepare the logo The given file *is* the logo — there's no site to search. Apply the same sizing rule as Step 3's site-mode logo handling: ```bash identify logo.png # check WxH (skip for SVG — no fixed raster size) convert logo.png -resize 190x60 logo_header.png # only if it exceeds 190x60 ``` If the source file is an SVG, use it directly for `header_svg`/`email_svg`, and additionally rasterize a PNG rendition for `header_png`/`email_png` (`convert logo.svg logo.png`, then resize per above if needed) — Step 8 uploads whichever fields you have. --- ## Step 1 (site mode): Fetch the source site ```bash curl -sL -A "Mozilla/5.0 (compatible; ChamiloThemeBot/1.0)" "" -o home.html ``` Extract every same-origin `` href from `home.html` and fetch each one. Skip (or heavily discount) generic CDN/vendor stylesheets (Bootstrap CDN builds, Google Fonts, common carousel/slider libraries) — they inject well-known framework-default colors (Bootstrap's stock `#0d6efd`/`#dc3545`/`#198754`/`#ffc107`/`#0dcaf0`/`#6c757d`) that will otherwise masquerade as "brand colors" if you just count frequency. --- ## Step 2 (site mode): Extract the real brand colors 1. Collect every `#rrggbb` (and `rgb()`/`rgba()`) color across the fetched CSS, tally frequency, and shortlist the top ~20. 2. For each shortlisted color, grep its actual usage context (the selector and property it appears in) — don't trust raw frequency alone. A color is a genuine brand signal when it shows up on: link hover states, button/CTA backgrounds, active nav states, headline accents/underlines. A color is framework noise when it only appears in unmodified `.btn-primary`, `.btn-success`, etc. rule blocks that match Bootstrap's own stock hex values verbatim. 3. Pick: - **primary** — the single most consistently-used deliberate accent color (CTAs, hovers, active states). This is almost always obvious once vendor noise is filtered out. - **secondary** — a second deliberate accent if one exists, otherwise the site's own custom neutral/text-link color (not a generic Bootstrap gray). - **tertiary** — the site's actual body text color (often a dark gray close to, but distinct from, `#212529`). 4. **Do not fully replace** `success`/`info`/`warning`/`danger` with site colors. Those carry meaning (green=good, red=bad) independent of brand identity, so their hue family must never change — but they should still feel like part of the same palette as `primary`/`secondary` rather than looking bolted on. Step 4 covers the harmonization (a small, capped hue nudge + a partial saturation blend toward the site's own primary/ secondary) that keeps each one clearly still "green"/"blue"/"yellow"/ "red" while relating to the brand. --- ## Step 3 (site mode): Find the logo Check, in this order, stopping at the first hit: 1. JSON-LD `"logo"` field (`