metatroncubeswdev cf656377ef O9: align Admissions, Contact Us and Login with the homepage design, real Metatroncube logo/favicon, and a chain of placeholder-content leaks found underneath
Per discussion: the marketing homepage looked polished but the rest of
the public site (the admission form, contact us, the backend login
screen) was still bare stock Odoo, and the site nav still showed
stock Odoo's own default logo/favicon rather than Metatroncube's real
ones. This brings the same design system (Manrope + Work Sans, the
navy/indigo palette) to all three pages and wires up the two supplied
brand assets - static/img/logo.png (the wordmark, used as
website.logo, so it renders in every page's nav automatically) and
static/img/favicon.png (the cube mark, website.favicon). Loaded via a
post_init_hook (hooks.py) rather than a plain <record>: website's two
stock demo sites (website.default_website, website.website2) are both
noupdate=True, and Python is the tool that isn't gated by that the way
a <record>/<function> tag would be on an already-installed database
(see hooks.py's own docstring for the exact mechanism and its
fresh-install-only limitation).

Admissions keeps its exact form markup/field names/model untouched -
only the surrounding hero and "what happens next" strip are new, added
via position="before"/"after" around the existing <section> so the
stock s_website_form JS widget this page depends on is never touched.

Restyling contact us and the login screen surfaced a chain of real,
separate placeholder-content bugs, each found by looking at the next
page down rather than assuming the previous fix was complete:

1. website.contactus's sidebar hardcodes "My Company" and a fake US
   street address - shared/DEMO_SCRIPT.md explicitly bans placeholder
   content, and it had been live here the whole time. Reading
   res_company fields instead means it shows whichever school is
   actually active, same as the admission form's own live program list.
2. website.footer_custom - the footer on EVERY page, not just contact
   us - carries the identical yourcompany.example.com / +1
   555-555-5556 placeholder, plus a "Products/Services/Legal" link
   list to nowhere and dead social icons. Fixing #1 alone would have
   left this sitting directly underneath it on every single page.
3. website.header_text_element, the nav bar's own phone/email widget,
   carries the same fake number - a fourth, separate occurrence, this
   time in the header. First pass here fixed only its "phone_mail"
   variant, assumed (wrongly) to be what this site's header actually
   uses; the live page still showed the fake number afterward. It
   turned out both nav slots (desktop, mobile) use the plain default
   branch instead - caught only by re-checking the real page rather
   than trusting that assumption, and fixed every variant that carries
   contact info this time, not just the one guessed at.
4. Underneath all three page-level fixes: base.main_company itself
   still carried stock Odoo's own demo phone (+1 555-555-5556) and
   email (info@yourcompany.com) - the page-level fixes above only
   surfaced this because they read real company data for the first
   time; the data itself needed fixing too. Real Indian/Canadian
   contact details added to both demo companies in mc_theme_demo.xml.
5. web.login_layout hardcodes its own separate "Powered by Odoo" link
   - a third distinct occurrence, different from the portal sidebar
   text and the web.brand_promotion badge already fixed, reached by
   the very first screen anyone doing this demo sees.

Full suite (all 9 modules + web_responsive) re-verified together after
every fix in this chain: 0 failed, 0 error(s), tests up to 93.

One correction to this session's own earlier work: mc_cast_users_demo.xml
claimed <function> "isn't subject to" the noupdate/mode guard that
blocks a plain <record> on an already-installed database - false,
odoo/tools/convert.py's _tag_function has the exact same
"if self.noupdate and self.mode != 'init': return" check. What
actually fixed that earlier bug was applying the change as a one-off
manual Python snippet, not the tag choice - the same manual-application
pattern used again here for base.main_company's contact fields on the
already-running school_demo database.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 00:53:31 -04:00

249 lines
17 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Same type pairing as mc_education_theme's marketing homepage
(Manrope for headings, Work Sans for body) so a visitor moving
from the homepage to this page doesn't land on a visibly
different, unstyled product. Loaded here too, not only from
theme, so this module still looks intentional if installed on
its own - matching CLAUDE.md's "each addon installs
independently" rule; the browser dedupes the identical URL if
both happen to be loaded on the same site. -->
<template id="admission_fonts" inherit_id="website.layout" name="Admission form fonts">
<xpath expr="//head" position="inside">
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="crossorigin"/>
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@600;700;800&amp;family=Work+Sans:wght@400;500;600;700&amp;display=swap" rel="stylesheet"/>
</xpath>
</template>
<!--
Hand-written to match the DOM structure/classes of Odoo core's own
s_website_form snippet (website/views/snippets/s_website_form.xml)
exactly, so the stock frontend widget (form.js, loaded by every
website page via website.layout) picks it up automatically - no
custom JS, no custom submit controller. Posting still goes through
Odoo's generic /website/form/<model> controller; see
data/mc_applicant_website_form_data.xml for the field whitelist
that is the actual security boundary on that path.
The hero band and "what happens next" strip around the form are
presentation only, matching the site design system - they do not
touch a single field, name or class inside <form>, since that
exact markup is what the stock form.js widget depends on.
-->
<template id="admission_apply_page" name="Admission Application">
<t t-call="website.layout">
<div id="wrap" class="mc-admissions">
<style>
.mc-admissions{ --navy:#131B2E; --navy-2:#1C2A46; --indigo:#4B3F92; --indigo-2:#2E63B8;
--paper:#F6F7FB; --card:#FFFFFF; --line:#E4E7F0; --ink:#16192B; --ink-soft:#5B6178;
--shadow:0 1px 2px rgba(19,27,46,.06), 0 14px 34px -16px rgba(19,27,46,.22);
font-family:'Work Sans', system-ui, sans-serif; color:var(--ink); background:var(--paper);
}
.mc-admissions *{ box-sizing:border-box; }
.mc-admissions h1, .mc-admissions h2, .mc-admissions h3{
font-family:'Manrope', system-ui, sans-serif; font-weight:800; letter-spacing:-.01em;
color:var(--ink); text-wrap:balance; margin:0;
}
.mc-adm-hero{
background: radial-gradient(900px 380px at 90% -10%, rgba(75,63,146,.5), transparent 60%),
linear-gradient(180deg, var(--navy), var(--navy-2));
color:#fff; padding:52px 24px 60px; text-align:center;
}
.mc-adm-hero .pill{
display:inline-block; font-size:.72rem; letter-spacing:.08em; text-transform:uppercase;
font-weight:600; color:#EFE9FF; background:linear-gradient(90deg, var(--indigo), var(--indigo-2));
padding:6px 14px; border-radius:20px; margin-bottom:16px;
}
.mc-adm-hero h1{ color:#fff; font-size:clamp(1.6rem,3.4vw,2.3rem); max-width:20ch; margin:0 auto; }
.mc-adm-hero p{ color:#C4C9E2; max-width:52ch; margin:14px auto 0; font-size:1rem; line-height:1.6; }
.mc-adm-body{ max-width:900px; margin:-30px auto 0; padding:0 20px 64px; position:relative; }
.mc-adm-card{
background:var(--card); border:1px solid var(--line); border-radius:16px;
box-shadow:var(--shadow); padding:36px 32px;
}
@media (max-width:600px){ .mc-adm-card{ padding:26px 18px; } }
.mc-adm-card h1{ font-size:1.4rem; margin-bottom:6px; }
.mc-adm-card .lede{ color:var(--ink-soft); margin-bottom:26px; font-size:.94rem; }
.mc-admissions .s_website_form_label{ font-weight:600; color:var(--ink); font-size:.88rem; }
.mc-admissions .s_website_form_mark{ color:var(--indigo); }
.mc-admissions .form-control, .mc-admissions .form-select{
border-color:var(--line); border-radius:9px; padding:10px 12px; font-family:'Work Sans', sans-serif;
}
.mc-admissions .form-control:focus, .mc-admissions .form-select:focus{
border-color:var(--indigo); box-shadow:0 0 0 3px rgba(75,63,146,.15);
}
.mc-admissions .btn-primary{
background:linear-gradient(90deg, var(--indigo), var(--indigo-2)); border:none;
padding:11px 26px; border-radius:9px; font-weight:600;
}
.mc-admissions .btn-primary:hover{ filter:brightness(1.08); }
.mc-adm-steps{ max-width:900px; margin:0 auto; padding:0 20px 70px; }
.mc-adm-steps h2{ font-size:1.1rem; text-align:center; margin-bottom:24px; }
.mc-steps-row{ display:grid; grid-template-columns:repeat(5,1fr); gap:10px; }
@media (max-width:760px){ .mc-steps-row{ grid-template-columns:repeat(2,1fr); } }
.mc-step{ background:var(--card); border:1px solid var(--line); border-radius:12px; padding:16px 12px; text-align:center; }
.mc-step .n{
width:26px; height:26px; border-radius:50%; background:var(--indigo); color:#fff;
display:flex; align-items:center; justify-content:center; margin:0 auto 8px;
font-family:'Manrope',sans-serif; font-weight:700; font-size:.8rem;
}
.mc-step .t{ font-size:.82rem; font-weight:600; color:var(--ink); }
</style>
<div class="mc-adm-hero">
<span class="pill">Admissions Open</span>
<h1>Apply for Admission</h1>
<p>Fill in your child's details, attach a birth certificate, and submit - no office visit needed. You'll get an application number the moment you submit, and can track its progress from Applied through to Enrolled.</p>
</div>
<div class="mc-adm-body">
<div class="mc-adm-card">
<section class="s_website_form pt32 pb32" data-snippet="s_website_form">
<div class="container-fluid">
<h1 class="mb-4">Application Form</h1>
<form action="/website/form/" method="post" enctype="multipart/form-data"
class="o_mark_required" data-mark="*"
data-model_name="mc.applicant"
data-success-mode="message">
<div class="s_website_form_rows row s_col_no_bgcolor">
<div data-name="Field" class="s_website_form_field mb-3 col-12 s_website_form_required" data-type="char">
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-3 s_website_form_label" for="admission_name">
<span class="s_website_form_label_content">Student Name</span>
<span class="s_website_form_mark"> *</span>
</label>
<div class="col-sm-9">
<input class="form-control s_website_form_input" type="text"
name="name" id="admission_name" required="1"/>
</div>
</div>
</div>
<div data-name="Field" class="s_website_form_field mb-3 col-12" data-type="date">
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-3 s_website_form_label" for="admission_dob">
<span class="s_website_form_label_content">Date of Birth</span>
</label>
<div class="col-sm-9">
<input class="form-control s_website_form_input" type="date"
name="dob" id="admission_dob"/>
</div>
</div>
</div>
<div data-name="Field" class="s_website_form_field mb-3 col-12 s_website_form_required" data-type="many2one">
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-3 s_website_form_label" for="admission_program_id">
<span class="s_website_form_label_content">Grade Applied For</span>
<span class="s_website_form_mark"> *</span>
</label>
<div class="col-sm-9">
<select class="form-select s_website_form_input"
name="program_id" id="admission_program_id" required="1">
<option value="">Select a grade...</option>
<!-- Program display labels are public information (a
school's grade list is meant to be seen by anyone
filling this form) - sudo() only reads name/id for
this dropdown, nothing is written here. -->
<t t-foreach="request.env['mc.program'].sudo().search([], order='sequence_no')" t-as="program">
<option t-att-value="program.id" t-esc="program.display_label"/>
</t>
</select>
</div>
</div>
</div>
<div data-name="Field" class="s_website_form_field mb-3 col-12 s_website_form_required" data-type="char">
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-3 s_website_form_label" for="admission_guardian_name">
<span class="s_website_form_label_content">Parent / Guardian Name</span>
<span class="s_website_form_mark"> *</span>
</label>
<div class="col-sm-9">
<input class="form-control s_website_form_input" type="text"
name="guardian_name" id="admission_guardian_name" required="1"/>
</div>
</div>
</div>
<div data-name="Field" class="s_website_form_field mb-3 col-12 s_website_form_required" data-type="tel">
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-3 s_website_form_label" for="admission_guardian_phone">
<span class="s_website_form_label_content">Parent / Guardian Phone</span>
<span class="s_website_form_mark"> *</span>
</label>
<div class="col-sm-9">
<input class="form-control s_website_form_input" type="tel"
name="guardian_phone" id="admission_guardian_phone" required="1"/>
</div>
</div>
</div>
<div data-name="Field" class="s_website_form_field mb-3 col-12 s_website_form_required" data-type="email">
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-3 s_website_form_label" for="admission_guardian_email">
<span class="s_website_form_label_content">Parent / Guardian Email</span>
<span class="s_website_form_mark"> *</span>
</label>
<div class="col-sm-9">
<input class="form-control s_website_form_input" type="email"
name="guardian_email" id="admission_guardian_email" required="1"/>
</div>
</div>
</div>
<div data-name="Field" class="s_website_form_field mb-3 col-12" data-type="binary">
<div class="row s_col_no_resize s_col_no_bgcolor">
<label class="col-form-label col-sm-3 s_website_form_label" for="admission_birth_certificate">
<span class="s_website_form_label_content">Birth Certificate</span>
</label>
<div class="col-sm-9">
<!-- "birth_certificate" matches no real field on mc.applicant,
so Odoo's generic form controller stores it as a plain
ir.attachment linked to the new record - exactly the
"file uploads to ir.attachment" the spec asks for,
with no dedicated Binary field needed on the model. -->
<input class="form-control s_website_form_input" type="file"
name="birth_certificate" id="admission_birth_certificate"/>
</div>
</div>
</div>
<div class="mb-0 py-2 col-12 s_website_form_submit text-end s_website_form_no_submit_label" data-name="Submit Button">
<div class="s_website_form_label"/>
<span id="s_website_form_result"/>
<a href="#" role="button" class="btn btn-primary s_website_form_send">Submit Application</a>
</div>
</div>
</form>
</div>
</section>
</div>
</div>
<div class="mc-adm-steps">
<h2>What happens after you submit</h2>
<div class="mc-steps-row">
<div class="mc-step"><div class="n">1</div><div class="t">Applied</div></div>
<div class="mc-step"><div class="n">2</div><div class="t">Document Verification</div></div>
<div class="mc-step"><div class="n">3</div><div class="t">Interview</div></div>
<div class="mc-step"><div class="n">4</div><div class="t">Offered</div></div>
<div class="mc-step"><div class="n">5</div><div class="t">Enrolled</div></div>
</div>
</div>
</div>
</t>
</template>
<record id="website_page_admission_apply" model="website.page">
<field name="url">/admissions/apply</field>
<field name="view_id" ref="admission_apply_page"/>
<field name="is_published">True</field>
</record>
</odoo>