diff --git a/.github/workflows/site-validation.yml b/.github/workflows/site-validation.yml index 505bc52..e189c83 100644 --- a/.github/workflows/site-validation.yml +++ b/.github/workflows/site-validation.yml @@ -66,6 +66,7 @@ jobs: run: | python scripts/check_site_navigation.py _site --site-url "$SITE_URL" python scripts/check_site_links.py _site --site-url "$SITE_URL" + python scripts/check_site_branding.py _site - uses: actions/upload-artifact@v4 with: name: site-navigation-preview-${{ matrix.name }} diff --git a/FMA/index.md b/FMA/index.md index d6d3e3b..a684aef 100644 --- a/FMA/index.md +++ b/FMA/index.md @@ -17,7 +17,7 @@ alternate_zh: /zh-cn/FMA/ 简中 - Fuzzy Technologies + {% include brand-logo.html %} @@ -158,8 +158,4 @@ FMA is built around engineering mathematics and explicit data-processing rules: > **FMA: mathematics, engineering and real markets — without magic promises.** - +{% include site-footer.html %} diff --git a/FMA/instruments.md b/FMA/instruments.md index 48e76b5..c386262 100644 --- a/FMA/instruments.md +++ b/FMA/instruments.md @@ -17,7 +17,7 @@ alternate_zh: /zh-cn/FMA/instruments.html 简中 - Fuzzy Technologies + {% include brand-logo.html %} @@ -94,8 +94,4 @@ MDMG, LIFE, GECO, OZPH, ABIO, PRMD, GEMC. | PLDRUB_TOM | rub | Palladium | - +{% include site-footer.html %} diff --git a/FuzzyRoutines/index.md b/FuzzyRoutines/index.md index a6b23fe..58368f5 100644 --- a/FuzzyRoutines/index.md +++ b/FuzzyRoutines/index.md @@ -19,7 +19,7 @@ alternate_zh: /zh-cn/FuzzyRoutines/ 简中 - Fuzzy Technologies + {% include brand-logo.html %} @@ -100,8 +100,4 @@ The project deliberately keeps its numerical objects readable: functions expose - [Tests](https://github.com/Fuzzy-Technologies/FuzzyRoutines/tree/master/tests) - [Apache-2.0 License](https://github.com/Fuzzy-Technologies/FuzzyRoutines/blob/master/LICENSE) - +{% include site-footer.html %} diff --git a/TKSBrokerAPI/en/index.md b/TKSBrokerAPI/en/index.md index aeb0c4a..9ef2066 100644 --- a/TKSBrokerAPI/en/index.md +++ b/TKSBrokerAPI/en/index.md @@ -15,7 +15,7 @@ alternate_ru: /TKSBrokerAPI/ RU - Fuzzy Technologies + {% include brand-logo.html %} @@ -157,8 +157,4 @@ if __name__ == "__main__": More examples are available in the [project README](https://github.com/Fuzzy-Technologies/TKSBrokerAPI). - +{% include site-footer.html %} diff --git a/TKSBrokerAPI/index.md b/TKSBrokerAPI/index.md index 8624f14..e73b03c 100644 --- a/TKSBrokerAPI/index.md +++ b/TKSBrokerAPI/index.md @@ -15,7 +15,7 @@ description: Платформа ⚙️ TKSBrokerAPI для упрощённой RU - Fuzzy Technologies + {% include brand-logo.html %} @@ -293,8 +293,4 @@ if __name__ == "__main__":
- +{% include site-footer.html %} diff --git a/_includes/brand-logo.html b/_includes/brand-logo.html new file mode 100644 index 0000000..25a0a13 --- /dev/null +++ b/_includes/brand-logo.html @@ -0,0 +1 @@ +Fuzzy Technologies® \ No newline at end of file diff --git a/_includes/brand-name.html b/_includes/brand-name.html new file mode 100644 index 0000000..e36df3a --- /dev/null +++ b/_includes/brand-name.html @@ -0,0 +1 @@ +Fuzzy Technologies® \ No newline at end of file diff --git a/_includes/site-footer.html b/_includes/site-footer.html new file mode 100644 index 0000000..c20a3d7 --- /dev/null +++ b/_includes/site-footer.html @@ -0,0 +1,17 @@ +{% assign brand_lang = page.lang | default: "en" %} +{% case brand_lang %} + {% when "ru" %} + {% assign certificate_hint = "Товарный знак № 1276641 — свидетельство Роспатента" %} + {% assign brand_motto = "Технологии · Знания · Наука" %} + {% when "zh-CN" %} + {% assign certificate_hint = "第 1276641 号商标 — 注册证书(俄罗斯 FIPS/Rospatent 网站)" %} + {% assign brand_motto = "技术 · 知识 · 科学" %} + {% else %} + {% assign certificate_hint = "Trademark No. 1276641 — registration certificate (Russian FIPS/Rospatent website)" %} + {% assign brand_motto = "Technologies · Knowledge · Science" %} +{% endcase %} + diff --git a/_layouts/article.html b/_layouts/article.html index affc10c..c747b7a 100644 --- a/_layouts/article.html +++ b/_layouts/article.html @@ -17,7 +17,7 @@ {% endif %} @@ -141,14 +141,4 @@

{{ page.title }}

{{ articles_label }} - +{% include site-footer.html %} diff --git a/_layouts/articles.html b/_layouts/articles.html index 3e21af7..58f020d 100644 --- a/_layouts/articles.html +++ b/_layouts/articles.html @@ -29,7 +29,7 @@ {% endif %} @@ -92,14 +92,4 @@

{{ article.title }}

{% endfor %} - +{% include site-footer.html %} diff --git a/index.md b/index.md index edcea2f..3fa8f34 100644 --- a/index.md +++ b/index.md @@ -10,7 +10,7 @@ alternate_zh: /zh-cn/ --- @@ -135,8 +135,4 @@ alternate_zh: /zh-cn/ - +{% include site-footer.html %} diff --git a/ru/FMA/index.md b/ru/FMA/index.md index 3e75aff..be29dbc 100644 --- a/ru/FMA/index.md +++ b/ru/FMA/index.md @@ -17,7 +17,7 @@ description: Сервисы биржевых сигналов Fuzzy Market Analy 简中 - Fuzzy Technologies + {% include brand-logo.html %} @@ -205,8 +205,4 @@ FMA помогает принимать обоснованные решения, *[Через тернии к звёздам: история создания торгового алгоритма](https://teletype.in/@tgilmullin/trading-algorithm-history)* - +{% include site-footer.html %} diff --git a/ru/FMA/instruments.md b/ru/FMA/instruments.md index 7784d76..0b2b556 100644 --- a/ru/FMA/instruments.md +++ b/ru/FMA/instruments.md @@ -17,7 +17,7 @@ description: Сервисы биржевых сигналов Fuzzy Market Analy 简中 - Fuzzy Technologies + {% include brand-logo.html %} @@ -294,8 +294,4 @@ description: Сервисы биржевых сигналов Fuzzy Market Analy | PLTRUB_TOM | rub | Платина. | | PLDRUB_TOM | rub | Палладий. | - +{% include site-footer.html %} diff --git a/ru/FuzzyRoutines/index.md b/ru/FuzzyRoutines/index.md index 0e508b5..f257d2f 100644 --- a/ru/FuzzyRoutines/index.md +++ b/ru/FuzzyRoutines/index.md @@ -19,7 +19,7 @@ alternate_zh: /zh-cn/FuzzyRoutines/ 简中 - Fuzzy Technologies + {% include brand-logo.html %} @@ -100,8 +100,4 @@ FuzzyRoutines распространяется под лицензией Apache- - [Тесты](https://github.com/Fuzzy-Technologies/FuzzyRoutines/tree/master/tests) - [Лицензия Apache-2.0](https://github.com/Fuzzy-Technologies/FuzzyRoutines/blob/master/LICENSE) - +{% include site-footer.html %} diff --git a/ru/index.md b/ru/index.md index 858d703..4572040 100644 --- a/ru/index.md +++ b/ru/index.md @@ -10,7 +10,7 @@ alternate_zh: /zh-cn/ --- @@ -135,8 +135,4 @@ alternate_zh: /zh-cn/ - +{% include site-footer.html %} diff --git a/scripts/check_site_branding.py b/scripts/check_site_branding.py new file mode 100644 index 0000000..8b060db --- /dev/null +++ b/scripts/check_site_branding.py @@ -0,0 +1,143 @@ +"""Validate registered company branding on every rendered site page.""" + +import argparse +from html.parser import HTMLParser +from pathlib import Path + +CERTIFICATE_URL = "https://fips.ru/EGD/e3ee2ab0-72e9-4524-a4b9-7123356f06fe" +LOGO_URL = "/static/images/FuzzyTechnologies-Logo-registered.svg" +HINTS = { + "en": "Trademark No. 1276641 — registration certificate (Russian FIPS/Rospatent website)", + "ru": "Товарный знак № 1276641 — свидетельство Роспатента", + "zh-CN": "第 1276641 号商标 — 注册证书(俄罗斯 FIPS/Rospatent 网站)", +} + + +class BrandingParser(HTMLParser): + """Collect public header logos and accessible certificate links.""" + + def __init__(self): + """Initialize independent state for one HTML document.""" + + super().__init__() + self.lang = "en" + self.has_header = False + self.logos = [] + self.links = [] + self.current_link = None + self.in_brand_button = False + self.button_images = [] + + def handle_starttag(self, tag, attrs): + """Collect branding attributes without relying on source indentation.""" + + attrs = dict(attrs) + classes = attrs.get("class", "").split() + + if tag == "html": + self.lang = attrs.get("lang", "en") + + if tag == "header" and "site-header" in classes: + self.has_header = True + + if tag == "img" and "FuzzyTechnologies-Logo-" in attrs.get("src", ""): + self.logos.append(attrs) + + if tag == "a" and "brand-button" in classes: + self.in_brand_button = True + + if tag == "img" and self.in_brand_button: + self.button_images.append(attrs) + + if tag == "a" and "trademark-link" in classes: + self.current_link = {"attrs": attrs, "text": "", "superscript": False} + self.links.append(self.current_link) + + if tag == "sup" and self.current_link is not None: + self.current_link["superscript"] = "trademark-symbol" in classes + + def handle_startendtag(self, tag, attrs): + """Support both HTML and XHTML image serialization.""" + + self.handle_starttag(tag, attrs) + + def handle_endtag(self, tag): + """Stop collecting the certificate link's visible text at its boundary.""" + + if tag == "a": + self.current_link = None + self.in_brand_button = False + + def handle_data(self, data): + """Capture the full linked brand name, including its registration mark.""" + + if self.current_link is not None: + self.current_link["text"] += data + + +def ValidatePage(path): + """Return failures for branded pages; ignore legacy redirect documents.""" + + parser = BrandingParser() + parser.feed(path.read_text(encoding="utf-8")) + + if not parser.has_header: + return None + + errors = [] + + if len(parser.logos) != 1 or parser.logos[0].get("src") != LOGO_URL: + errors.append("header must use the registered logo exactly once") + + if len(parser.button_images) != 1 or parser.button_images[0].get("src") != LOGO_URL: + errors.append("brand button must wrap only its logo; check inline include whitespace") + + if len(parser.links) != 1: + errors.append("footer must contain exactly one certificate link") + + else: + link = parser.links[0] + attrs = link["attrs"] + hint = HINTS.get(parser.lang) + + if attrs.get("href") != CERTIFICATE_URL: + errors.append("certificate URL does not match the registered document") + + if not hint or attrs.get("title") != hint or attrs.get("aria-description") != hint: + errors.append("certificate tooltip and accessible description must match the page language") + + if link["text"].strip() != "Fuzzy Technologies®" or not link["superscript"]: + errors.append("full linked brand name must include a superscript registration mark") + + return errors + + +def Main(): + """Check the build directory and fail on missing or inconsistent branding.""" + + cli = argparse.ArgumentParser(description=__doc__) + cli.add_argument("site_dir", type=Path) + args = cli.parse_args() + checked = 0 + failures = [] + + for path in sorted(args.site_dir.rglob("*.html")): + errors = ValidatePage(path) + + if errors is None: + continue + + checked += 1 + failures.extend(f"{path}: {error}" for error in errors) + + if not checked: + failures.append("No branded pages found; check the build directory.") + + if failures: + raise SystemExit("\n".join(failures)) + + print(f"PASS: {checked} pages use registered branding and localized certificate links.") + + +if __name__ == "__main__": + Main() diff --git a/zh-cn/FMA/index.md b/zh-cn/FMA/index.md index 12b4cfb..82c9d2f 100644 --- a/zh-cn/FMA/index.md +++ b/zh-cn/FMA/index.md @@ -17,7 +17,7 @@ alternate_zh: /zh-cn/FMA/ 简中 - Fuzzy Technologies + {% include brand-logo.html %} @@ -158,8 +158,4 @@ FMA 建立在工程数学和明确的数据处理规则之上: > **FMA:数学、工程与真实市场——不靠“神奇承诺”。** - +{% include site-footer.html %} diff --git a/zh-cn/FMA/instruments.md b/zh-cn/FMA/instruments.md index e0319af..d8cea79 100644 --- a/zh-cn/FMA/instruments.md +++ b/zh-cn/FMA/instruments.md @@ -17,7 +17,7 @@ alternate_zh: /zh-cn/FMA/instruments.html 简中 - Fuzzy Technologies + {% include brand-logo.html %} @@ -93,8 +93,4 @@ MDMG, LIFE, GECO, OZPH, ABIO, PRMD, GEMC. | PLTRUB_TOM | rub | 铂金 | | PLDRUB_TOM | rub | 钯金 | - +{% include site-footer.html %} diff --git a/zh-cn/FuzzyRoutines/index.md b/zh-cn/FuzzyRoutines/index.md index 5186d6f..20c545a 100644 --- a/zh-cn/FuzzyRoutines/index.md +++ b/zh-cn/FuzzyRoutines/index.md @@ -19,7 +19,7 @@ alternate_zh: /zh-cn/FuzzyRoutines/ 简中 - Fuzzy Technologies + {% include brand-logo.html %} @@ -100,8 +100,4 @@ FuzzyRoutines 采用 Apache-2.0 许可证。PyPI 上的稳定版本为 `2.0.0` - [测试](https://github.com/Fuzzy-Technologies/FuzzyRoutines/tree/master/tests) - [Apache-2.0 许可证](https://github.com/Fuzzy-Technologies/FuzzyRoutines/blob/master/LICENSE) - +{% include site-footer.html %} diff --git a/zh-cn/index.md b/zh-cn/index.md index e315cb2..b4eedc7 100644 --- a/zh-cn/index.md +++ b/zh-cn/index.md @@ -10,7 +10,7 @@ alternate_zh: /zh-cn/ --- @@ -135,8 +135,4 @@ alternate_zh: /zh-cn/ - +{% include site-footer.html %}