From 9f0a216c5e4639f1bd0d3deffd5e26f51d9cf065 Mon Sep 17 00:00:00 2001 From: zwitschi Date: Sat, 2 May 2026 13:05:43 +0200 Subject: [PATCH] Add new templates and tests for improved functionality - Created index.html template for the homepage with service cards and partner logos. - Added page_from_md.html template for rendering pages from markdown. - Developed services.html template detailing various services offered. - Implemented tests for link handling in markdown, ensuring external links open in new tabs and internal links function correctly. - Enhanced markdown parser tests to validate heading extraction, content rendering, and link safety. - Introduced utility tests for template rendering, HTML minification, and JavaScript minification. Co-authored-by: Copilot --- .gitignore | 6 + docs/de/AGB-allucanget.biz.md | 105 ++++ docs/de/AGB.md | 89 +++ docs/en/coaching.md | 234 ++++++++ docs/en/imprint.md | 81 +++ docs/en/index.md | 122 ++++ docs/en/services.md | 55 ++ docs/en/terms_and_conditions.md | 56 ++ docs/en/webservices.md | 94 +++ docs/pages.json | 92 +++ html/.htaccess | 83 +++ html/coaching.html | 1 + html/css/styles.css | 565 ++++++++++++++++++ html/favicon.ico | Bin 0 -> 374 bytes html/google8753946d6666aa9a.html | 1 + html/img/Logo-AllYouCanGet-204x100.png | Bin 0 -> 7132 bytes html/img/barchart.svg | 13 + html/img/cloud.svg | 10 + html/img/coaching-career.svg | 3 + html/img/coaching-leadership.svg | 3 + html/img/coaching-personal.svg | 3 + html/img/coaching-technical.svg | 3 + ...ed-Logo-AllYouCanGet-512x512-1-180x180.png | Bin 0 -> 5957 bytes ...ed-Logo-AllYouCanGet-512x512-1-192x192.png | Bin 0 -> 6179 bytes ...pped-Logo-AllYouCanGet-512x512-1-32x32.png | Bin 0 -> 374 bytes html/img/imprint-shield.svg | 3 + html/img/infrastructure.svg | 14 + html/img/it-consulting.svg | 11 + html/img/management-consulting.svg | 12 + html/img/network.svg | 13 + html/img/partner/aws.svg | 15 + html/img/partner/google.svg | 2 + html/img/partner/ibm.svg | 4 + html/img/partner/informatica.svg | 40 ++ html/img/partner/microsoft.svg | 8 + html/img/partner/oracle.svg | 1 + html/img/partner/salesforce.svg | 25 + html/img/partner/sap.svg | 8 + html/img/people.svg | 14 + html/img/security.svg | 10 + ...utomotive-lighting-infrastructure-blue.jpg | Bin 0 -> 12958 bytes html/img/system-integration.svg | 9 + html/img/test_image.png | 1 + html/imprint.html | 1 + html/index.html | 1 + html/js/contact.js | 1 + html/js/humansnotbots.js | 1 + html/js/nav.js | 1 + html/js/partner.js | 1 + html/php.ini | 20 + html/robots.txt | 2 + html/services.html | 1 + html/signature.html | 122 ++++ html/terms_and_conditions.html | 1 + html/webservices.html | 1 + lib/__init__.py | 0 lib/markdown_parser.py | 398 ++++++++++++ lib/types.py | 54 ++ lib/utils.py | 126 ++++ main.py | 189 ++++++ requirements.txt | 10 + templates/_base.html | 17 + templates/_footer.html | 9 + templates/_head.html | 35 ++ templates/_header.html | 56 ++ templates/_nav.html | 22 + templates/coaching.html | 498 +++++++++++++++ templates/components/card.html | 17 + templates/components/hero.html | 8 + templates/components/section.html | 13 + templates/imprint.html | 238 ++++++++ templates/index.html | 204 +++++++ templates/page_from_md.html | 20 + templates/services.html | 208 +++++++ tests/test_links.py | 16 + tests/test_markdown_helpers.py | 133 +++++ tests/test_markdown_parser.py | 351 +++++++++++ tests/test_unsafe_links.py | 15 + tests/test_utils.py | 101 ++++ 79 files changed, 4700 insertions(+) create mode 100644 docs/de/AGB-allucanget.biz.md create mode 100644 docs/de/AGB.md create mode 100644 docs/en/coaching.md create mode 100644 docs/en/imprint.md create mode 100644 docs/en/index.md create mode 100644 docs/en/services.md create mode 100644 docs/en/terms_and_conditions.md create mode 100644 docs/en/webservices.md create mode 100644 docs/pages.json create mode 100644 html/.htaccess create mode 100644 html/coaching.html create mode 100644 html/css/styles.css create mode 100644 html/favicon.ico create mode 100644 html/google8753946d6666aa9a.html create mode 100644 html/img/Logo-AllYouCanGet-204x100.png create mode 100644 html/img/barchart.svg create mode 100644 html/img/cloud.svg create mode 100644 html/img/coaching-career.svg create mode 100644 html/img/coaching-leadership.svg create mode 100644 html/img/coaching-personal.svg create mode 100644 html/img/coaching-technical.svg create mode 100644 html/img/cropped-Logo-AllYouCanGet-512x512-1-180x180.png create mode 100644 html/img/cropped-Logo-AllYouCanGet-512x512-1-192x192.png create mode 100644 html/img/cropped-Logo-AllYouCanGet-512x512-1-32x32.png create mode 100644 html/img/imprint-shield.svg create mode 100644 html/img/infrastructure.svg create mode 100644 html/img/it-consulting.svg create mode 100644 html/img/management-consulting.svg create mode 100644 html/img/network.svg create mode 100644 html/img/partner/aws.svg create mode 100644 html/img/partner/google.svg create mode 100644 html/img/partner/ibm.svg create mode 100644 html/img/partner/informatica.svg create mode 100644 html/img/partner/microsoft.svg create mode 100644 html/img/partner/oracle.svg create mode 100644 html/img/partner/salesforce.svg create mode 100644 html/img/partner/sap.svg create mode 100644 html/img/people.svg create mode 100644 html/img/security.svg create mode 100644 html/img/sky-street-light-automotive-lighting-infrastructure-blue.jpg create mode 100644 html/img/system-integration.svg create mode 100644 html/img/test_image.png create mode 100644 html/imprint.html create mode 100644 html/index.html create mode 100644 html/js/contact.js create mode 100644 html/js/humansnotbots.js create mode 100644 html/js/nav.js create mode 100644 html/js/partner.js create mode 100644 html/php.ini create mode 100644 html/robots.txt create mode 100644 html/services.html create mode 100644 html/signature.html create mode 100644 html/terms_and_conditions.html create mode 100644 html/webservices.html create mode 100644 lib/__init__.py create mode 100644 lib/markdown_parser.py create mode 100644 lib/types.py create mode 100644 lib/utils.py create mode 100644 main.py create mode 100644 requirements.txt create mode 100644 templates/_base.html create mode 100644 templates/_footer.html create mode 100644 templates/_head.html create mode 100644 templates/_header.html create mode 100644 templates/_nav.html create mode 100644 templates/coaching.html create mode 100644 templates/components/card.html create mode 100644 templates/components/hero.html create mode 100644 templates/components/section.html create mode 100644 templates/imprint.html create mode 100644 templates/index.html create mode 100644 templates/page_from_md.html create mode 100644 templates/services.html create mode 100644 tests/test_links.py create mode 100644 tests/test_markdown_helpers.py create mode 100644 tests/test_markdown_parser.py create mode 100644 tests/test_unsafe_links.py create mode 100644 tests/test_utils.py diff --git a/.gitignore b/.gitignore index 30fb372..3352ca3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ # vscode settings .vscode/ +v5.code-workspace # python virtual environment .venv/ @@ -10,3 +11,8 @@ # python cache __pycache__/ *.pyc + +#pytest cache +.pytest_cache/ +.mypy_cache/ +.coverage diff --git a/docs/de/AGB-allucanget.biz.md b/docs/de/AGB-allucanget.biz.md new file mode 100644 index 0000000..74a4128 --- /dev/null +++ b/docs/de/AGB-allucanget.biz.md @@ -0,0 +1,105 @@ +# Nutzungsbedingungen – AllYouCanGET + +## Impressum + +gemäß §5 Telemediengesetz bzw. § 5 Abs. 1 E-Commerce-Gesetz bzw. Art. 3 des Bundesgesetzes gegen den unlauteren Wettbewerb (UWG) + +```text +All You Can GET +c/o Sinn Consulting +Im Dornäcker 16 +CH-8967 Widen +georg AT allucanget DOT biz +``` + +_Aktualisiert am: 22.10.2025_ + +Willkommen auf der Website von **AllYouCanGET – Sinn Consulting**, mit Sitz in Widen, Schweiz. +Durch den Zugriff auf diese Website erklärst du dich mit den folgenden Nutzungsbedingungen einverstanden. +Wenn du mit diesen Bedingungen nicht einverstanden bist, wird gebeten, die Website nicht zu nutzen. + +--- + +## 1. Geltungsbereich + +Diese Nutzungsbedingungen regeln den Zugriff auf und die Nutzung der von **AllYouCanGET – Sinn Consulting** (nachfolgend „AllYouCanGET“ oder „das Unternehmen“) betriebenen Website. +Sie gelten für alle Besucherinnen und Besucher, Nutzerinnen und Nutzer dieser Website. + +Für die Inanspruchnahme von Dienstleistungen des Unternehmens gelten die separaten **Allgemeinen Geschäftsbedingungen (AGB)**. + +--- + +## 2. Zweck der Website + +Die Website dient der Information über die Dienstleistungen, Philosophie und Angebote von AllYouCanGET in den Bereichen +**Business Consulting, Creative Development, Digital Marketing, Coaching und Technische Services**. + +AllYouCanGET behält sich das Recht vor, Inhalte der Website jederzeit ohne Vorankündigung zu ändern, zu ergänzen oder zu entfernen. + +--- + +## 3. Urheberrecht und geistiges Eigentum + +Alle Inhalte dieser Website – insbesondere Texte, Grafiken, Logos, Bilder, Design-Elemente und herunterladbare Materialien – sind urheberrechtlich und durch andere Schutzrechte geschützt. +Sofern nicht anders angegeben, liegen alle Rechte bei **AllYouCanGET – Sinn Consulting**. + +Das Anzeigen, Herunterladen oder Ausdrucken von Inhalten ist nur für den **privaten, nicht kommerziellen Gebrauch** erlaubt, sofern alle Urheberrechts- und Eigentumshinweise bestehen bleiben. +Eine Vervielfältigung, Verbreitung, Bearbeitung oder Veröffentlichung von Inhalten zu kommerziellen Zwecken ist ohne ausdrückliche schriftliche Zustimmung von AllYouCanGET untersagt. + +--- + +## 4. Nutzung der Website + +Die Website darf nur zu rechtmässigen Zwecken und in einer Weise genutzt werden, die den Betrieb oder die Sicherheit der Website nicht beeinträchtigt. + +Automatisierte Zugriffe (z. B. durch Bots oder Crawler) sind nur im Rahmen der in der Datei `robots.txt` festgelegten Regeln erlaubt. +AllYouCanGET behält sich das Recht vor, den Zugriff auf die Website jederzeit einzuschränken oder zu sperren. + +--- + +## 5. Externe Links und Inhalte Dritter + +Diese Website kann Links zu externen Websites Dritter enthalten. +AllYouCanGET hat keinen Einfluss auf deren Inhalte und übernimmt keinerlei Verantwortung oder Haftung für deren Richtigkeit, Rechtmässigkeit oder Verfügbarkeit. +Der Zugriff auf solche externen Seiten erfolgt auf eigenes Risiko der Nutzerin oder des Nutzers. + +--- + +## 6. Vertraulichkeit und Datenschutz + +AllYouCanGET behandelt personenbezogene Daten, die über diese Website übermittelt werden (z. B. über Kontaktformulare oder Newsletter-Anmeldungen), **vertraulich** und gemäss den geltenden Schweizer Datenschutzbestimmungen. +Die Daten werden ausschliesslich für den angegebenen Zweck verwendet. +Weitere Informationen finden sich in der separaten **Datenschutzerklärung**. + +Nicht personenbezogene Informationen oder Materialien, die über die Website übermittelt werden (z. B. Feedback, Ideen, Konzepte), gelten als **nicht vertraulich**. +AllYouCanGET darf solche Informationen frei verwenden, sofern nichts anderes schriftlich vereinbart wurde. + +--- + +## 7. Haftungsausschluss + +AllYouCanGET ist bemüht, die Inhalte dieser Website sorgfältig und aktuell zu halten. +Dennoch wird keine Gewähr für die Richtigkeit, Vollständigkeit oder Verfügbarkeit der bereitgestellten Informationen übernommen. + +AllYouCanGET haftet ausschliesslich für Schäden, die durch **vorsätzliches oder grobfahrlässiges Verhalten** verursacht werden. +Eine Haftung für **indirekte oder Folgeschäden**, insbesondere Datenverluste oder entgangenen Gewinn, ist ausgeschlossen. + +--- + +## 8. Änderungen der Website und Nutzungsbedingungen + +AllYouCanGET kann die Inhalte der Website sowie diese Nutzungsbedingungen jederzeit und ohne Vorankündigung ändern oder aktualisieren. +Es gilt stets die zum Zeitpunkt des Zugriffs auf der Website veröffentlichte Fassung. + +--- + +## 9. Anwendbares Recht und Gerichtsstand + +Diese Nutzungsbedingungen unterstehen ausschliesslich dem **schweizerischen Recht**. +Gerichtsstand für alle Streitigkeiten im Zusammenhang mit der Nutzung dieser Website ist **Widen, Schweiz**. + +--- + +**AllYouCanGET – Sinn Consulting** +Widen, Schweiz +[https://www.allyoucanget.ch](https://www.allyoucanget.biz) diff --git a/docs/de/AGB.md b/docs/de/AGB.md new file mode 100644 index 0000000..ba4f4fe --- /dev/null +++ b/docs/de/AGB.md @@ -0,0 +1,89 @@ +# **Allgemeine Geschäftsbedingungen (AGB)** + +**AllYouCanGET – Sinn Consulting** +Widen, Schweiz + +--- + +## **1. Geltungsbereich** + +Diese Allgemeinen Geschäftsbedingungen (AGB) gelten für sämtliche Dienstleistungen der **AllYouCanGET – Sinn Consulting** (nachfolgend „das Unternehmen“). +Sie finden Anwendung auf Verträge mit **Privatpersonen (B2C)** sowie **Unternehmen (B2B)**. Mit der Beauftragung des Unternehmens anerkennt der Kunde diese AGB in vollem Umfang. + +--- + +## **2. Leistungen** + +Das Unternehmen erbringt Dienstleistungen in den Bereichen **Business Consulting, Creative Development, Digital Marketing, Coaching und Technische Services**. +Art, Umfang und Ziel der jeweiligen Dienstleistung werden individuell schriftlich oder elektronisch vereinbart. + +--- + +## **3. Angebote und Vertragsabschluss** + +Alle Angebote des Unternehmens sind **unverbindlich**, bis sie schriftlich bestätigt werden. +Änderungen, Ergänzungen oder Zusatzleistungen bedürfen ebenfalls der **schriftlichen Bestätigung** durch das Unternehmen. + +--- + +## **4. Preise und Zahlungsbedingungen** + +Sofern nicht anders vereinbart, verstehen sich alle Preise in **Schweizer Franken (CHF)**, exklusive Mehrwertsteuer (sofern anwendbar). +Die Rechnungsstellung erfolgt **nach Abschluss der Dienstleistung**. +Rechnungen sind innerhalb von **30 Tagen** ab Rechnungsdatum ohne Abzug zu bezahlen. +Bei Zahlungsverzug behält sich das Unternehmen das Recht vor, **Mahngebühren** und **Verzugszinsen** zu erheben. + +--- + +## **5. Annullierung und Rückerstattung** + +Annullierungen durch den Kunden müssen **schriftlich** erfolgen. + +- **Bis 7 Tage vor Leistungserbringung:** Rückerstattung von 50 % des vereinbarten Honorars. +- **Weniger als 7 Tage vor Leistungserbringung:** keine Rückerstattung möglich. + +Das Unternehmen behält sich vor, Dienstleistungen aus wichtigen Gründen (z. B. Krankheit, unvorhergesehene Ereignisse) zu verschieben oder abzusagen. In diesem Fall wird ein neuer Termin vereinbart oder bereits geleistete Zahlungen werden zurückerstattet. + +--- + +## **6. Haftung** + +Das Unternehmen haftet ausschliesslich für **vorsätzliches oder grobfahrlässiges Verhalten**. +Eine Haftung für leichte Fahrlässigkeit, **indirekte Schäden**, **Folgeschäden**, **entgangenen Gewinn** oder **Datenverlust** ist ausdrücklich ausgeschlossen. +Der Kunde bleibt für die Umsetzung und die Entscheidungen, die aus der Beratung resultieren, selbst verantwortlich. + +--- + +## **7. Urheberrecht und Nutzungsrechte** + +Alle vom Unternehmen erstellten Unterlagen, Konzepte, Designs und Materialien bleiben bis zur **vollständigen Bezahlung** Eigentum des Unternehmens. +Sofern nicht anders vereinbart, verbleiben sämtliche **Urheber- und Nutzungsrechte** beim Unternehmen. +Eine Weitergabe, Vervielfältigung oder Nutzung der Materialien zu anderen Zwecken ist ohne vorherige schriftliche Zustimmung nicht gestattet. + +--- + +## **8. Vertraulichkeit** + +Das Unternehmen verpflichtet sich, alle im Rahmen der Zusammenarbeit erhaltenen Informationen und Daten des Kunden **vertraulich** zu behandeln. +Diese Pflicht besteht auch nach Beendigung der Geschäftsbeziehung fort. +Der Kunde verpflichtet sich ebenso, vertrauliche Informationen über das Unternehmen nicht an Dritte weiterzugeben. + +--- + +## **9. Datenschutz** + +Personenbezogene Daten werden ausschliesslich zur **Erbringung und Verwaltung der Dienstleistungen** verwendet. +Eine Weitergabe an Dritte erfolgt nur mit ausdrücklicher Zustimmung des Kunden oder wenn eine gesetzliche Verpflichtung besteht. + +--- + +## **10. Anwendbares Recht und Gerichtsstand** + +Diese AGB sowie alle Vertragsbeziehungen zwischen dem Unternehmen und dem Kunden unterstehen ausschliesslich dem **schweizerischen Recht**. +Gerichtsstand ist **Widen, Schweiz**. + +--- + +**AllYouCanGET – Sinn Consulting** +Widen, Schweiz +_(Gültig ab 01.01.2025)_ diff --git a/docs/en/coaching.md b/docs/en/coaching.md new file mode 100644 index 0000000..6c1c2cc --- /dev/null +++ b/docs/en/coaching.md @@ -0,0 +1,234 @@ +# Our Coaching Areas + +Comprehensive coaching programs tailored to your professional and personal growth + +## ![Career Advice](img/coaching-career.svg) Career Advice + +Navigate your career path with expert guidance tailored to your industry and location. + +### IT Career + +- Career paths in software development +- Technical vs management track +- Transitioning between specialties + +### Switzerland Specifics + +- Work culture and expectations +- Salary negotiations +- Work permits and regulations + +### Germany Specifics + +- Job market trends +- Industry hubs and opportunities + +### Remote Work + +- Building a remote work routine +- Communication strategies + +### Freelancing + +- Setting up as independent contractor +- Client acquisition and retention +- Pricing strategies and negotiation + +### Startups + +- Evaluating startup opportunities +- Equity considerations +- Growth expectations + +### Corporates + +- Navigating corporate structures +- Internal mobility +- Building influence + +### Consulting + +- Types of consulting roles +- Client management +- Balancing multiple projects + +## ![Personal Development](img/coaching-personal.svg) Personal Development + +Discover your values, strengths, and purpose to create a fulfilling life and career. + +### Values + +- Value identification exercises +- Aligning career with personal values +- Identity Workshop + +### Ikigai + +- Finding purpose through passion +- Mission, vocation, and profession +- Practical discovery exercises + +### Strengths + +- Assessment tools and frameworks +- Leveraging strengths professionally + +### Weaknesses + +- Growth mindset approaches +- Strategic improvement planning + +### Vision + +- Creating personal vision statements +- Long-term life planning +- Visualization techniques + +### Goals + +- SMART goal framework +- Balancing short and long-term objectives + +### Agile Self Organization + +- Personal Kanban implementation +- Quarterly planning and review cycles +- Agile methods for personal productivity + +## ![Leadership Coaching](img/coaching-leadership.svg) Leadership Coaching + +Develop essential leadership skills to inspire teams and drive organizational success. + +### Leadership Styles + +- Management vs Leadership +- Situational leadership approaches +- Transformational vs transactional + +### Communication + +- Active listening techniques +- Delivering difficult messages +- Cross-cultural communication + +### Feedback + +- Structured feedback frameworks +- Creating feedback culture +- Growth-oriented approaches + +### Delegation + +- RACI and DACI models +- Identifying tasks to delegate +- Building team capacity + +### Decision Making + +- OODA loop, DECIDE model +- Data-driven decisions +- Managing decision paralysis + +### Conflict Management + +- Conflict resolution models +- Mediation techniques +- Transforming conflict to opportunity + +### Motivation + +- Intrinsic vs extrinsic motivation +- SCARF and Drive frameworks +- Tailoring to individual needs + +### Empathy & Compassion + +- Building empathetic leadership +- Compassion without compromising + +### Trust + +- Trust-building exercises +- Trust in remote teams +- Rebuilding broken trust + +### Psychological Safety + +- Assessment techniques +- Creating safety in teams +- Connection to innovation + +### Team Building + +- Tuckman's team formation +- Remote team activities +- Cross-functional dynamics + +### Agile Project Management + +- Project roles and responsibilities +- Agile hierarchies +- Balancing agility with governance + +## ![Technical Coaching](img/coaching-technical.svg) Technical Coaching + +Master technical skills and best practices in software development and IT infrastructure. + +### Software and Web Development + +#### Scalability + +- Horizontal vs vertical scaling +- Distributed systems principles +- Load balancing strategies + +#### Browser + +- Developer tools mastery +- Performance optimization +- Extension development + +### Operating Systems + +#### Linux + +- Debian, Ubuntu +- RedHat, Fedora, CentOS +- Arch, Manjaro +- SUSE, openSUSE + +#### Windows + +- Windows 10 & 11 +- Windows Server 2019 +- Windows Server 2022 + +#### macOS + +- macOS basics +- System administration +- Shell commands + +### Development Tools + +#### Git + +- Advanced git workflows +- Git hooks and automation +- Collaboration strategies + +#### Docker + +- Container optimization +- Docker Compose +- Kubernetes orchestration + +#### Visual Studio Code + +- Extension ecosystem +- Customization and settings +- Productivity optimization + +## Ready to Accelerate Your Growth? + +Let's work together to unlock your full potential diff --git a/docs/en/imprint.md b/docs/en/imprint.md new file mode 100644 index 0000000..c8ac00b --- /dev/null +++ b/docs/en/imprint.md @@ -0,0 +1,81 @@ +# Terms of Use – AllYouCanGET + +## ![IT Consulting](img/it-consulting.svg) Impressum + +gemäß §5 Telemediengesetz bzw. § 5 Abs. 1 E-Commerce-Gesetz bzw. Art. 3 des Bundesgesetzes gegen den unlauteren Wettbewerb (UWG): + +All You Can GET, c/o Sinn Consulting, Im Dornäcker 16, CH-8967 Widen - georg AT allucanget DOT biz + +## Welcome + +Welcome to the website of **AllYouCanGET – Sinn Consulting**, based in Widen, Switzerland. +By accessing or using this website, you agree to be bound by the following Terms of Use. +If you do not agree, please refrain from using this site. + +## 1. Scope of Application + +These Terms of Use govern the access to and use of the website operated by **AllYouCanGET – Sinn Consulting** (hereinafter “AllYouCanGET” or “the Company”). +They apply to all visitors, users, and other persons who access the website. + +Separate agreements apply to consulting and service contracts concluded with clients. For such services, the **General Terms and Conditions (GTC)** of AllYouCanGET apply. + +## 2. Purpose of the Website + +This website serves to provide information about the Company’s services, philosophy, and current offerings in the areas of **business consulting, creative development, digital marketing, coaching, and technical services**. + +AllYouCanGET reserves the right to update, modify, or remove website content at any time without prior notice. + +## 3. Intellectual Property and Copyright + +All content on this website — including text, graphics, logos, images, design elements, and downloadable materials — is protected by copyright and other intellectual property rights. +Unless otherwise stated, all rights belong to **AllYouCanGET – Sinn Consulting**. + +You may view, download, and print website content **for personal and non-commercial use only**, provided that all copyright and proprietary notices remain intact. +Any reproduction, distribution, modification, or publication of website content for commercial purposes is prohibited without prior written permission from AllYouCanGET. + +## 4. Use of the Website + +Users agree to use this website responsibly and only for lawful purposes. +Any use that could impair or disrupt the functionality of the website or violate applicable laws is prohibited. + +Automated access (e.g., by bots or crawlers) is only permitted within the limits defined by the site’s `robots.txt` file. +AllYouCanGET reserves the right to restrict or block access to the website at its sole discretion. + +## 5. External Links and Third-Party Content + +This website may contain links to external websites operated by third parties. +AllYouCanGET has no control over the content of these websites and assumes no responsibility or liability for their accuracy, legality, or reliability. +Access to linked websites is at the user’s own risk. + +## 6. Confidentiality and Data Protection + +AllYouCanGET treats personal data provided via this website (e.g., through contact forms or newsletter subscriptions) as **strictly confidential**. +Data is processed solely for the stated purpose and in accordance with applicable Swiss data protection regulations. +Please refer to our **Privacy Policy** for detailed information on how personal data is handled. + +Any non-personal information or material sent to AllYouCanGET via the website (e.g., feedback, ideas, or concepts) is deemed non-confidential, and AllYouCanGET may use such information freely, unless otherwise agreed in writing. + +## 7. Disclaimer + +AllYouCanGET strives to ensure that the information on this website is accurate and up to date. +However, no guarantee is given for the completeness, correctness, or availability of the information provided. + +AllYouCanGET assumes no liability for any damages arising from the use of or inability to use the website or its contents, unless caused by gross negligence or intent. +Liability for indirect or consequential damages, including data loss or lost profits, is excluded. + +## 8. Changes to the Website and Terms + +AllYouCanGET may change, suspend, or discontinue any part of this website, or update these Terms of Use at any time without prior notice. +The current version published on the website applies. + +## 9. Applicable Law and Jurisdiction + +These Terms of Use are governed exclusively by **Swiss law**. + +The exclusive place of jurisdiction is **Widen, Switzerland**. + +--- + +## Last Update + +Effective from 01.01.2025 diff --git a/docs/en/index.md b/docs/en/index.md new file mode 100644 index 0000000..df50b27 --- /dev/null +++ b/docs/en/index.md @@ -0,0 +1,122 @@ +# Streamline operations, optimize performance, and drive innovation with AllYouCanGET + +Streamline operations, optimize performance, and drive innovation with AllYouCanGET + +## ![IT Consulting](img/it-consulting.svg) IT Consulting + +Strategic technology solutions to drive your digital transformation + +### ✓ IT Strategy & Planning + +A strategic view on business goals helps to align technology roadmaps accordingly + +We develop comprehensive IT strategies that align with your business goals and drive digital transformation + +### ✓ Technology Assessment + +We evaluate existing IT infrastructure and identify improvement areas + +A thorough assessment of your current technology landscape to identify strengths, weaknesses, and opportunities for improvement + +### ✓ Enterprise Architecture + +We design and implement scalable IT architectures that align with business objectives + +Our enterprise architecture services help you design and implement scalable IT architectures that align with your business objectives + +### ✓ Cybersecurity + +Protect your digital assets! We guide you through comprehensive security strategies + +Our cybersecurity services help you protect your digital assets and ensure compliance with industry standards + +## ![Management Consulting](img/management-consulting.svg) Management Consulting + +Optimize operations and accelerate business growth + +### ✓ Strategy Development + +We develop comprehensive strategies to drive business success + +Tailored strategies that align with your business goals and drive growth + +### ✓ Process Optimization + +We improve efficiency by streamlining operations and reducing costs + +Our process optimization services help you identify bottlenecks, eliminate waste, and enhance productivity + +### ✓ Change Management + +We implement effective change management strategies to ensure smooth transitions + +Our change management services help you navigate organizational change and ensure successful adoption of new initiatives + +### ✓ Financial Analysis + +We provide data-driven insights for informed financial decision-making + +Our financial analysis services help you make informed decisions based on data-driven insights + +## ![Infrastructure Services](img/infrastructure.svg) Infrastructure Services + +Build robust foundations for your business success + +### ✓ Cloud Solutions + +Build scalable and flexible cloud infrastructures tailored to your needs + +We design and implement scalable and flexible cloud infrastructures that meet your business needs + +### ✓ Network Design + +We design and implement robust network architectures to ensure seamless connectivity + +Our network design services help you create robust network architectures that ensure seamless connectivity and optimal performance + +### ✓ System Integration + +We integrate diverse systems and applications to streamline processes and enhance data flow + +Our system integration services help you connect disparate systems and applications to streamline processes and enhance data flow + +### ✓ Security Implementation + +We implement comprehensive security measures to protect your infrastructure + +Our security implementation services help you safeguard your infrastructure against cyber threats and ensure compliance with industry standards + +## Our Partners + +We collaborate with leading technology providers to deliver the best solutions for your business. + +### + +![IBM](img/partner/ibm.svg) +![Microsoft](img/partner/microsoft.svg) +![AWS](img/partner/aws.svg) +![Google](img/partner/google.svg) +![Oracle](img/partner/oracle.svg) +![Informatica](img/partner/informatica.svg) +![SAP](img/partner/sap.svg) +![Salesforce](img/partner/salesforce.svg) + +## Projects + +### Open Mic Odyssey + +Website for a documentary film project about three best friends embarking on an outrageous adventure. + +[Open Mic Odyssey](https://openmicodyssey.com) + +### Bobby Ludlam + +A landing page for a stand-up comedian, artist and entrepeneur. + +[Bobby Ludlam](https://bobbyludlam.com) + +### William Montgomery + +Wordpress-based website for a stand-up comedian. + +[William Montgomery](https://williamfmontgomery.com) \ No newline at end of file diff --git a/docs/en/services.md b/docs/en/services.md new file mode 100644 index 0000000..3a23ae7 --- /dev/null +++ b/docs/en/services.md @@ -0,0 +1,55 @@ +# Our Services + +Comprehensive solutions to transform your business + +## ![IT Consulting](img/it-consulting.svg) IT Consulting + +Comprehensive IT solutions to drive digital transformation and optimize your technology infrastructure. + +### ![IT Strategy & Planning](img/management-consulting.svg) IT Strategy & Planning + +Develop technology roadmaps aligned with business objectives + +### ![Infrastructure Management](img/infrastructure.svg) Infrastructure Management + +Design and implement robust IT infrastructure solutions + +### ![Cybersecurity](img/security.svg) Cybersecurity + +Protect your assets with advanced security measures + +## ![Management Consulting](img/barchart.svg) Management Consulting + +Strategic business solutions to improve efficiency and drive growth. + +### ![Process Optimization](img/management-consulting.svg) Process Optimization + +Streamline operations and improve business efficiency + +### ![Change Management](img/people.svg) Change Management + +Guide organizational transformation effectively + +### ![Financial Analysis](img/barchart.svg) Financial Analysis + +Data-driven financial planning and optimization + +## ![Infrastructure Services](img/infrastructure.svg) Infrastructure Services + +Build robust foundations for your business success + +### ![Cloud Solutions](img/cloud.svg) Cloud Solutions + +Leverage cloud technology for scalable solutions + +### ![Network Design](img/network.svg) Network Design + +Create secure and efficient network architectures + +### ![System Integration](img/system-integration.svg) System Integration + +Seamlessly integrate systems for optimal performance + +## Need Customized Solutions? + +Let's discuss how we can help transform your business diff --git a/docs/en/terms_and_conditions.md b/docs/en/terms_and_conditions.md new file mode 100644 index 0000000..6de4cec --- /dev/null +++ b/docs/en/terms_and_conditions.md @@ -0,0 +1,56 @@ +# General Terms and Conditions (GTC) + +## 1. Scope of Application + +These General Terms and Conditions (GTC) apply to all services provided by **AllYouCanGET – Sinn Consulting** (hereinafter “the Company”) to its clients, whether private individuals or businesses. By commissioning the Company, the client accepts these GTC in full. + +## 2. Services + +The Company provides services in the areas of **business consulting, creative development, digital marketing, coaching, and technical services**. The specific scope, content, and objectives of a service are agreed upon individually in writing or electronically. + +## 3. Offers and Agreements + +All offers are non-binding until confirmed by the Company in writing. Agreements, changes, or additional services are valid only with written confirmation. + +## 4. Prices and Payment Terms + +Unless otherwise agreed, prices are stated in Swiss francs (CHF) and exclude VAT (if applicable). +Invoices are issued after completion of the agreed service and are payable within **30 days** from the invoice date without deductions. +In case of late payment, the Company reserves the right to charge reminder fees and default interest. + +## 5. Cancellations and Refunds + +Cancellations by the client must be made in writing. + +- **Up to 7 days before the service date:** partial refund (50%) of the agreed amount. +- **Less than 7 days before the service date:** no refund possible. + The Company reserves the right to cancel or postpone services for important reasons (e.g. illness, unforeseen events), in which case a new date will be arranged or any payments already made will be refunded. + +## 6. Liability + +The Company is liable only for **intentional or grossly negligent actions**. Liability for slight negligence, indirect or consequential damages, lost profits, or data loss is excluded. +The client remains responsible for the decisions and results derived from the consulting services. + +## 7. Intellectual Property + +All materials, concepts, designs, and documentation created by the Company remain the property of the Company until full payment has been received. +Unless otherwise agreed, the Company retains all copyrights and usage rights to its materials, which may not be copied, shared, or used for other purposes without prior written consent. + +## 8. Confidentiality + +The Company treats all information and data provided by clients as **strictly confidential**. This obligation continues after the business relationship ends. The client likewise undertakes to maintain confidentiality regarding all information obtained about the Company. + +## 9. Data Protection + +Personal data is collected and processed solely for the purpose of providing and managing services. Data is not shared with third parties without consent, except where legally required. + +## 10. Applicable Law and Jurisdiction + +These GTC and all contractual relationships between the Company and the client are governed exclusively by **Swiss law**. +The exclusive place of jurisdiction is **Widen, Switzerland**. + +--- + +## Last Update + +Effective from 01.01.2025 diff --git a/docs/en/webservices.md b/docs/en/webservices.md new file mode 100644 index 0000000..239d9be --- /dev/null +++ b/docs/en/webservices.md @@ -0,0 +1,94 @@ +# Web Services – AllYouCanGET + +Reliable, secure, and fully managed hosting solutions tailored for your business + +## ![Web Hosting](img/security.svg) Managed Hosting Solutions + +Enjoy peace of mind with our fully managed hosting services. We handle maintenance, updates, backups, and security — so you can focus on your business. + +### Managed CMS Hosting (PHP-based) + +Ideal for WordPress and similar CMS platforms. + +- Daily backups +- System and plugin updates +- Security screening and monitoring +- Starting from **25 CHF / month (incl. VAT)** + +### Managed Application Hosting + +(Node.js / Python) + +Perfect for modern web applications and APIs. + +- Daily backups +- Framework and dependency updates +- Security screening and optimization +- Starting from **25 CHF / month (incl. VAT)** + +### Managed Docker Hosting + +Flexible and containerized environments for developers. + +- Daily backups +- Security screening and resource monitoring +- Starting from **25 CHF / month (incl. VAT)** + +## ![Web Packages](img/infrastructure.svg) Hosting Packages + +Simple, transparent, and scalable hosting plans with Swiss reliability. + +### Basic Hosting + +- **2 GB Diskspace** +- **1 Domain** +- **Unlimited Emails** +- **1 GB RAM** +- **SSH Access** +- **10 CHF / month (incl. VAT)** + +### Standard Hosting + +- **5 GB Diskspace** +- **1 Domain** +- **Unlimited Emails** +- **2 GB RAM** +- **SSH Access** +- **25 CHF / month (incl. VAT)** + +### Premium Hosting + +- **10 GB Diskspace** +- **1 Domain** +- **Unlimited Emails** +- **4 GB RAM** +- **SSH Access** +- **50 CHF / month (incl. VAT)** + +## ![Why Choose Us](img/people.svg) Why Choose AllYouCanGET? + +### Reliable Swiss Hosting + +- Swiss data protection and hosting standards +- 24/7 monitoring and proactive security +- Daily backups included in all plans +- Scalable resources for growing businesses +- Expert technical support from experienced professionals + +### Seamless Migration + +- Free migration assistance for new customers +- Minimal downtime during transition +- Comprehensive support throughout the migration process + +### Custom Solutions + +- Tailored hosting environments to fit your needs +- Flexible resource allocation and scaling options +- Expert advice on architecture and optimization +- Integration with third-party services and tools +- Performance tuning for optimal speed and reliability + +## Ready to Get Started? + +Let’s set up your hosting today — secure, managed, and built for growth. diff --git a/docs/pages.json b/docs/pages.json new file mode 100644 index 0000000..1235e32 --- /dev/null +++ b/docs/pages.json @@ -0,0 +1,92 @@ +{ + "index.html": { + "name": "Home", + "url": "index.html", + "active": "False", + "page_title": "Transform Your Business", + "page_subtitle": "with expert IT & Management Consulting", + "page_cta": "See all Services", + "page_cta_url": "services.html", + "meta": { + "title": "AllYouCanGET - IT & Management Consulting", + "description": "AllYouCanGET is an expert IT & Management Consulting firm helping businesses transform their operations and drive growth through technology and strategy solutions.", + "keywords": "IT consulting services, Management consulting solutions, Digital transformation, Strategic technology solutions, IT strategy and planning, Technology assessment, Enterprise architecture consulting, Cybersecurity services, Infrastructure services, Cloud solutions, Network design and implementation, System integration, Security implementation, Streamline operations, Optimize business performance, Process optimization, Change management, Strategy development, Financial analysis, Business growth acceleration, Operations consulting, Expert consulting team, Business innovation, AllYouCanGET consulting", + "og_description": "Transform your business with expert IT and Management consulting. We streamline operations, optimize performance, and drive innovation." + } + }, + "services.html": { + "name": "Services", + "url": "services.html", + "active": "False", + "page_title": "AllYouCanGET Services", + "page_subtitle": "IT & Management Consulting", + "page_cta": "Get Started", + "page_cta_url": "#contact-form", + "meta": { + "title": "Our Services - IT & Management Consulting | AllYouCanGET", + "description": "AllYouCanGET offers IT consulting services and management consulting solutions to help businesses drive digital transformation and optimize operations. Our expert consulting team provides strategic technology solutions to streamline operations, optimize business performance, and accelerate business growth. Contact us for a free consultation.", + "keywords": "IT consulting services, Management consulting solutions, Digital transformation, Strategic technology solutions, IT strategy and planning, Technology assessment, Enterprise architecture consulting, Cybersecurity services, Infrastructure services, Cloud solutions, Network design and implementation, System integration, Security implementation, Streamline operations, Optimize business performance, Process optimization, Change management, Strategy development, Financial analysis, Business growth acceleration, Operations consulting, Expert consulting team, Business innovation, AllYouCanGET consulting", + "og_description": "Explore our expert IT, Management, and Infrastructure consulting services designed to drive growth and efficiency for your business." + } + }, + "webservices.html": { + "name": "Web Services", + "url": "webservices.html", + "active": "False", + "page_title": "Web Services Solutions", + "page_subtitle": "Scalable and Secure Web Services", + "page_cta": "Learn More", + "page_cta_url": "#contact-form", + "meta": { + "title": "Web Services - Scalable and Secure Solutions | AllYouCanGET", + "description": "AllYouCanGET provides scalable and secure web services solutions to help businesses enhance their online presence and improve customer engagement. Our expert team delivers customized web services tailored to your business needs. Contact us for more information.", + "keywords": "Web services solutions, Scalable web services, Secure web services, Online presence enhancement, Customer engagement, Customized web services, Business web solutions, IT consulting, Management consulting, AllYouCanGET consulting", + "og_description": "Discover our scalable and secure web services solutions designed to enhance your online presence and engage customers effectively." + } + }, + "coaching.html": { + "name": "Coaching", + "url": "coaching.html", + "active": "False", + "page_title": "Professional Coaching Services", + "page_subtitle": "Unlock Your Potential Through Expert Mentoring", + "page_cta": "Start Your Journey", + "page_cta_url": "#contact-form", + "meta": { + "title": "Professional Coaching Services | AllYouCanGET", + "description": "AllYouCanGET offers professional coaching services in Career Advice, Personal Development, Leadership, and Technical Skills—helping individuals unlock their potential and achieve their personal and professional goals through expert mentoring and guidance.", + "keywords": "Coaching, Mentoring, Career Advice, Personal Development, Leadership Coaching, Technical Coaching, Software Development, IT Career, Switzerland, Germany, Remote Work, Freelancing, Startups, Corporates, Consulting, Agile, Leadership, Technical Skills, Professional coaching, Career coaching, Executive coaching, Team coaching, Business coaching", + "og_description": "Transform your career with AllYouCanGET's professional coaching services. Contact us for a free consultation." + } + }, + "imprint.html": { + "name": "Imprint", + "url": "imprint.html", + "active": "False", + "page_title": "AllYouCanGET Imprint", + "page_subtitle": "IT & Management Consulting", + "page_cta": "Contact Us", + "page_cta_url": "#contact-form", + "meta": { + "title": "Imprint | AllYouCanGET", + "description": "Legal imprint and contact information for AllYouCanGET - Sinn Consulting. Find our company details, address, and legal disclosures as required by law.", + "keywords": "Imprint, Legal Notice, AllYouCanGET, Sinn Consulting, Impressum, contact information, legal disclosure, company details", + "og_description": "Official imprint and legal information for AllYouCanGET - Sinn Consulting. Includes company details and contact information." + } + }, + "terms_and_conditions.html": { + "name": "Terms", + "url": "terms_and_conditions.html", + "active": "False", + "page_title": "Terms and Conditions", + "page_subtitle": "AllYouCanGET Consulting Services", + "page_cta": "Contact Us", + "page_cta_url": "#contact-form", + "meta": { + "title": "Terms and Conditions | AllYouCanGET", + "description": "Read the terms and conditions for using AllYouCanGET consulting services. Understand your rights and obligations when engaging with our IT and management consulting solutions.", + "keywords": "Terms and Conditions, AllYouCanGET, Consulting Services, IT Consulting, Management Consulting, Legal Terms, User Agreement, Service Agreement", + "og_description": "Review the terms and conditions for AllYouCanGET consulting services. Know your rights and responsibilities." + } + } +} diff --git a/html/.htaccess b/html/.htaccess new file mode 100644 index 0000000..e7e5002 --- /dev/null +++ b/html/.htaccess @@ -0,0 +1,83 @@ +## Canonicalization and security headers +## ------------------------------------ + +## Enable rewrite engine +RewriteEngine On + +## 1) Canonical host: redirect www -> apex (always to HTTPS) +RewriteCond %{HTTP_HOST} ^www\.allucanget\.biz$ [NC] +RewriteRule ^ https://allucanget.biz%{REQUEST_URI} [R=301,L] + +## 2) Force HTTPS (works for direct HTTPS and when behind proxies setting X-Forwarded-Proto) +RewriteCond %{HTTPS} !=on [OR] +RewriteCond %{HTTP:X-Forwarded-Proto} !https +RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L] + +## 3) Security headers + + # HSTS: one year, include subdomains (enable after HTTPS is configured everywhere) + Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" env=HTTPS + + # Clickjacking protection (also enforced via CSP frame-ancestors) + Header set X-Frame-Options "SAMEORIGIN" + + # MIME sniffing protection + Header set X-Content-Type-Options "nosniff" + + # Referrer policy + Header set Referrer-Policy "strict-origin-when-cross-origin" + + # Lock down powerful features not in use + Header set Permissions-Policy "geolocation=(), camera=(), microphone=(), payment=(), usb=(), accelerometer=(), gyroscope=(), magnetometer=()" + + # Content Security Policy tuned for local assets + jsdelivr Tailwind CDN + Header set Content-Security-Policy "object-src 'none'; frame-ancestors 'self'; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; upgrade-insecure-requests; block-all-mixed-content" + Header set Content-Security-Policy "default-src *.allucanget.biz; base-uri 'self'; script-src 'self' 'unsafe-inline' https://contact.allucanget.biz https://cdn.jsdelivr.net https://static.cloudflareinsights.com; style-src 'self' 'unsafe-inline' https://contact.allucanget.biz https://cdn.jsdelivr.net https://static.cloudflareinsights.com; img-src 'self' data: https:; form-action https://contact.allucanget.biz; connect-src 'self' https://contact.allucanget.biz;" + + +# ------------------------------------------------------------------ +# Redirect the root URL ("/") to the canonical file "index.html" +# ------------------------------------------------------------------ +RewriteRule ^$ /index.html [L] + +# ------------------------------------------------------------------ +# Internally rewrite clean URLs like "/about" → "about.html" +# ------------------------------------------------------------------ +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^([^/]+)/?$ $1.html [L] + +# ensure charset encoding is UTF-8 for all content +AddDefaultCharset UTF-8 + +# BEGIN cPanel-generated php ini directives, do not edit +# Manual editing of this file may result in unexpected behavior. +# To make changes to this file, use the cPanel MultiPHP INI Editor (Home >> Software >> MultiPHP INI Editor) +# For more information, read our documentation (https://go.cpanel.net/EA4ModifyINI) + + php_flag asp_tags Off + php_flag display_errors Off + php_value max_execution_time 30 + php_value max_input_time 60 + php_value max_input_vars 1000 + php_value memory_limit 64M + php_value post_max_size 16M + php_value session.gc_maxlifetime 1440 + php_value session.save_path "/var/cpanel/php/sessions/ea-php56" + php_value upload_max_filesize 16M + php_flag zlib.output_compression Off + + + php_flag asp_tags Off + php_flag display_errors Off + php_value max_execution_time 30 + php_value max_input_time 60 + php_value max_input_vars 1000 + php_value memory_limit 64M + php_value post_max_size 16M + php_value session.gc_maxlifetime 1440 + php_value session.save_path "/var/cpanel/php/sessions/ea-php56" + php_value upload_max_filesize 16M + php_flag zlib.output_compression Off + +# END cPanel-generated php ini directives, do not edit diff --git a/html/coaching.html b/html/coaching.html new file mode 100644 index 0000000..369e75c --- /dev/null +++ b/html/coaching.html @@ -0,0 +1 @@ +Professional Coaching Services

Our Coaching Areas

Career Advice Career Advice

Navigate your career path with expert guidance tailored to your industry and location.

IT Career

  • Career paths in software development
  • Technical vs management track
  • Transitioning between specialties

Switzerland Specifics

  • Work culture and expectations
  • Salary negotiations
  • Work permits and regulations

Germany Specifics

  • Job market trends
  • Industry hubs and opportunities

Remote Work

  • Building a remote work routine
  • Communication strategies

Freelancing

  • Setting up as independent contractor
  • Client acquisition and retention
  • Pricing strategies and negotiation

Startups

  • Evaluating startup opportunities
  • Equity considerations
  • Growth expectations

Corporates

  • Navigating corporate structures
  • Internal mobility
  • Building influence

Consulting

  • Types of consulting roles
  • Client management
  • Balancing multiple projects

Personal Development Personal Development

Discover your values, strengths, and purpose to create a fulfilling life and career.

Values

  • Value identification exercises
  • Aligning career with personal values
  • Identity Workshop

Ikigai

  • Finding purpose through passion
  • Mission, vocation, and profession
  • Practical discovery exercises

Strengths

  • Assessment tools and frameworks
  • Leveraging strengths professionally

Weaknesses

  • Growth mindset approaches
  • Strategic improvement planning

Vision

  • Creating personal vision statements
  • Long-term life planning
  • Visualization techniques

Goals

  • SMART goal framework
  • Balancing short and long-term objectives

Agile Self Organization

  • Personal Kanban implementation
  • Quarterly planning and review cycles
  • Agile methods for personal productivity

Leadership Coaching Leadership Coaching

Develop essential leadership skills to inspire teams and drive organizational success.

Leadership Styles

  • Management vs Leadership
  • Situational leadership approaches
  • Transformational vs transactional

Communication

  • Active listening techniques
  • Delivering difficult messages
  • Cross-cultural communication

Feedback

  • Structured feedback frameworks
  • Creating feedback culture
  • Growth-oriented approaches

Delegation

  • RACI and DACI models
  • Identifying tasks to delegate
  • Building team capacity

Decision Making

  • OODA loop, DECIDE model
  • Data-driven decisions
  • Managing decision paralysis

Conflict Management

  • Conflict resolution models
  • Mediation techniques
  • Transforming conflict to opportunity

Motivation

  • Intrinsic vs extrinsic motivation
  • SCARF and Drive frameworks
  • Tailoring to individual needs

Empathy & Compassion

  • Building empathetic leadership
  • Compassion without compromising

Trust

  • Trust-building exercises
  • Trust in remote teams
  • Rebuilding broken trust

Psychological Safety

  • Assessment techniques
  • Creating safety in teams
  • Connection to innovation

Team Building

  • Tuckman's team formation
  • Remote team activities
  • Cross-functional dynamics

Agile Project Management

  • Project roles and responsibilities
  • Agile hierarchies
  • Balancing agility with governance

Technical Coaching Technical Coaching

Master technical skills and best practices in software development and IT infrastructure.

Software and Web Development

Scalability

  • Horizontal vs vertical scaling
  • Distributed systems principles
  • Load balancing strategies

Browser

  • Developer tools mastery
  • Performance optimization
  • Extension development

Operating Systems

Linux

  • Debian, Ubuntu
  • RedHat, Fedora, CentOS
  • Arch, Manjaro
  • SUSE, openSUSE

Windows

  • Windows 10 & 11
  • Windows Server 2019
  • Windows Server 2022

macOS

  • macOS basics
  • System administration
  • Shell commands

Development Tools

Git

  • Advanced git workflows
  • Git hooks and automation
  • Collaboration strategies

Docker

  • Container optimization
  • Docker Compose
  • Kubernetes orchestration

Visual Studio Code

Let's work together to unlock your full potential

Ready to Accelerate Your Growth?

Ready to Get Started?

Contact us today to learn more about how we can help you.

\ No newline at end of file diff --git a/html/css/styles.css b/html/css/styles.css new file mode 100644 index 0000000..1081247 --- /dev/null +++ b/html/css/styles.css @@ -0,0 +1,565 @@ +/* Color Palette and Typography */ + +/* Global Styles */ +:root { + --primary: #10263d; + --secondary: #1b3c52; + --tertiary: #2f74ad; + --quaternary: #a3523f; + --quinary: #d9834d; + --white: #ffffff; + --black: var(--primary); + --black-translucent: rgba(16, 38, 61, 0.55); + --black-translucent-hover: rgba(16, 38, 61, 0.75); + --color-background: var(--primary); + --color-text: var(--primary); + --color-text-inverse: var(--white); + --color-link: #1f5f96; + --color-link-hover: #174875; + --color-link-inverse: var(--secondary); + --color-background-inverse: var(--white); + --color-muted: #e8eef6; + --color-muted-hover-bg: #2b5677; + --header-start: #0b8f69; + --header-end: #083f76; + --hero-start: #087f5d; + --hero-end: #062f5c; + --color-gray-50: oklch(98.5% 0.002 247.839); + --color-gray-dark: var(--primary); + --color-gray-medium-dark: var(--secondary); + --color-border-faint: rgba(255, 255, 255, 0.72); + --color-input-border: #a8b6c7; + --bg-inverse: var(--secondary); +} +*, +*::before, +*::after { + box-sizing: border-box; +} + +html { + width: 100%; + overflow-x: hidden; +} + +body { + font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", + "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + line-height: 1.3; + color: var(--color-text); + margin: 0; + padding: 0; + width: 100%; + overflow-x: hidden; +} +a { + color: var(--color-link); + text-decoration: underline; +} +a:hover { + color: var(--color-link-hover); + text-decoration: underline; +} +a:focus-visible, +button:focus-visible, +input:focus-visible, +textarea:focus-visible { + outline: 2px solid var(--quinary); + outline-offset: 2px; +} +svg { + max-width: 64px; + max-height: 64px; +} +img, +video, +iframe { + max-width: 100%; + height: auto; +} + +iframe { + width: 100%; + min-height: 440px; + display: block; +} + +ul { + list-style-type: none; + padding: 0; +} +li { + margin-bottom: 0.2rem; +} + +h2 img { + display: inline; + max-height: 1.5em; + min-height: 1.5em; + vertical-align: middle; + /* position left of text */ +} +h3 img { + display: inline; + max-height: 1.2em; + min-height: 1.2em; + vertical-align: middle; + /* position left of text */ +} +#header { + /*background-color: var(--color-gray-medium-dark);*/ + background: linear-gradient(120deg, var(--header-start), var(--header-end)); + color: var(--white); + padding: 1rem 1rem 2rem 1rem; + text-align: center; +} + +#page-title { + width: 100%; +} +.page-title-header { + font-size: clamp(1.6rem, 4.5vw, 2.5rem); + line-height: 1.2; + font-weight: bold; + margin-bottom: 1rem; +} +.page-subtitle { + font-size: clamp(1rem, 3.4vw, 1.5rem); + line-height: 1.2; + margin: 0; + padding: 0; +} +.header-cta { + margin-top: 1rem; + padding-inline: 1.5rem; +} +.header-cta-buttons { + display: flex; + justify-content: center; + align-items: center; + gap: 0.75rem; + flex-wrap: wrap; +} +#site-navigation { + color: var(--white); + width: 100%; + position: relative; + z-index: 50; +} +#site-navigation-container { + max-width: 1200px; + margin: 0 auto; + padding: 0 1rem; + min-height: 80px; + display: flex; + align-items: center; + position: relative; +} +#site-navigation-content { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; +} +#site-navigation-bar { + display: flex; + align-items: center; + width: 100%; + justify-content: space-between; + min-width: 0; +} +#site-navigation-brand { + flex-shrink: 0; + min-width: 0; +} +a.site-home-link { + display: flex; + align-items: center; + color: var(--white); + text-decoration: none; + font-size: 1.5rem; + font-weight: bold; + gap: 0.75rem; + transition: opacity 0.2s ease; + min-width: 0; +} +a.site-home-link:hover { + opacity: 0.9; +} +.nav-logo { + height: 50px; + width: auto; + object-fit: contain; +} +.nav-text { + font-size: 1.5rem; + font-weight: bold; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.hamburger-menu { + display: none; + flex-direction: column; + justify-content: space-around; + width: 30px; + height: 30px; + background: transparent; + border: none; + cursor: pointer; + padding: 0; + z-index: 1001; +} +.hamburger-line { + width: 30px; + height: 3px; + background: var(--white); + border-radius: 2px; + transition: all 0.3s ease; + transform-origin: center; +} +.nav-menu { + display: flex; +} +#site-nav-links { + display: flex; + gap: 0.5rem; + align-items: center; + margin: 0; + padding: 0; + list-style: none; +} +a.site-nav-link { + color: var(--color-muted); + text-decoration: none; + font-weight: 600; + font-size: 1rem; + padding: 0.75rem 1rem; + border-radius: 0.5rem; + transition: all 0.2s ease; + position: relative; +} +a.site-nav-link:hover { + color: var(--white); + background-color: var(--color-muted-hover-bg); +} +a.site-nav-link.active { + color: var(--white); + background-color: rgba(255, 255, 255, 0.2); +} +#main-content { + padding-top: 0; +} +#main { + background-color: var(--white); + min-height: 100vh; + margin: 0 auto; + padding: 0; + box-sizing: border-box; + border: 0 solid; +} +.bigbutton { + border-radius: 0.5rem; + padding: 0.5rem 1rem; + background: linear-gradient(135deg, var(--quaternary), var(--quinary)); + color: var(--white); + margin-top: 0.5rem; + border: 1px solid var(--color-border-faint); +} +.bigbutton:hover { + background: linear-gradient(135deg, #934332, #c86f3a); + color: var(--white); + border-color: var(--white); +} +.bigbutton a { + color: var(--white); + font-weight: bold; + font-size: 1.2rem; + text-decoration: none; +} +.bigbutton a:hover { + color: var(--white); + font-weight: bold; + text-decoration: none; + border-color: var(--white); +} +.bigbutton a span:last-of-type { + margin-left: 0.5rem; +} + +#hero { + background: linear-gradient( + to right, + var(--hero-start), + var(--hero-end) + ); + color: var(--white); + padding: 4rem 0; +} +#hero .container { + text-align: left; +} +#hero h1 { + font-size: clamp(1.35rem, 4vw, 2rem); + line-height: 1.2; + font-weight: bold; + margin-bottom: 1rem; + text-align: center; +} +#hero p { + font-size: clamp(1rem, 3vw, 1.5rem); + margin: 0; + padding: 0; +} +.section { + background-color: var(--white); + padding-block: 3rem; +} +.section:nth-last-child(4) { + min-height: 300px; + height: auto; + text-align: center; +} +.section:nth-last-child(5) { + min-height: 300px; + height: auto; + text-align: center; +} +.section-container { + padding-inline: 1rem; + margin-inline: auto; +} +.section h2 { + font-size: clamp(1.3rem, 4vw, 2.25rem); + font-weight: bold; + margin-bottom: 2rem; +} +.section-content { + font-size: clamp(1rem, 2.3vw, 1.125rem); + color: var(--color-text); + margin-bottom: 1rem; + overflow-wrap: anywhere; +} +.section-content hr { + max-width: 80%; + margin: 3rem auto 1rem auto; +} +.section-cards { + display: grid; + gap: 2rem; + grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); +} +.section-card { + background-color: var(--white); + border-radius: 0.5rem; + padding: 1.5rem; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + transition: box-shadow 0.3s ease; +} +.section-card:hover { + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.2); +} +.section-card-title { + font-size: 1.25rem; + margin-bottom: 1rem; + font-weight: bold; +} +.section-card-detail { + background-color: var(--color-background-inverse); + border-radius: 0.375rem; + padding: 0.75rem; +} +img[src*="img/partner/"] { + max-width: 200px; + max-height: 100px; + margin: 0 auto; + display: block; + margin: 2rem auto; +} +img[src*="img/partner/"].hidden { + max-width: 200px; + max-height: 100px; + display: none; +} +#call-to-action { + background: linear-gradient(120deg, #0a8e67, #073b70); + color: var(--white); + margin-top: 0; + padding: 3rem 0; + text-align: center; +} +#cta-container { + max-width: 800px; + padding: 0 1.5rem; + text-align: center; +} +#cta-container iframe { + max-width: 100%; + height: auto; + min-height: 520px; +} +#contact-form { + display: block; + max-width: 600px; + max-height: 600px; + margin: 0 auto; + scroll-behavior: auto; +} +#contact-form input, +#contact-form textarea { + width: 100%; + padding: 0.5rem; + margin-bottom: 1rem; + border: 1px solid var(--color-input-border); + border-radius: 0.25rem; +} +#contact-form textarea { + height: 150px; +} +#footer { + background-color: var(--color-background); + color: var(--quaternary); + padding: 2rem 0; + text-align: center; + display: block; +} +.footer-link { + color: var(--quinary); + text-decoration: underline; +} +#services { + display: block; +} + +.container { + margin: 0 auto; + width: min(100%, 1200px); + max-width: 100%; + padding-inline: 1rem; +} + + +@media screen and (max-width: 640px) { + #header { + padding: 0; + padding-bottom: 1.5rem; + } + + h1, h1.page-title-header, #hero h1 { + font-size: 1.4rem; + } + .page-subtitle { + font-size: 1rem; + } + .section h2 { + font-size: 1.3rem; + } + .section-content { + font-size: 1rem; + } + #page-title { + margin: 0.5rem auto; + padding: 0.5rem; + } + #hero { + padding: 1.5rem 0; + } + .container { + width: 100%; + max-width: 100%; + padding: 0 0.9rem; + } + +} +@media screen and (max-width: 768px) { + #site-navigation-container { + padding: 0 1rem; + min-height: 60px; + } + + #site-navigation-bar { + gap: 0.75rem; + } + + a.site-home-link { + font-size: 1.25rem; + gap: 0.5rem; + max-width: calc(100% - 44px); + } + + .nav-logo { + height: 35px; + } + + .nav-text { + font-size: 1.25rem; + } + + .hamburger-menu { + display: flex; + } + + .nav-menu { + position: absolute; + top: 100%; + left: 0; + right: 0; + background-color: var(--color-gray-dark); + transform: translateY(-100%); + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; + border-top: 1px solid rgba(255, 255, 255, 0.1); + z-index: 1000; + width: 100%; + max-height: calc(100vh - 60px); + overflow-y: auto; + } + + .nav-menu.open { + transform: translateY(0); + opacity: 1; + visibility: visible; + } + + #site-nav-links { + flex-direction: column; + width: 100%; + padding: 1rem 0; + } + + a.site-nav-link { + width: 100%; + text-align: center; + padding: 1rem; + margin: 0.25rem 0; + border-radius: 0; + } + + a.site-nav-link:hover { + background-color: var(--color-muted-hover-bg); + } + + /* Hamburger animation */ + .hamburger-menu.open .hamburger-line:nth-child(1) { + transform: rotate(45deg) translate(6px, 6px); + } + + .hamburger-menu.open .hamburger-line:nth-child(2) { + opacity: 0; + } + + .hamburger-menu.open .hamburger-line:nth-child(3) { + transform: rotate(-45deg) translate(6px, -6px); + } +} + +@media screen and (max-width: 420px) { + .nav-text { + display: none; + } + + a.site-home-link { + max-width: calc(100% - 40px); + } +} diff --git a/html/favicon.ico b/html/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..076e847535504e1d0412a6e10b04402cd56d10d3 GIT binary patch literal 374 zcmV-+0g3*JP)QheS1?y`yN5XKzI*|f z3gf~{Q13&$g7d=G5SZzW`(6QO%NAD=0mJqSs2V)TAuK}K+zYs4O?!mw7#km-fFdnb zgFP8}2kjGdV__k!5e{^_KEd_EjZhEreq6;96iyDx5@WDU>IvG|XsSQZ#}jn@0;(KZ zJdimLwEOvC?k3)du{<_n$4{W5Z4Y&kE!IQtJORgMGXD`YWUVLAHn<{9 literal 0 HcmV?d00001 diff --git a/html/google8753946d6666aa9a.html b/html/google8753946d6666aa9a.html new file mode 100644 index 0000000..f348af1 --- /dev/null +++ b/html/google8753946d6666aa9a.html @@ -0,0 +1 @@ +google-site-verification: google8753946d6666aa9a.html \ No newline at end of file diff --git a/html/img/Logo-AllYouCanGet-204x100.png b/html/img/Logo-AllYouCanGet-204x100.png new file mode 100644 index 0000000000000000000000000000000000000000..a873c520c767f431b59337cd46885ae29693ec7f GIT binary patch literal 7132 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 + + + + \ No newline at end of file diff --git a/html/img/cloud.svg b/html/img/cloud.svg new file mode 100644 index 0000000..0524910 --- /dev/null +++ b/html/img/cloud.svg @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/html/img/coaching-career.svg b/html/img/coaching-career.svg new file mode 100644 index 0000000..e80aa01 --- /dev/null +++ b/html/img/coaching-career.svg @@ -0,0 +1,3 @@ + + + diff --git a/html/img/coaching-leadership.svg b/html/img/coaching-leadership.svg new file mode 100644 index 0000000..611f87a --- /dev/null +++ b/html/img/coaching-leadership.svg @@ -0,0 +1,3 @@ + + + diff --git a/html/img/coaching-personal.svg b/html/img/coaching-personal.svg new file mode 100644 index 0000000..a3dd481 --- /dev/null +++ b/html/img/coaching-personal.svg @@ -0,0 +1,3 @@ + + + diff --git a/html/img/coaching-technical.svg b/html/img/coaching-technical.svg new file mode 100644 index 0000000..a05403e --- /dev/null +++ b/html/img/coaching-technical.svg @@ -0,0 +1,3 @@ + + + diff --git a/html/img/cropped-Logo-AllYouCanGet-512x512-1-180x180.png b/html/img/cropped-Logo-AllYouCanGet-512x512-1-180x180.png new file mode 100644 index 0000000000000000000000000000000000000000..d465888340f7a7178a6eaca43758cdb288eef7e3 GIT binary patch literal 5957 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

xmI4FtVSXS<{>At zA(@aiWu&EdVEU1>RZ?I$rlT8z%F=X*>&a~XTxN+*mZizN&3J4z$I$?dAQ8r6T`lTq zaNr(9=>-@1bYC>;{fwXjr+quJB9kKcV{bZn(=AIA#@4#q${A&UPZCazb+)(98>O#~ zNz%UBZLbw!l0z3dPP*?-v81%j-StsTLt^wS+KNa!yEb*21d`uEq1T57yNay`=qTou z;Y1{?khQPt(s<9~HVOr-QW>RC%fHkDRg9q=boANqq9Q+c%cN zz(k6BJiLczbJ=Cb%>3-Xo2MS*O(?<#7Y`(t?sFmPdwzV>ydgyZr@DP(jz_Q8SjIs~ zpLOI{d2RVd3L}_JNlMd~K$q%c9(IckK=gX1 zp1we%w1pV!=A|#|mM&nK3?z6jEv^8HQT0^1Jz(k+?)zLwEN7G>E~)J_(Z)@P=PWnt zGuSg-om*z{&SywkpH`0n$g|+j4HWwBLDPClfSMXfOYE`G0gsZP)FPyL=)VAGi(s2i zB{$dfV<-L57e{FG!n@0eJ=C@jtXKq&4N)x zWGnX4uS}Q2Aqe{S)5`QHVou6-bLeBE*L9)q{?!Q)LDm6T_7FFUQ$oDldlcm9T2o2E z#E|WCI&n@fyHD?BWb`F7N(gC71&+vbGrz>3I(d3JX5sxJ_P-a(P_8XVSlpX7mj3L@j#4OElIo15F z(Ru@g-fI*As#ZryN6t=>WpVnF7aJEMt>!dMakSjg4&Fea8z-=rx^NcGAS_!>v1RBF zP-$Xl2%tsSA4ehjwpUP`mXKpIbYT(+d?D4rEydu>dI%I3`u-@W%*@auDsa6EXHAlpBO}i4b0*oG_?$4pnD=eZH zC(PTn@R&I-a}6G{W7aX2CvJuW%hYYWzhORiQRrPwgt{Q04|SU6d3m;Zp62k9jI{ml(%yOCeH0oUn%*2O8efjD{~i>2uhIDZvwLo%&}TW#-_c&9&`5zk#G>&H zQZFBLABE^oqtNS4y|$BAZb#oip})^&-asKSh+9H@YESg0#^^o@{YblQIpfbi^w)9e zZ?dO1P^cd)dinK7Jn3&iAsP|!dv^b=oqWFmeC}jqGEBEoXl(T2ott%`yT1B^_&6HX z_(P|G-yX;Wg?|6vW?lTRY6A)>`dv*lAl5#_q>tj%t$#oM)f%lD`0H50H=AhSSGVwM z1t8JDH*M%|`!nN%1_pZ7ef_O|@-Og#CH(5P{14FfA2tuH#FhX6002ovPDHLkV1hEG B$vgl6 literal 0 HcmV?d00001 diff --git a/html/img/cropped-Logo-AllYouCanGet-512x512-1-32x32.png b/html/img/cropped-Logo-AllYouCanGet-512x512-1-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..076e847535504e1d0412a6e10b04402cd56d10d3 GIT binary patch literal 374 zcmV-+0g3*JP)QheS1?y`yN5XKzI*|f z3gf~{Q13&$g7d=G5SZzW`(6QO%NAD=0mJqSs2V)TAuK}K+zYs4O?!mw7#km-fFdnb zgFP8}2kjGdV__k!5e{^_KEd_EjZhEreq6;96iyDx5@WDU>IvG|XsSQZ#}jn@0;(KZ zJdimLwEOvC?k3)du{<_n$4{W5Z4Y&kE!IQtJORgMGXD`YWUVLAHn<{9 literal 0 HcmV?d00001 diff --git a/html/img/imprint-shield.svg b/html/img/imprint-shield.svg new file mode 100644 index 0000000..2bc6ef3 --- /dev/null +++ b/html/img/imprint-shield.svg @@ -0,0 +1,3 @@ + + + diff --git a/html/img/infrastructure.svg b/html/img/infrastructure.svg new file mode 100644 index 0000000..97df610 --- /dev/null +++ b/html/img/infrastructure.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/html/img/it-consulting.svg b/html/img/it-consulting.svg new file mode 100644 index 0000000..201bdd5 --- /dev/null +++ b/html/img/it-consulting.svg @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/html/img/management-consulting.svg b/html/img/management-consulting.svg new file mode 100644 index 0000000..1140d46 --- /dev/null +++ b/html/img/management-consulting.svg @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/html/img/network.svg b/html/img/network.svg new file mode 100644 index 0000000..cd834f8 --- /dev/null +++ b/html/img/network.svg @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/html/img/partner/aws.svg b/html/img/partner/aws.svg new file mode 100644 index 0000000..3444ffa --- /dev/null +++ b/html/img/partner/aws.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/html/img/partner/google.svg b/html/img/partner/google.svg new file mode 100644 index 0000000..ca31904 --- /dev/null +++ b/html/img/partner/google.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/html/img/partner/ibm.svg b/html/img/partner/ibm.svg new file mode 100644 index 0000000..a87d112 --- /dev/null +++ b/html/img/partner/ibm.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/html/img/partner/informatica.svg b/html/img/partner/informatica.svg new file mode 100644 index 0000000..4a5072b --- /dev/null +++ b/html/img/partner/informatica.svg @@ -0,0 +1,40 @@ + + logo-informatica-svg + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/html/img/partner/microsoft.svg b/html/img/partner/microsoft.svg new file mode 100644 index 0000000..b6d5cea --- /dev/null +++ b/html/img/partner/microsoft.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/html/img/partner/oracle.svg b/html/img/partner/oracle.svg new file mode 100644 index 0000000..e16fccf --- /dev/null +++ b/html/img/partner/oracle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/html/img/partner/salesforce.svg b/html/img/partner/salesforce.svg new file mode 100644 index 0000000..a888113 --- /dev/null +++ b/html/img/partner/salesforce.svg @@ -0,0 +1,25 @@ + + +Salesforce.com logo +A cloud computing company based in San Francisco, California, United States + + + + image/svg+xml + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/html/img/partner/sap.svg b/html/img/partner/sap.svg new file mode 100644 index 0000000..689e0fd --- /dev/null +++ b/html/img/partner/sap.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/html/img/people.svg b/html/img/people.svg new file mode 100644 index 0000000..359a495 --- /dev/null +++ b/html/img/people.svg @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/html/img/security.svg b/html/img/security.svg new file mode 100644 index 0000000..390f6e1 --- /dev/null +++ b/html/img/security.svg @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/html/img/sky-street-light-automotive-lighting-infrastructure-blue.jpg b/html/img/sky-street-light-automotive-lighting-infrastructure-blue.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6e0d613ee0cfa62940ef01221f46f85d94cfa51c GIT binary patch literal 12958 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 + + \ No newline at end of file diff --git a/html/img/test_image.png b/html/img/test_image.png new file mode 100644 index 0000000..016a09f --- /dev/null +++ b/html/img/test_image.png @@ -0,0 +1 @@ +dummy image content \ No newline at end of file diff --git a/html/imprint.html b/html/imprint.html new file mode 100644 index 0000000..90ee1d0 --- /dev/null +++ b/html/imprint.html @@ -0,0 +1 @@ +AllYouCanGET Imprint

Terms of Use – AllYouCanGET

IT Consulting Impressum

gemäß §5 Telemediengesetz bzw. § 5 Abs. 1 E-Commerce-Gesetz bzw. Art. 3 des Bundesgesetzes gegen den unlauteren Wettbewerb (UWG): All You Can GET, c/o Sinn Consulting, Im Dornäcker 16, CH-8967 Widen - georg AT allucanget DOT biz

Welcome

Welcome to the website of AllYouCanGET – Sinn Consulting, based in Widen, Switzerland.
By accessing or using this website, you agree to be bound by the following Terms of Use.
If you do not agree, please refrain from using this site.

1. Scope of Application

These Terms of Use govern the access to and use of the website operated by AllYouCanGET – Sinn Consulting (hereinafter “AllYouCanGET” or “the Company”).
They apply to all visitors, users, and other persons who access the website. Separate agreements apply to consulting and service contracts concluded with clients. For such services, the General Terms and Conditions (GTC) of AllYouCanGET apply.

2. Purpose of the Website

This website serves to provide information about the Company’s services, philosophy, and current offerings in the areas of business consulting, creative development, digital marketing, coaching, and technical services. AllYouCanGET reserves the right to update, modify, or remove website content at any time without prior notice.

3. Intellectual Property and Copyright

All content on this website — including text, graphics, logos, images, design elements, and downloadable materials — is protected by copyright and other intellectual property rights.
Unless otherwise stated, all rights belong to AllYouCanGET – Sinn Consulting. You may view, download, and print website content for personal and non-commercial use only, provided that all copyright and proprietary notices remain intact.
Any reproduction, distribution, modification, or publication of website content for commercial purposes is prohibited without prior written permission from AllYouCanGET.

4. Use of the Website

Users agree to use this website responsibly and only for lawful purposes.
Any use that could impair or disrupt the functionality of the website or violate applicable laws is prohibited. Automated access (e.g., by bots or crawlers) is only permitted within the limits defined by the site’s robots.txt file.
AllYouCanGET reserves the right to restrict or block access to the website at its sole discretion.

5. External Links and Third-Party Content

This website may contain links to external websites operated by third parties.
AllYouCanGET has no control over the content of these websites and assumes no responsibility or liability for their accuracy, legality, or reliability.
Access to linked websites is at the user’s own risk.

6. Confidentiality and Data Protection

AllYouCanGET treats personal data provided via this website (e.g., through contact forms or newsletter subscriptions) as strictly confidential.
Data is processed solely for the stated purpose and in accordance with applicable Swiss data protection regulations.
Please refer to our Privacy Policy for detailed information on how personal data is handled. Any non-personal information or material sent to AllYouCanGET via the website (e.g., feedback, ideas, or concepts) is deemed non-confidential, and AllYouCanGET may use such information freely, unless otherwise agreed in writing.

7. Disclaimer

AllYouCanGET strives to ensure that the information on this website is accurate and up to date.
However, no guarantee is given for the completeness, correctness, or availability of the information provided. AllYouCanGET assumes no liability for any damages arising from the use of or inability to use the website or its contents, unless caused by gross negligence or intent.
Liability for indirect or consequential damages, including data loss or lost profits, is excluded.

8. Changes to the Website and Terms

AllYouCanGET may change, suspend, or discontinue any part of this website, or update these Terms of Use at any time without prior notice.
The current version published on the website applies.

9. Applicable Law and Jurisdiction

These Terms of Use are governed exclusively by Swiss law. The exclusive place of jurisdiction is Widen, Switzerland.


Last Update

Effective from 01.01.2025

Ready to Get Started?

Contact us today to learn more about how we can help you.

\ No newline at end of file diff --git a/html/index.html b/html/index.html new file mode 100644 index 0000000..6ad1f9a --- /dev/null +++ b/html/index.html @@ -0,0 +1 @@ +Transform Your Business

Streamline operations, optimize performance, and drive innovation with AllYouCanGET

IT Consulting IT Consulting

Strategic technology solutions to drive your digital transformation

✓ IT Strategy & Planning

A strategic view on business goals helps to align technology roadmaps accordingly We develop comprehensive IT strategies that align with your business goals and drive digital transformation

✓ Technology Assessment

We evaluate existing IT infrastructure and identify improvement areas A thorough assessment of your current technology landscape to identify strengths, weaknesses, and opportunities for improvement

✓ Enterprise Architecture

We design and implement scalable IT architectures that align with business objectives Our enterprise architecture services help you design and implement scalable IT architectures that align with your business objectives

✓ Cybersecurity

Protect your digital assets! We guide you through comprehensive security strategies Our cybersecurity services help you protect your digital assets and ensure compliance with industry standards

Management Consulting Management Consulting

Optimize operations and accelerate business growth

✓ Strategy Development

We develop comprehensive strategies to drive business success Tailored strategies that align with your business goals and drive growth

✓ Process Optimization

We improve efficiency by streamlining operations and reducing costs Our process optimization services help you identify bottlenecks, eliminate waste, and enhance productivity

✓ Change Management

We implement effective change management strategies to ensure smooth transitions Our change management services help you navigate organizational change and ensure successful adoption of new initiatives

✓ Financial Analysis

We provide data-driven insights for informed financial decision-making Our financial analysis services help you make informed decisions based on data-driven insights

Infrastructure Services Infrastructure Services

Build robust foundations for your business success

✓ Cloud Solutions

Build scalable and flexible cloud infrastructures tailored to your needs We design and implement scalable and flexible cloud infrastructures that meet your business needs

✓ Network Design

We design and implement robust network architectures to ensure seamless connectivity Our network design services help you create robust network architectures that ensure seamless connectivity and optimal performance

✓ System Integration

We integrate diverse systems and applications to streamline processes and enhance data flow Our system integration services help you connect disparate systems and applications to streamline processes and enhance data flow

✓ Security Implementation

We implement comprehensive security measures to protect your infrastructure Our security implementation services help you safeguard your infrastructure against cyber threats and ensure compliance with industry standards

Our Partners

We collaborate with leading technology providers to deliver the best solutions for your business.

IBMMicrosoftAWSGoogleOracleInformaticaSAPSalesforce

Projects

Open Mic Odyssey

Website for a documentary film project about three best friends embarking on an outrageous adventure. Open Mic Odyssey

Bobby Ludlam

A landing page for a stand-up comedian, artist and entrepeneur. Bobby Ludlam

William Montgomery

Wordpress-based website for a stand-up comedian. William Montgomery

Ready to Get Started?

Contact us today to learn more about how we can help you.

\ No newline at end of file diff --git a/html/js/contact.js b/html/js/contact.js new file mode 100644 index 0000000..98d39d1 --- /dev/null +++ b/html/js/contact.js @@ -0,0 +1 @@ +document.getElementById('contact-form').addEventListener('submit', function(event) { event.preventDefault(); const formData = new FormData(event.target); fetch(event.target.action, { method: event.target.method, body: formData, }) .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }) .then(data => { console.log('Success:', data); }) .catch((error) => { console.error('Error:', error); }); }); \ No newline at end of file diff --git a/html/js/humansnotbots.js b/html/js/humansnotbots.js new file mode 100644 index 0000000..0a037ae --- /dev/null +++ b/html/js/humansnotbots.js @@ -0,0 +1 @@ +function HumansNotBots(replacement_method, strAT, strDOT) { if (document.getElementsByTagName) { if (strAT == null || strDOT == null) { strAT = "AT"; strDOT = "DOT"; } var disguisedForm = "([a-zA-Z0-9._%+-]+)\\s" + strAT + "\\s([a-zA-Z0-9.-]+)\\s" + strDOT + "\\s(co.uk|[a-zA-Z]{2,4})"; var htmlbody = document.getElementsByTagName("body")[0]; if (replacement_method == "innerhtml") HumansNotBots_innerhtml(htmlbody, disguisedForm); else if (replacement_method == "dom") HumansNotBots_dom(htmlbody, disguisedForm); } } function HumansNotBots_innerhtml(htmlbody, disguisedForm) { var matchexp = new RegExp(disguisedForm, "g"); var replacement = '$1@$2.$3'; var newInnerHTML = htmlbody.innerHTML.replace(matchexp, replacement); htmlbody.innerHTML = newInnerHTML; } function HumansNotBots_dom(element, disguisedForm) { if (element) { if (element.hasChildNodes()) { var child = element.firstChild; while (child) { HumansNotBots_dom(child, disguisedForm); child = child.nextSibling; } } else if (element.nodeValue) { var matchexp = new RegExp(disguisedForm, "g"); var disguisedEmailAddresses = element.nodeValue.match(matchexp); if (disguisedEmailAddresses !== null) { var unprocessedText = element.nodeValue; var newParentsChildren = new Array(); for (var i = 0; i < disguisedEmailAddresses.length; i++) { var disguisedEmailAddress = disguisedEmailAddresses[i]; var textBefore = unprocessedText.substring( 0, unprocessedText.search(new RegExp(disguisedEmailAddress)) ); if (textBefore) { newParentsChildren.push(document.createTextNode(textBefore)); } var matchexp1 = new RegExp(disguisedForm); var realEmailAddress = disguisedEmailAddress.replace( matchexp1, "$1@$2.$3" ); var a = document.createElement("a"); a.setAttribute("href", "mailto:" + realEmailAddress); a.appendChild(document.createTextNode(realEmailAddress)); newParentsChildren.push(a); unprocessedText = unprocessedText.substring( textBefore.length + disguisedEmailAddress.length ); } if (unprocessedText) { newParentsChildren.push(document.createTextNode(unprocessedText)); } var parent = element.parentNode; var child = newParentsChildren.shift(); while (child) { parent.insertBefore(child, element); child = newParentsChildren.shift(); } parent.removeChild(element); } } } } \ No newline at end of file diff --git a/html/js/nav.js b/html/js/nav.js new file mode 100644 index 0000000..7433121 --- /dev/null +++ b/html/js/nav.js @@ -0,0 +1 @@ +document.addEventListener("DOMContentLoaded", function () { const hamburger = document.querySelector(".hamburger-menu"); const navMenu = document.querySelector(".nav-menu"); if (!hamburger || !navMenu) { return; } hamburger.addEventListener("click", function () { hamburger.classList.toggle("open"); navMenu.classList.toggle("open"); }); document.addEventListener("click", function (event) { if (!hamburger.contains(event.target) && !navMenu.contains(event.target)) { hamburger.classList.remove("open"); navMenu.classList.remove("open"); } }); navMenu.addEventListener("click", function (event) { if (event.target.tagName === "A") { hamburger.classList.remove("open"); navMenu.classList.remove("open"); } }); }); \ No newline at end of file diff --git a/html/js/partner.js b/html/js/partner.js new file mode 100644 index 0000000..828c48b --- /dev/null +++ b/html/js/partner.js @@ -0,0 +1 @@ +function partnerRotate(interval = 3000) { const partners = document.querySelectorAll("img[src*='img/partner/']"); for (let i = 1; i < partners.length; i++) { partners[i].classList.add("hidden"); } let currentIndex = 0; const totalPartners = partners.length; setInterval(() => { partners[currentIndex].classList.add("hidden"); currentIndex = (currentIndex + 1) % totalPartners; partners[currentIndex].classList.remove("hidden"); }, interval); } window.onload = function () { partnerRotate(5000); }; \ No newline at end of file diff --git a/html/php.ini b/html/php.ini new file mode 100644 index 0000000..a082cb4 --- /dev/null +++ b/html/php.ini @@ -0,0 +1,20 @@ +; cPanel-generated php ini directives, do not edit +; Manual editing of this file may result in unexpected behavior. +; To make changes to this file, use the cPanel MultiPHP INI Editor (Home >> Software >> MultiPHP INI Editor) +; For more information, read our documentation (https://go.cpanel.net/EA4ModifyINI) + +allow_url_fopen = On +allow_url_include = Off +asp_tags = Off +display_errors = Off +enable_dl = Off +file_uploads = On +max_execution_time = 30 +max_input_time = 60 +max_input_vars = 1000 +memory_limit = 64M +post_max_size = 16M +session.gc_maxlifetime = 1440 +session.save_path = "/var/cpanel/php/sessions/ea-php56" +upload_max_filesize = 16M +zlib.output_compression = Off diff --git a/html/robots.txt b/html/robots.txt new file mode 100644 index 0000000..5e4ff3e --- /dev/null +++ b/html/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: \ No newline at end of file diff --git a/html/services.html b/html/services.html new file mode 100644 index 0000000..c844e0b --- /dev/null +++ b/html/services.html @@ -0,0 +1 @@ +AllYouCanGET Services

Our Services

IT Consulting IT Consulting

Comprehensive IT solutions to drive digital transformation and optimize your technology infrastructure.

IT Strategy & Planning IT Strategy & Planning

Develop technology roadmaps aligned with business objectives

Infrastructure Management Infrastructure Management

Design and implement robust IT infrastructure solutions

Cybersecurity Cybersecurity

Protect your assets with advanced security measures

Management Consulting Management Consulting

Strategic business solutions to improve efficiency and drive growth.

Process Optimization Process Optimization

Streamline operations and improve business efficiency

Change Management Change Management

Guide organizational transformation effectively

Financial Analysis Financial Analysis

Data-driven financial planning and optimization

Infrastructure Services Infrastructure Services

Build robust foundations for your business success

Cloud Solutions Cloud Solutions

Leverage cloud technology for scalable solutions

Network Design Network Design

Create secure and efficient network architectures

System Integration System Integration

Seamlessly integrate systems for optimal performance

Need Customized Solutions?

Let's discuss how we can help transform your business

Ready to Get Started?

Contact us today to learn more about how we can help you.

\ No newline at end of file diff --git a/html/signature.html b/html/signature.html new file mode 100644 index 0000000..cc56fc5 --- /dev/null +++ b/html/signature.html @@ -0,0 +1,122 @@ + + + + + + + + Email Signature - AllYouCanGET + + + + +
+
+ +
+
+
Georg Sinn
+
IT & Management Consulting
+ +
+
All You Can GET
+
c/o Sinn Consulting
+
Im Dornäcker 16
+
8967 Widen
+
Switzerland
+
+
+
+ + + \ No newline at end of file diff --git a/html/terms_and_conditions.html b/html/terms_and_conditions.html new file mode 100644 index 0000000..4e4841a --- /dev/null +++ b/html/terms_and_conditions.html @@ -0,0 +1 @@ +Terms and Conditions

General Terms and Conditions (GTC)

1. Scope of Application

These General Terms and Conditions (GTC) apply to all services provided by AllYouCanGET – Sinn Consulting (hereinafter “the Company”) to its clients, whether private individuals or businesses. By commissioning the Company, the client accepts these GTC in full.

2. Services

The Company provides services in the areas of business consulting, creative development, digital marketing, coaching, and technical services. The specific scope, content, and objectives of a service are agreed upon individually in writing or electronically.

3. Offers and Agreements

All offers are non-binding until confirmed by the Company in writing. Agreements, changes, or additional services are valid only with written confirmation.

4. Prices and Payment Terms

Unless otherwise agreed, prices are stated in Swiss francs (CHF) and exclude VAT (if applicable). Invoices are issued after completion of the agreed service and are payable within 30 days from the invoice date without deductions. In case of late payment, the Company reserves the right to charge reminder fees and default interest.

5. Cancellations and Refunds

Cancellations by the client must be made in writing. - Up to 7 days before the service date: partial refund (50%) of the agreed amount. - Less than 7 days before the service date: no refund possible. The Company reserves the right to cancel or postpone services for important reasons (e.g. illness, unforeseen events), in which case a new date will be arranged or any payments already made will be refunded.

6. Liability

The Company is liable only for intentional or grossly negligent actions. Liability for slight negligence, indirect or consequential damages, lost profits, or data loss is excluded. The client remains responsible for the decisions and results derived from the consulting services.

7. Intellectual Property

All materials, concepts, designs, and documentation created by the Company remain the property of the Company until full payment has been received. Unless otherwise agreed, the Company retains all copyrights and usage rights to its materials, which may not be copied, shared, or used for other purposes without prior written consent.

8. Confidentiality

The Company treats all information and data provided by clients as strictly confidential. This obligation continues after the business relationship ends. The client likewise undertakes to maintain confidentiality regarding all information obtained about the Company.

9. Data Protection

Personal data is collected and processed solely for the purpose of providing and managing services. Data is not shared with third parties without consent, except where legally required.

10. Applicable Law and Jurisdiction

These GTC and all contractual relationships between the Company and the client are governed exclusively by Swiss law. The exclusive place of jurisdiction is Widen, Switzerland.


Last Update

Effective from 01.01.2025

Ready to Get Started?

Contact us today to learn more about how we can help you.

\ No newline at end of file diff --git a/html/webservices.html b/html/webservices.html new file mode 100644 index 0000000..d5935c2 --- /dev/null +++ b/html/webservices.html @@ -0,0 +1 @@ +Web Services Solutions

Web Services – AllYouCanGET

Web Hosting Managed Hosting Solutions

Enjoy peace of mind with our fully managed hosting services. We handle maintenance, updates, backups, and security — so you can focus on your business.

Managed CMS Hosting (PHP-based)

Ideal for WordPress and similar CMS platforms. - Daily backups
- System and plugin updates
- Security screening and monitoring
- Starting from 25 CHF / month (incl. VAT)

Managed Application Hosting

(Node.js / Python) Perfect for modern web applications and APIs. - Daily backups
- Framework and dependency updates
- Security screening and optimization
- Starting from 25 CHF / month (incl. VAT)

Managed Docker Hosting

Flexible and containerized environments for developers. - Daily backups
- Security screening and resource monitoring
- Starting from 25 CHF / month (incl. VAT)

Web Packages Hosting Packages

Simple, transparent, and scalable hosting plans with Swiss reliability.

Basic Hosting

  • 2 GB Diskspace
  • 1 Domain
  • Unlimited Emails
  • 1 GB RAM
  • SSH Access
  • 10 CHF / month (incl. VAT)

Standard Hosting

  • 5 GB Diskspace
  • 1 Domain
  • Unlimited Emails
  • 2 GB RAM
  • SSH Access
  • 25 CHF / month (incl. VAT)

Premium Hosting

  • 10 GB Diskspace
  • 1 Domain
  • Unlimited Emails
  • 4 GB RAM
  • SSH Access
  • 50 CHF / month (incl. VAT)

Why Choose Us Why Choose AllYouCanGET?

Reliable Swiss Hosting

  • Swiss data protection and hosting standards
  • 24/7 monitoring and proactive security
  • Daily backups included in all plans
  • Scalable resources for growing businesses
  • Expert technical support from experienced professionals

Seamless Migration

  • Free migration assistance for new customers
  • Minimal downtime during transition
  • Comprehensive support throughout the migration process

Custom Solutions

  • Tailored hosting environments to fit your needs
  • Flexible resource allocation and scaling options
  • Expert advice on architecture and optimization
  • Integration with third-party services and tools
  • Performance tuning for optimal speed and reliability

Ready to Get Started?

Let’s set up your hosting today — secure, managed, and built for growth.

Ready to Get Started?

Contact us today to learn more about how we can help you.

\ No newline at end of file diff --git a/lib/__init__.py b/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/markdown_parser.py b/lib/markdown_parser.py new file mode 100644 index 0000000..42e5eb8 --- /dev/null +++ b/lib/markdown_parser.py @@ -0,0 +1,398 @@ +""" +Markdown parser for converting Markdown files into structured component data. + +This module reads Markdown files and returns a structured representation that maps +heading levels to component types: + - H1 (#) -> page title / hero + - H2 (##) -> major sections + - H3 (###) -> cards or subsections within sections + - Lists -> converted to component-compatible format +""" + +import os +import re +import textwrap +import markdown +from bs4 import BeautifulSoup +from markdown.treeprocessors import Treeprocessor +from markdown.preprocessors import Preprocessor +from markdown.extensions import Extension +from typing import Dict, List, Any, Optional, cast +from lib.types import PageData, Section, Card, Detail, ParserState + + +class HeadingCollector(Treeprocessor): + """ + Custom Markdown tree processor that collects headings and their content. + """ + + def __init__(self, md: Any) -> None: + super().__init__(md) + self.headings: List[Dict[str, Any]] = [] + self.current_content: List[str] = [] + self.main_intro: str = '' + + def run(self, root: Any) -> Any: + """Process the element tree and collect headings with content.""" + self.headings = [] + self.main_intro = '' + collecting_intro = False + for element in root: + if element.tag == 'h1': + collecting_intro = True + elif collecting_intro and element.tag in ['h2', 'h3', 'h4', 'h5', 'h6']: + collecting_intro = False + elif collecting_intro: + intro_text = self._extract_text(element) + if intro_text: + if self.main_intro: + self.main_intro += '\n\n' + intro_text + else: + self.main_intro = intro_text + self._process_element(element) + return root + + def _process_element(self, element: Any) -> None: + """Recursively process elements to extract heading structure.""" + if element.tag in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']: + # Convert heading tag to level (e.g., 'h2' -> 2) + level = int(element.tag[1]) + text = self._extract_text(element) + self.headings.append({ + 'level': level if level > 1 else 2, # Treat H1 as level 2 for sectioning + 'text': text, + 'tag': element.tag, + 'element': element, + }) + elif element.tag in ['ul', 'ol']: + # Extract list items + items: List[str] = [] + for li in element: + items.append(self._extract_text(li)) + self.headings.append({ + 'type': element.tag, + 'items': items, + 'element': element, + }) + else: + # Process children + for child in element: + self._process_element(child) + + def _extract_text(self, element: Any) -> str: + """Extract all text from an element and its children.""" + if element.text: + text = element.text + else: + text = '' + for child in element: + text += self._extract_text(child) + if child.tail: + text += child.tail + return text.strip() + + +class DedentPreprocessor(Preprocessor): + """Normalize leading indentation so headings aren't treated as code blocks.""" + + def run(self, lines: List[str]) -> List[str]: + text = '\n'.join(lines) + dedented = textwrap.dedent(text) + return dedented.split('\n') + + +class HeadingExtension(Extension): + """Markdown extension to collect headings.""" + + def extendMarkdown(self, md: Any) -> None: + md.preprocessors.register( + DedentPreprocessor(md), 'dedent_preprocessor', 27) + md.treeprocessors.register( + HeadingCollector(md), 'heading_collector', 5) + + +def parse_markdown_file(file_path: str) -> PageData: + """ + Parse a Markdown file and return a structured representation. + + Args: + file_path (str): Path to the Markdown file to parse. + + Returns: + dict: A nested dictionary representing the page structure. + + Raises: + FileNotFoundError: If the file does not exist. + IOError: If there is an error reading the file. + """ + if not os.path.exists(file_path): + raise FileNotFoundError(f"Markdown file not found: {file_path}") + + with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Parse the content to extract structure + return build_component_structure(content, file_path) + + +def get_markdown_files(docs_dir: str = 'docs/en') -> List[str]: + """Return Markdown filenames from the given docs directory.""" + if not os.path.exists(docs_dir): + return [] + return [f for f in os.listdir(docs_dir) if f.endswith('.md')] + + +def markdown_filename_to_html_filename(md_filename: str) -> str: + """Convert a Markdown filename to its HTML counterpart.""" + return md_filename.replace('.md', '.html').lower() + + +def build_component_structure( + markdown_content: str, file_path: str) -> PageData: + """ + Build a nested component structure from Markdown content. + + This function parses Markdown headings and content into a hierarchical structure + suitable for rendering with component templates. + """ + lines = markdown_content.split('\n') + page: PageData = { + 'title': None, + 'sections': [], + } + current_section: Optional[Section] = None + current_card: Optional[Card] = None + current_detail: Optional[Detail] = None + content_buffer: List[str] = [] + detail_buffer: List[str] = [] + + # Move local state into a dict so module-level helpers can operate on it + state: ParserState = { + 'page': page, + 'current_section': current_section, + 'current_card': current_card, + 'current_detail': current_detail, + 'content_buffer': content_buffer, + 'detail_buffer': detail_buffer, + } + + for line in lines: + process_line_with_state(line, state) + + # Rehydrate locals from state + current_section = state['current_section'] + current_card = state['current_card'] + current_detail = state['current_detail'] + content_buffer = state['content_buffer'] + detail_buffer = state['detail_buffer'] + + # Flush remaining content + flush_detail_buffer(state) + # Flush content buffer to card or section as appropriate + if current_card is not None and content_buffer: + current_card['content'] = markdown_to_html_lines( + '\n'.join(content_buffer).strip()) + elif current_section is not None and content_buffer: + current_section['content'] = markdown_to_html_lines( + '\n'.join(content_buffer).strip()) + + if page['title'] is None: + filename = os.path.basename(file_path) + page['title'] = os.path.splitext(filename)[0].replace('_', ' ').title() + + return page + + +def flush_detail_buffer(state: ParserState) -> None: + """Flush the detail buffer into the current detail entry.""" + current_detail = state['current_detail'] + detail_buffer = state['detail_buffer'] + if current_detail is not None and detail_buffer: + current_detail['content'] = markdown_to_html_lines( + '\n'.join(detail_buffer).strip()) + state['detail_buffer'] = [] + + +def flush_content_buffer_to_card(state: ParserState) -> None: + """Flush the content buffer into the current card.""" + current_card = state['current_card'] + content_buffer = state['content_buffer'] + if current_card is not None and content_buffer: + current_card['content'] = markdown_to_html_lines( + '\n'.join(content_buffer).strip()) + + +def flush_content_buffer_to_section(state: ParserState) -> None: + """Flush the content buffer into the current section.""" + current_section = state['current_section'] + content_buffer = state['content_buffer'] + if current_section is not None and content_buffer: + current_section['content'] = markdown_to_html_lines( + '\n'.join(content_buffer).strip()) + + +def start_section(title: str, state: ParserState) -> None: + """Start a new section with the given title.""" + if state['current_card'] is not None: + flush_content_buffer_to_card(state) + state['content_buffer'] = [] + state['current_card'] = None + if state['current_section'] is not None: + flush_content_buffer_to_section(state) + section = cast(Section, { + 'title': title, + 'content': '', + 'cards': [] + }) + sections = state['page'].get('sections', []) + sections.append(section) + state['page']['sections'] = sections + state['current_section'] = section + state['content_buffer'] = [] + state['current_card'] = None + + +def start_card(title: str, state: ParserState) -> None: + """Start a new card within the current section.""" + if state['current_section'] is None: + return + if state['current_card'] is None: + flush_content_buffer_to_section(state) + else: + flush_content_buffer_to_card(state) + card = cast(Card, { + 'title': title, + 'content': '' + }) + state['current_section']['cards'].append(card) + state['current_card'] = card + state['content_buffer'] = [] + + +def start_detail(title: str, state: ParserState) -> None: + """Start a new detail within the current card.""" + if state['current_card'] is None: + return + if state['current_detail'] is not None and state['detail_buffer']: + state['current_detail']['content'] = markdown_to_html_lines( + '\n'.join(state['detail_buffer']).strip()) + state['detail_buffer'] = [] + detail = cast(Detail, { + 'title': title, + 'content': '' + }) + if 'details' not in state['current_card']: + state['current_card']['details'] = [] + state['current_card']['details'].append(detail) + state['current_detail'] = detail + + +def process_line_with_state(line: str, state: ParserState) -> None: + """Process a single markdown line, updating the provided state dict.""" + if line.startswith('# '): + # H1 - page title + state['page']['title'] = line[2:].strip() + # intro content before sections + if state['current_section'] is not None and state['content_buffer']: + flush_content_buffer_to_section(state) + state['current_section'] = None + elif line.startswith('## '): + # H2 - major section + flush_detail_buffer(state) + title = line[3:].strip() + title = check_image_in_title(title) + start_section(title, state) + elif line.startswith('### '): + # H3 - card or subsection + flush_detail_buffer(state) + title = line[4:].strip() + title = check_image_in_title(title) + start_card(title, state) + elif line.startswith('#### '): + # H4 - detail inside a card + if state['current_card'] is not None: + title = line[5:].strip() + title = check_image_in_title(title) + start_detail(title, state) + else: + state['content_buffer'].append(line) + elif line.strip(): + if state['current_detail'] is not None: + state['detail_buffer'].append(line) + else: + state['content_buffer'].append(line) + + +def check_image_in_title(title: str) -> str: + """ + Check if there is an image in the title, preserve original title text and create HTML img tag with alt text and src. + """ + img_pattern = r'!\[([^\]]*)\]\(([^)]+)\)' + match = re.search(img_pattern, title) + if match: + alt_text = match.group(1).strip() + src = match.group(2).strip() + img_tag = f'{alt_text}' + title = re.sub(img_pattern, img_tag, title).strip() + return title + + +def markdown_to_html_lines(text: str) -> str: + """ + Convert Markdown text to HTML. + """ + if not text: + return '' + + md = markdown.Markdown() + html = md.convert(text) + + def _is_unsafe(href: str) -> bool: + lower = href.strip().lower() + return lower.startswith('javascript:') or lower.startswith( + 'data:') or lower.startswith('vbscript:') + + soup = BeautifulSoup(html, 'html.parser') + + for anchor in soup.find_all('a'): + href = anchor.get('href') + if isinstance(href, (list, tuple)): + href = href[0] + href = (href or '').strip() + if not href: + continue + if _is_unsafe(href): + anchor['href'] = '#unsafe' + anchor.attrs.pop('target', None) + anchor.attrs.pop('rel', None) + continue + if href.startswith('http://') or href.startswith('https://'): + anchor['target'] = '_blank' + anchor['rel'] = 'noopener noreferrer' + + for image in soup.find_all('img'): + src = image.get('src') or '' + if isinstance(src, (list, tuple)): + src = src[0] + src = src.strip() + if not src: + image.decompose() + continue + + alt = image.get('alt') + if isinstance(alt, (list, tuple)): + alt = alt[0] + alt_text = (alt or '').strip() + # Determine final src for relative paths + if not (src.startswith('http://') or src.startswith('https://') + or src.startswith('/') or src.startswith('img/')): + filename = os.path.basename(src) + src = f'img/{filename}' if filename else src + image['src'] = src + + if not alt_text: + alt_text = os.path.splitext(os.path.basename(src))[ + 0].replace('-', ' ').replace('_', ' ').strip() + image['alt'] = alt_text + + return str(soup) diff --git a/lib/types.py b/lib/types.py new file mode 100644 index 0000000..17b542a --- /dev/null +++ b/lib/types.py @@ -0,0 +1,54 @@ +from typing import TypedDict, List, Dict, Optional + + +class Detail(TypedDict): + title: str + content: str + + +class Card(TypedDict, total=False): + title: str + content: str + details: List[Detail] + + +class Section(TypedDict): + title: str + content: str + cards: List[Card] + + +class PageData(TypedDict): + title: Optional[str] + sections: List[Section] + + +class PageMeta(TypedDict, total=False): + title: str + description: str + keywords: str + og_description: str + favicon: str + twitter_image: str + og_image: str + + +class PageEntry(TypedDict, total=False): + page_title: str + page_subtitle: str + page_cta: str + page_cta_url: str + meta: PageMeta + active: bool + + +PagesDict = Dict[str, PageEntry] + + +class ParserState(TypedDict): + page: PageData + current_section: Optional[Section] + current_card: Optional[Card] + current_detail: Optional[Detail] + content_buffer: List[str] + detail_buffer: List[str] diff --git a/lib/utils.py b/lib/utils.py new file mode 100644 index 0000000..cca586b --- /dev/null +++ b/lib/utils.py @@ -0,0 +1,126 @@ +import os +import re +from typing import Dict, List, Any +from jinja2 import Environment, FileSystemLoader +from lib.types import PagesDict + +DEFAULT_TEMPLATE_DIR = 'templates' +DEFAULT_OUTPUT_DIR = 'html' + + +def get_template_files(template_dir: str = DEFAULT_TEMPLATE_DIR) -> List[str]: + """Return template filenames from the provided template directory.""" + if not os.path.exists(template_dir): + return [] + return [ + name + for name in os.listdir(template_dir) + if name.endswith('.html') and not name.startswith('_') + ] + + +def get_css_files(output_dir: str = DEFAULT_OUTPUT_DIR) -> List[str]: + """Return CSS file paths contained in the output directory's css folder.""" + css_dir = os.path.join(output_dir, 'css') + if not os.path.exists(css_dir): + return [] + return [ + os.path.join(css_dir, name) + for name in os.listdir(css_dir) + if name.endswith('.css') + ] + + +def get_js_files(output_dir: str = DEFAULT_OUTPUT_DIR) -> List[str]: + """Return JavaScript file paths contained in the output directory's js folder.""" + js_dir = os.path.join(output_dir, 'js') + if not os.path.exists(js_dir): + return [] + return [ + os.path.join(js_dir, name) + for name in os.listdir(js_dir) + if name.endswith('.js') + ] + + +def render_template( + template_name: str, + context: Dict[str, Any], + template_dir: str = DEFAULT_TEMPLATE_DIR) -> str: + """Render a Jinja2 template with the provided context.""" + env = Environment(loader=FileSystemLoader(template_dir)) + template = env.get_template(template_name) + return template.render(context) + + +def set_active_page_by_url(pages: PagesDict, page_url: str) -> None: + """Mutate navigation dictionary to mark the active page.""" + for key, value in pages.items(): + # value is a PageEntry TypedDict + value['active'] = key == page_url + + +def minify_js(js: str) -> str: + """Minify JavaScript by removing comments and redundant whitespace.""" + js = re.sub(r'//.*?\n|/\*.*?\*/', '', js, flags=re.DOTALL) + js = re.sub(r'\s+', ' ', js) + js = re.sub(r';\s+', ';\n', js) + js = re.sub(r'\n+', ' ', js) + return js.strip() + + +def minify_css(css: str) -> str: + """Minify CSS by removing comments and redundant whitespace.""" + css = re.sub(r'/\*.*?\*/', '', css, flags=re.DOTALL) + css = re.sub(r'\s+', ' ', css) + css = re.sub(r';\s+', ';\n', css) + css = re.sub(r'\s+([\{\s])', r'\1', css) + css = re.sub(r'\s+}', '}', css) + css = re.sub(r'\n+', ' ', css) + return css.strip() + + +def minify_html(html: str) -> str: + """Minify HTML by removing comments and redundant whitespace.""" + html = re.sub(r'', '', html, flags=re.DOTALL) + html = re.sub(r'\s+', ' ', html) + html = re.sub(r'>\s+<', '><', html) + html = re.sub(r'<\s+', '<', html) + html = re.sub(r'\s+ None: + """Minify all CSS files within the output directory.""" + for css_path in get_css_files(output_dir): + with open(css_path, 'r', encoding='utf-8') as handle: + content = handle.read() + minified = minify_css(content) + with open(css_path, 'w', encoding='utf-8') as handle: + handle.write(minified) + + +def js_minifier(output_dir: str = DEFAULT_OUTPUT_DIR) -> None: + """Minify all JavaScript files within the output directory.""" + for js_path in get_js_files(output_dir): + with open(js_path, 'r', encoding='utf-8') as handle: + content = handle.read() + minified = minify_js(content) + with open(js_path, 'w', encoding='utf-8') as handle: + handle.write(minified) + + +__all__ = [ + 'DEFAULT_OUTPUT_DIR', + 'DEFAULT_TEMPLATE_DIR', + 'get_template_files', + 'get_css_files', + 'get_js_files', + 'render_template', + 'set_active_page_by_url', + 'minify_js', + 'minify_css', + 'minify_html', + 'css_minifier', + 'js_minifier', +] diff --git a/main.py b/main.py new file mode 100644 index 0000000..40d9350 --- /dev/null +++ b/main.py @@ -0,0 +1,189 @@ +import json +import os +from typing import Any, Dict +from lib.markdown_parser import ( + parse_markdown_file, + get_markdown_files, + markdown_filename_to_html_filename, +) +from lib.utils import ( + get_template_files, + render_template, + set_active_page_by_url, + minify_html, + js_minifier, +) +from lib.types import PagesDict + +TEMPLATE_DIR = 'templates' +OUTPUT_DIR = 'html' + +PAGES_JSON = 'docs/pages.json' + +# Load json data for navigation and pages if needed +with open(PAGES_JSON, 'r') as f: + PAGES = json.load(f) + + +# Ensure output directory exists +if not os.path.exists(OUTPUT_DIR): + os.makedirs(OUTPUT_DIR) + + +def get_pages() -> PagesDict: + """Load and return pages data from the pages.json file. + + Returns: + dict: Pages dictionary structured as follows: + { + "page.html": { + "name": str, + "url": str, + "active": str, + "page_title": str, + "page_subtitle": str, + "page_cta": str, + "page_cta_url": str, + "meta": { + "title": str, + "description": str, + "keywords": str, + "og_description": str, + } + }, + {...} + } + """ + # Set a default favicon for all pages + favicon = 'https://allucanget.biz/img/cropped-Logo-AllYouCanGet-512x512-1-180x180.png' + with open(PAGES_JSON, 'r', encoding='utf-8') as f: + pages = json.load(f) + + # Update the meta information for each page + for page in pages.values(): + page['meta']['favicon'] = favicon + page['meta']['og_image'] = favicon + page['meta']['twitter_image'] = favicon + return pages + + +def process_markdown_file( + md_file_path: str, + output_html_path: str, + pages: PagesDict, + template_dir: str = TEMPLATE_DIR, +): + """ + Process a single Markdown file and render it using the page_from_md template. + + Args: + md_file_path (str): Full path to the Markdown file. + output_html_path (str): Full path to write the output HTML. + pages (dict): Pages dictionary for context. + """ + try: + # Parse the Markdown file + page_data = parse_markdown_file(md_file_path) + + # Get the HTML filename to determine active nav + html_filename = os.path.basename(output_html_path) + set_active_page_by_url(pages, html_filename) + + # Build default meta data for the page + default_meta: Dict[str, Any] = { + 'title': page_data.get('title', ''), + 'description': 'Explore our comprehensive services and coaching programs.', + 'keywords': 'consulting, coaching, services', + 'og_description': page_data.get('title', ''), + 'favicon': 'https://allucanget.biz/img/cropped-Logo-AllYouCanGet-512x512-1-180x180.png', + 'twitter_image': 'https://allucanget.biz/img/cropped-Logo-AllYouCanGet-512x512-1-180x180.png', + 'og_image': 'https://allucanget.biz/img/cropped-Logo-AllYouCanGet-512x512-1-180x180.png', + } + page = pages.get(html_filename, {}) + meta = page.get('meta', default_meta) + page_title = page.get('page_title', page_data.get('title', '')) + page_subtitle = page.get('page_subtitle', '') + + # Build context for rendering + context: Dict[str, Any] = { + 'nav_pages': pages, + 'page': page_data, + 'page_title': page_title, + 'page_subtitle': page_subtitle, + 'meta': meta, + 'page_url': html_filename, + 'page_cta': page.get('page_cta', 'Get Started'), + 'page_cta_url': page.get('page_cta_url', '#contact-form'), + } + + # Render the template and write the output + output = render_template( + 'page_from_md.html', context, template_dir=template_dir) + html = minify_html(output) + os.makedirs(os.path.dirname(output_html_path), exist_ok=True) + with open(output_html_path, 'w', encoding='utf-8') as f: + f.write(html) + + print(f'Processed Markdown: {md_file_path} -> {output_html_path}') + except Exception as e: + print(f'Error processing Markdown file {md_file_path}: {e}') + + +def main(): + """Main function to render templates with navigation context.""" + # Get all pages with their details + pages = get_pages() + if not pages: + print("No pages found to render.") + return + # Get navigation pages + templates = get_template_files(TEMPLATE_DIR) + if not templates: + print("No templates found to render.") + return + print(f'Found {len(templates)} templates to process.') + + # # Process each template + # for template_name in templates: + # # Skip templates that are not in the pages dictionary + # if template_name not in pages: + # print(f'Skipping template: {template_name}') + # continue + # print(f'Processing template: {template_name}') + # # Set the active page based on the template name + # set_active_page_by_url(pages, template_name) + # page_details: PageEntry = pages[template_name] + # context = { + # 'nav_pages': pages, + # 'page_title': page_details.get('page_title', ''), + # 'page_subtitle': page_details.get('page_subtitle', ''), + # 'page_cta': page_details.get('page_cta', ''), + # 'page_cta_url': page_details.get('page_cta_url', ''), + # 'meta': page_details.get('meta', {}), + # } + + # output = render_template( + # template_name, context, template_dir=TEMPLATE_DIR) + # html = minify_html(output) + # with open(f'{OUTPUT_DIR}/{template_name}', 'w', encoding='utf-8') as f: + # f.write(html) + + # Process Markdown files from docs/en/ + print("\nProcessing Markdown files...") + markdown_files = get_markdown_files() + for md_file in markdown_files: + md_path = os.path.join('docs/en', md_file) + html_filename = markdown_filename_to_html_filename(md_file) + html_path = os.path.join(OUTPUT_DIR, html_filename) + process_markdown_file(md_path, html_path, pages, + template_dir=TEMPLATE_DIR) + + # Minify all CSS and JS files in the output directory. + print("\nMinifying CSS and JS files...") + # css_minifier() + js_minifier(OUTPUT_DIR) + print("Build complete!") + + +if __name__ == '__main__': + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..000e197 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,10 @@ +jinja2 +lib +markdown +pytest +beautifulsoup4 +pytest +pytest-cov +autopep8 +mypy +flake8 \ No newline at end of file diff --git a/templates/_base.html b/templates/_base.html new file mode 100644 index 0000000..a019992 --- /dev/null +++ b/templates/_base.html @@ -0,0 +1,17 @@ + + + {% include "_head.html" %} + + {% include "_header.html" %} +
+
+ {% block content %}{% endblock %} {%include + "_footer.html" %} +
+
+ {% block scripts %} + + {% endblock %} + + + diff --git a/templates/_footer.html b/templates/_footer.html new file mode 100644 index 0000000..5a6a0bc --- /dev/null +++ b/templates/_footer.html @@ -0,0 +1,9 @@ + diff --git a/templates/_head.html b/templates/_head.html new file mode 100644 index 0000000..004e4f6 --- /dev/null +++ b/templates/_head.html @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ page_title }} + diff --git a/templates/_header.html b/templates/_header.html new file mode 100644 index 0000000..3f478b5 --- /dev/null +++ b/templates/_header.html @@ -0,0 +1,56 @@ + diff --git a/templates/_nav.html b/templates/_nav.html new file mode 100644 index 0000000..63372f3 --- /dev/null +++ b/templates/_nav.html @@ -0,0 +1,22 @@ + diff --git a/templates/coaching.html b/templates/coaching.html new file mode 100644 index 0000000..ef5777b --- /dev/null +++ b/templates/coaching.html @@ -0,0 +1,498 @@ +{% extends "_base.html" %} {% block content %} +
+
+

Our Coaching Areas

+

+ Comprehensive coaching programs tailored to your professional and personal + growth +

+ + +
+
+ Career advice icon +

Career Advice

+
+

+ Navigate your career path with expert guidance tailored to your industry + and location. +

+ +
+
+

IT Career

+
    +
  • • Career paths in software development
  • +
  • • Technical vs management track
  • +
  • • Transitioning between specialties
  • +
+
+ +
+

Switzerland Specifics

+
    +
  • • Work culture and expectations
  • +
  • • Salary negotiations
  • +
  • • Work permits and regulations
  • +
+
+ +
+

Germany Specifics

+
    +
  • • Job market trends
  • +
  • • Industry hubs and opportunities
  • +
+
+ +
+

Remote Work

+
    +
  • • Building a remote work routine
  • +
  • • Communication strategies
  • +
+
+ +
+

Freelancing

+
    +
  • • Setting up as independent contractor
  • +
  • • Client acquisition and retention
  • +
  • • Pricing strategies and negotiation
  • +
+
+ +
+

Startups

+
    +
  • • Evaluating startup opportunities
  • +
  • • Equity considerations
  • +
  • • Growth expectations
  • +
+
+ +
+

Corporates

+
    +
  • • Navigating corporate structures
  • +
  • • Internal mobility
  • +
  • • Building influence
  • +
+
+ +
+

Consulting

+
    +
  • • Types of consulting roles
  • +
  • • Client management
  • +
  • • Balancing multiple projects
  • +
+
+
+
+ + +
+
+ Personal development icon +

Personal Development

+
+

+ Discover your values, strengths, and purpose to create a fulfilling life + and career. +

+ +
+
+

Values

+
    +
  • • Value identification exercises
  • +
  • • Aligning career with personal values
  • +
  • • Identity Workshop
  • +
+
+ +
+

Ikigai

+
    +
  • • Finding purpose through passion
  • +
  • • Mission, vocation, and profession
  • +
  • • Practical discovery exercises
  • +
+
+ +
+

Strengths

+
    +
  • • Assessment tools and frameworks
  • +
  • • Leveraging strengths professionally
  • +
+
+ +
+

Weaknesses

+
    +
  • • Growth mindset approaches
  • +
  • • Strategic improvement planning
  • +
+
+ +
+

Vision

+
    +
  • • Creating personal vision statements
  • +
  • • Long-term life planning
  • +
  • • Visualization techniques
  • +
+
+ +
+

Goals

+
    +
  • • SMART goal framework
  • +
  • • Balancing short and long-term objectives
  • +
+
+ +
+

Agile Self Organization

+
    +
  • • Personal Kanban implementation
  • +
  • • Quarterly planning and review cycles
  • +
  • • Agile methods for personal productivity
  • +
+
+
+
+ + +
+
+ Leadership coaching icon +

Leadership Coaching

+
+

+ Develop essential leadership skills to inspire teams and drive + organizational success. +

+ +
+
+

Leadership Styles

+
    +
  • • Management vs Leadership
  • +
  • • Situational leadership approaches
  • +
  • • Transformational vs transactional
  • +
+
+ +
+

Communication

+
    +
  • • Active listening techniques
  • +
  • • Delivering difficult messages
  • +
  • • Cross-cultural communication
  • +
+
+ +
+

Feedback

+
    +
  • • Structured feedback frameworks
  • +
  • • Creating feedback culture
  • +
  • • Growth-oriented approaches
  • +
+
+ +
+

Delegation

+
    +
  • • RACI and DACI models
  • +
  • • Identifying tasks to delegate
  • +
  • • Building team capacity
  • +
+
+ +
+

Decision Making

+
    +
  • • OODA loop, DECIDE model
  • +
  • • Data-driven decisions
  • +
  • • Managing decision paralysis
  • +
+
+ +
+

Conflict Management

+
    +
  • • Conflict resolution models
  • +
  • • Mediation techniques
  • +
  • • Transforming conflict to opportunity
  • +
+
+ +
+

Motivation

+
    +
  • • Intrinsic vs extrinsic motivation
  • +
  • • SCARF and Drive frameworks
  • +
  • • Tailoring to individual needs
  • +
+
+ +
+

Empathy & Compassion

+
    +
  • • Building empathetic leadership
  • +
  • • Compassion without compromising
  • +
+
+ +
+

Trust

+
    +
  • • Trust-building exercises
  • +
  • • Trust in remote teams
  • +
  • • Rebuilding broken trust
  • +
+
+ +
+

Psychological Safety

+
    +
  • • Assessment techniques
  • +
  • • Creating safety in teams
  • +
  • • Connection to innovation
  • +
+
+ +
+

Team Building

+
    +
  • • Tuckman's team formation
  • +
  • • Remote team activities
  • +
  • • Cross-functional dynamics
  • +
+
+ +
+

Agile Project Management

+
    +
  • • Project roles and responsibilities
  • +
  • • Agile hierarchies
  • +
  • • Balancing agility with governance
  • +
+
+
+
+ + +
+
+ Technical coaching icon +

Technical Coaching

+
+

+ Master technical skills and best practices in software development and + IT infrastructure. +

+ + +

+ Software and Web Development +

+
+
+

Scalability

+
    +
  • • Horizontal vs vertical scaling
  • +
  • • Distributed systems principles
  • +
  • • Load balancing strategies
  • +
+
+ +
+

Browser

+
    +
  • • Developer tools mastery
  • +
  • • Performance optimization
  • +
  • • Extension development
  • +
+
+
+ + +

Operating Systems

+
+
+

Linux

+
    +
  • • Debian, Ubuntu
  • +
  • • RedHat, Fedora, CentOS
  • +
  • • Arch, Manjaro
  • +
  • • SUSE, openSUSE
  • +
+
+ +
+

Windows

+
    +
  • • Windows 10 & 11
  • +
  • • Windows Server 2019
  • +
  • • Windows Server 2022
  • +
+
+ +
+

macOS

+
    +
  • • macOS basics
  • +
  • • System administration
  • +
  • • Shell commands
  • +
+
+
+ + +

Development Tools

+
+
+

Git

+
    +
  • • Advanced git workflows
  • +
  • • Git hooks and automation
  • +
  • • Collaboration strategies
  • +
+
+ +
+

Docker

+
    +
  • • Container optimization
  • +
  • • Docker Compose
  • +
  • • Kubernetes orchestration
  • +
+
+ +
+

Visual Studio Code

+
    +
  • • Extension ecosystem
  • +
  • • Customization and settings
  • +
  • • Productivity optimization
  • +
+
+
+
+
+
+ + +
+
+

Ready to Accelerate Your Growth?

+

+ Let's work together to unlock your full potential +

+ +
+
+{% endblock %} diff --git a/templates/components/card.html b/templates/components/card.html new file mode 100644 index 0000000..91e0b64 --- /dev/null +++ b/templates/components/card.html @@ -0,0 +1,17 @@ +
+

{{ card.title }}

+ {% if card.content %} +
{{ card.content | safe }}
+ {% endif %} {% if card.details %} +
+ {% for detail in card.details %} +
+

{{ detail.title }}

+ {% if detail.content %} +
{{ detail.content | safe }}
+ {% endif %} +
+ {% endfor %} +
+ {% endif %} +
diff --git a/templates/components/hero.html b/templates/components/hero.html new file mode 100644 index 0000000..6828bbd --- /dev/null +++ b/templates/components/hero.html @@ -0,0 +1,8 @@ +
+
+

{{ page.title }}

+ {% if page.subtitle %} +

{{ page.subtitle }}

+ {% endif %} +
+
diff --git a/templates/components/section.html b/templates/components/section.html new file mode 100644 index 0000000..83a5786 --- /dev/null +++ b/templates/components/section.html @@ -0,0 +1,13 @@ +
+
+

{{ section.title }}

+ {% if section.content %} +
{{ section.content | safe }}
+ {% endif %} {% if section.cards %} +
+ {% for card in section.cards %} {% include 'components/card.html' %} {% + endfor %} +
+ {% endif %} +
+
diff --git a/templates/imprint.html b/templates/imprint.html new file mode 100644 index 0000000..e4442ff --- /dev/null +++ b/templates/imprint.html @@ -0,0 +1,238 @@ +{% extends "_base.html" %}{% block content %} +
+
+

Imprint

+
+
+ Shield icon +

AllYouCanGET - Sinn Consulting

+
+

Impressum

+

+ gemäß §5 Telemediengesetz bzw. § 5 Abs. 1 E-Commerce-Gesetz bzw. Art. 3 + des Bundesgesetzes gegen den unlauteren Wettbewerb (UWG) +

+
+
+

+ All You Can GET
c/o Sinn Consulting
Im Dornäcker + 16
CH-8967 Widen
georg AT allucanget DOT biz +

+
+
+
+

Overview

+

+ The following are the terms of an agreement between you and + AllYouCanGET. By accessing, or using this Web site, you acknowledge that + you have read, understand, and agree to be bound by these terms and to + comply with all applicable laws and regulations, including export and + re-export control laws and regulations. If you do not agree to these + terms, please do not use this Web site. +

+

+ AllYouCanGET may, without notice to you, at any time, revise these Terms + of Use and any other information contained in this Web site. + AllYouCanGET may also make improvements or changes in the products, + services, or programs described in this site at any time without notice. +

+

General

+

+ This Web site contains proprietary notices and copyright information, + the terms of which must be observed and followed. Please see the tab + entitled “Copyright and trademark information” for related information. +

+

+ AllYouCanGET grants you a non-exclusive, non-transferable, limited + permission to access and display the Web pages within this site as a + customer or potential customer of AllYouCanGET provided you comply with + these Terms of Use, and all copyright, trademark, and other proprietary + notices remain intact. You may only use a crawler to crawl this Web site + as permitted by this Web site’s robots.txt protocol, and AllYouCanGET + may block any crawlers in its sole discretion. The use authorized under + this agreement is non-commercial in nature (e.g., you may not sell the + content you access on or through this Web site.) All other use of this + site is prohibited. +

+

+ Except for the limited permission in the preceding paragraph, + AllYouCanGET does not grant you any express or implied rights or + licenses under any patents, trademarks, copyrights, or other proprietary + or intellectual property rights. You may not mirror any of the content + from this site on another Web site or in any other media. Any software + and other materials that are made available for downloading, access, or + other use from this site with their own license terms will be governed + by such terms, conditions, and notices. Your failure to comply with such + terms or any of the terms on this site will result in automatic + termination of any rights granted to you, without prior notice, and you + must immediately destroy all copies of downloaded materials in your + possession, custody or control. +

+

Disclaimer

+

+ From time to time, this Web site may contain technical inaccuracies or + typographical errors, and we do not warrant the accuracy of any posted + information. Please confirm you are using the most up-to-date pages on + this Web site, and confirm the accuracy and completeness of information + before using it to make decisions relating to services, products, or + other matters described in this Web site. +

+

+ If any term in this Terms of Use is found by competent judicial + authority to be unenforceable in any respect, the validity of the + remainder of this Terms of Use will be unaffected, provided that such + unenforceability does not materially affect the parties’ rights under + this Terms of Use. +

+

+ Forward-looking and cautionary statements +

+

+ Except for historical information and discussions, statements set forth + throughout this web site may constitute forward-looking statements + within the meaning of the Private Securities Litigation Reform Act of + 1995 or other applicable laws. These statements involve a number of + risks, uncertainties, and other factors that could cause actual results + to differ materially, as discussed in the company’s filings with + the U.S. Securities and Exchange Commission. See the “SEC filings” tab + under “Investor relations” on this Web site for copies of such filings. +

+

+ Confidential information +

+

+ AllYouCanGET does not want to receive confidential or proprietary + information from you through our Web site. Please note that any + information or material sent to AllYouCanGET will be deemed NOT to be + confidential. By sending AllYouCanGET any information or material, you + grant AllYouCanGET an unrestricted, irrevocable license to copy, + reproduce, publish, upload, post, transmit, distribute, publicly + display, perform, modify, create derivative works from, and otherwise + freely use, those materials or information. You also agree that + AllYouCanGET is free to use any ideas, concepts, know-how, or techniques + that you send us for any purpose. However, we will not release your name + or otherwise publicize the fact that you submitted materials or other + information to us unless: (a) we obtain your permission to use your + name; or (b) we first notify you that the materials or other information + you submit to a particular part of this site will be published or + otherwise used with your name on it; or (c) we are required to do so by + law. Personally-identifiable information that you submit to AllYouCanGET + for the purpose of receiving products or services will be handled in + accordance with our privacy policies. Please see the tab entitled + “Privacy” for information regarding AllYouCanGET’s privacy policies. +

+

+ U.S. government restricted rights +

+

+ AllYouCanGET provides the software downloaded from this Web site to U.S. + Government users with “RESTRICTED RIGHTS.” Use, reproduction, or + disclosure is subject to the restrictions set forth in AllYouCanGET’s + GSA ADP Schedule contract. +

+

+ Global availability +

+

+ Information AllYouCanGET publishes on the Internet may contain + references or cross references to AllYouCanGET products, programs and + services that are not announced or available in your country. Such + references do not imply that AllYouCanGET intends to announce or make + available such products, programs, or services in your country. Please + consult your local AllYouCanGET business contact for information + regarding the products, programs, and services that may be available to + you. +

+

+ Business relationships +

+

+ This Web site may provide links or references to non-AllYouCanGET Web + sites and resources. AllYouCanGET makes no representations, warranties, + or other commitments or endorsements whatsoever about any + non-AllYouCanGET Web sites or third-party resources (including any + Lenovo Web site) that may be referenced, accessible from, or linked to + any AllYouCanGET site. In addition, AllYouCanGET is not a party to or + responsible for any transactions you may enter into with third parties, + even if you learn of such parties (or use a link to such parties) from + an AllYouCanGET site. When you access a non-AllYouCanGET Web site, even + one that may contain the AllYouCanGET-logo, please understand that it is + independent from AllYouCanGET, and that AllYouCanGET does not control + the content on that Web site. It is up to you to take precautions to + protect yourself from viruses, worms, Trojan horses, and other + potentially destructive programs, and to protect your information. +

+

+ Linking to this site +

+

+ AllYouCanGET consents only to links to this Web site in which the link + and the pages that are activated by the link do not: (a) create frames + around any page on this Web site or use other techniques that alter in + any way the visual presentation or appearance of any content within this + site; (b) misrepresent your relationship with AllYouCanGET; (c) imply + that AllYouCanGET approves or endorses you, your Web site, or your + service or product offerings; and (d) present false or misleading + impressions about AllYouCanGET or otherwise damage the goodwill + associated with the AllYouCanGET name or trademarks. As a further + condition to being permitted to link to this site, you agree that + AllYouCanGET may at any time, in its sole discretion, terminate + permission to link to this Web site. In such event, you agree to + immediately remove all links to this Web site and to cease any related + use of AllYouCanGET trademarks. +

+

+ DISCLAIMER OF WARRANTY +

+

+ USE OF THIS SITE IS AT YOUR SOLE RISK. ALL MATERIALS, INFORMATION, + PRODUCTS, SOFTWARE, PROGRAMS, AND SERVICES ARE PROVIDED “AS + IS,” WITH NO WARRANTIES OR GUARANTEES WHATSOEVER. AllYouCanGET + EXPRESSLY DISCLAIMS TO THE FULLEST EXTENT PERMITTED BY LAW ALL EXPRESS, + IMPLIED, STATUTORY, AND OTHER WARRANTIES, GUARANTEES, OR + REPRESENTATIONS, INCLUDING, WITHOUT LIMITATION, THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT + OF PROPRIETARY AND INTELLECTUAL PROPERTY RIGHTS. WITHOUT LIMITATION, + AllYouCanGET MAKES NO WARRANTY OR GUARANTEE THAT THIS WEB SITE WILL BE + UNINTERRUPTED, TIMELY, SECURE, OR ERROR-FREE. +

+

+ YOU UNDERSTAND AND AGREE THAT IF YOU DOWNLOAD OR OTHERWISE OBTAIN + MATERIALS, INFORMATION, PRODUCTS, SOFTWARE, PROGRAMS, OR SERVICES FROM + THIS WEB SITE, YOU DO SO AT YOUR OWN DISCRETION AND RISK AND THAT YOU + WILL BE SOLELY RESPONSIBLE FOR ANY DAMAGES THAT MAY RESULT, INCLUDING + LOSS OF DATA OR DAMAGE TO YOUR COMPUTER SYSTEM. SOME JURISDICTIONS DO + NOT ALLOW THE EXCLUSION OF WARRANTIES, SO THE ABOVE EXCLUSIONS MAY NOT + APPLY TO YOU. +

+

+ LIMITATION OF LIABILITY +

+

+ TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL + AllYouCanGET BE LIABLE TO ANY PARTY FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES OF ANY TYPE + WHATSOEVER RELATED TO OR ARISING FROM THIS WEB SITE OR ANY USE OF THIS + WEB SITE, OR OF ANY SITE OR RESOURCE LINKED TO, REFERENCED, OR ACCESSED + THROUGH THIS WEB SITE, OR FOR THE USE OR DOWNLOADING OF, OR ACCESS TO, + ANY MATERIALS, INFORMATION, PRODUCTS, OR SERVICES, INCLUDING, WITHOUT + LIMITATION, ANY LOST PROFITS, BUSINESS INTERRUPTION, LOST SAVINGS OR + LOSS OF PROGRAMS OR OTHER DATA, EVEN IF AllYouCanGET IS EXPRESSLY + ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THIS EXCLUSION AND WAIVER OF + LIABILITY APPLIES TO ALL CAUSES OF ACTION, WHETHER BASED ON CONTRACT, + WARRANTY, TORT, OR ANY OTHER LEGAL THEORIES. +

+
+
+
+{% endblock %} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..f9ee503 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,204 @@ +{% extends "_base.html" %} {% block content %} + +
+
+

Our Partners

+
+

+ We collaborate with leading technology providers to deliver the best + solutions for your business. +

+
+ IBM + Microsoft + AWS + Google + Oracle + Informatica + SAP + Salesforce +
+
+
+
+
+
+

Ready to Transform Your Business?

+

Schedule a free consultation with our experts

+ +
+
+{% endblock %} {% block scripts %} + + +{% endblock %} diff --git a/templates/page_from_md.html b/templates/page_from_md.html new file mode 100644 index 0000000..190ff8e --- /dev/null +++ b/templates/page_from_md.html @@ -0,0 +1,20 @@ +{% extends "_base.html" %} {% block content %} + +{% include 'components/hero.html' %} + + +{% for section in page.sections %} {% include 'components/section.html' %} {% +endfor %} + + +
+
+

Ready to Get Started?

+

+ Contact us today to learn more about how we can help you. +

+ +
+
+ +{% endblock %} diff --git a/templates/services.html b/templates/services.html new file mode 100644 index 0000000..010607b --- /dev/null +++ b/templates/services.html @@ -0,0 +1,208 @@ +{% extends "_base.html" %} {% block content %} +
+
+

Our Services

+

+ Comprehensive solutions to transform your business +

+
+
+ IT Consulting +

IT Consulting

+
+

+ Comprehensive IT solutions to drive digital transformation and optimize + your technology infrastructure. +

+
+
+ IT Strategy & Planning +

IT Strategy & Planning

+

+ Develop technology roadmaps aligned with business objectives +

+
+
+ Infrastructure Management +

Infrastructure Management

+

+ Design and implement robust IT infrastructure solutions +

+
+
+ Cybersecurity +

Cybersecurity

+

+ Protect your assets with advanced security measures +

+
+
+
+
+
+ Management Consulting +

Management Consulting

+
+

+ Strategic business solutions to improve efficiency and drive growth. +

+
+
+ Process Optimization +

Process Optimization

+

+ Streamline operations and improve business efficiency +

+
+
+ Change Management +

Change Management

+

+ Guide organizational transformation effectively +

+
+
+ Financial Analysis +

Financial Analysis

+

+ Data-driven financial planning and optimization +

+
+
+
+
+
+ Infrastructure Services +

Infrastructure Services

+
+

+ Build robust foundations for your business success +

+
+
+ Cloud Solutions +

Cloud Solutions

+

+ Leverage cloud technology for scalable solutions +

+
+
+ Network Design +

Network Design

+

+ Create secure and efficient network architectures +

+
+
+ System Integration +

System Integration

+

+ Seamlessly integrate systems for optimal performance +

+
+
+
+
+
+
+
+

Need Customized Solutions?

+

+ Let's discuss how we can help transform your business +

+ +
+
+{% endblock %} diff --git a/tests/test_links.py b/tests/test_links.py new file mode 100644 index 0000000..48a900a --- /dev/null +++ b/tests/test_links.py @@ -0,0 +1,16 @@ +from lib.markdown_parser import markdown_to_html_lines + + +def test_external_link_gets_target_rel(): + md = '[external](https://example.com)' + html = markdown_to_html_lines(md) + assert 'href="https://example.com"' in html + assert 'target="_blank"' in html + assert 'rel="noopener noreferrer"' in html + + +def test_internal_link_no_target(): + md = '[internal](/about)' + html = markdown_to_html_lines(md) + assert 'href="/about"' in html + assert 'target=' not in html diff --git a/tests/test_markdown_helpers.py b/tests/test_markdown_helpers.py new file mode 100644 index 0000000..33d0711 --- /dev/null +++ b/tests/test_markdown_helpers.py @@ -0,0 +1,133 @@ +import textwrap +from lib import markdown_parser + + +def make_empty_state(): + return { + 'page': {'title': None, 'sections': []}, + 'current_section': None, + 'current_card': None, + 'current_detail': None, + 'content_buffer': [], + 'detail_buffer': [], + } + + +def test_flush_detail_buffer(): + state = make_empty_state() + state['current_detail'] = {'title': 'D', 'content': ''} + state['detail_buffer'] = ['Line 1', 'Line 2'] + + markdown_parser.flush_detail_buffer(state) + + assert 'Line 1' in state['current_detail']['content'] + assert state['detail_buffer'] == [] + + +def test_flush_content_buffer_to_card(): + state = make_empty_state() + state['current_card'] = {'title': 'C', 'content': ''} + state['content_buffer'] = ['para 1', 'para 2'] + + markdown_parser.flush_content_buffer_to_card(state) + + assert 'para 1' in state['current_card']['content'] + + +def test_flush_content_buffer_to_section(): + state = make_empty_state() + state['current_section'] = {'title': 'S', 'content': '', 'cards': []} + state['content_buffer'] = ['a', 'b'] + + markdown_parser.flush_content_buffer_to_section(state) + + assert 'a' in state['current_section']['content'] + + +def test_start_section_and_card_and_detail_and_process_line(): + state = make_empty_state() + + # start a section + markdown_parser.start_section('Sec1', state) + assert state['current_section']['title'] == 'Sec1' + assert state['page']['sections'][0]['title'] == 'Sec1' + + # start a card + markdown_parser.start_card('Card1', state) + assert state['current_card']['title'] == 'Card1' + assert state['page']['sections'][0]['cards'][0]['title'] == 'Card1' + + # start a detail + markdown_parser.start_detail('Det1', state) + assert state['current_detail']['title'] == 'Det1' + + # process a normal content line into detail_buffer + markdown_parser.process_line_with_state('Detail line 1', state) + assert 'Detail line 1' in state['detail_buffer'][0] + + # process an H3 to start a new card + markdown_parser.process_line_with_state('### NewCard', state) + assert state['current_card']['title'] == 'NewCard' + + # process an H2 to start a new section + markdown_parser.process_line_with_state('## NewSection', state) + assert state['current_section']['title'] == 'NewSection' + + +def test_start_card_without_section_is_noop(): + state = make_empty_state() + + markdown_parser.start_card('Orphan', state) + + assert state['current_card'] is None + assert state['page']['sections'] == [] + + +def test_start_detail_without_card_is_noop(): + state = make_empty_state() + + markdown_parser.start_detail('Detail', state) + + assert state['current_detail'] is None + assert state['detail_buffer'] == [] + + +def test_process_h4_without_card_is_treated_as_content(): + state = make_empty_state() + + markdown_parser.process_line_with_state('#### Heading without card', state) + + assert '#### Heading without card' in state['content_buffer'] + + +def test_process_new_h1_flushes_section_content(): + state = make_empty_state() + markdown_parser.start_section('First', state) + state['content_buffer'].append('Paragraph text') + + markdown_parser.process_line_with_state('# New Page', state) + + assert 'Paragraph text' in state['page']['sections'][0]['content'] + assert state['page']['title'] == 'New Page' + + +def test_build_component_structure_matches_integration(): + md = textwrap.dedent(""" + # Title + + ## Section A + + ### Card A + Intro A + + #### Detail X + Line X + """) + + page = markdown_parser.build_component_structure(md, 'f.md') + assert page['title'] == 'Title' + assert page['sections'][0]['title'] == 'Section A' + card = page['sections'][0]['cards'][0] + assert card['title'] == 'Card A' + assert 'Intro A' in card['content'] + assert 'Detail X' in card['details'][0]['title'] diff --git a/tests/test_markdown_parser.py b/tests/test_markdown_parser.py new file mode 100644 index 0000000..d4ab213 --- /dev/null +++ b/tests/test_markdown_parser.py @@ -0,0 +1,351 @@ +import textwrap +import pytest +from bs4 import BeautifulSoup +from markdown import Markdown +from xml.etree.ElementTree import Element, SubElement +from lib.markdown_parser import ( + HeadingCollector, + HeadingExtension, + build_component_structure, + markdown_to_html_lines, + parse_markdown_file, +) + + +def test_h4_creates_card_details(): + md = textwrap.dedent(""" + # Page Title + + ## Section One + + ### Card One + Card intro paragraph. + + #### Detail A + Detail content line 1. + - item 1 + - item 2 + + #### Detail B + Another detail paragraph with [a link](https://example.com). + """) + + page = build_component_structure(md, "test.md") + + assert page['title'] == 'Page Title' + assert len(page['sections']) == 1 + section = page['sections'][0] + assert section['title'] == 'Section One' + assert len(section['cards']) == 1 + card = section['cards'][0] + assert card['title'] == 'Card One' + # content should include the intro paragraph converted to HTML + assert 'Card intro paragraph' in card['content'] + # details should be present + assert 'details' in card + assert len(card['details']) == 2 + assert card['details'][0]['title'] == 'Detail A' + assert 'item 1' in card['details'][0]['content'] + assert card['details'][1]['title'] == 'Detail B' + assert 'https://example.com' in card['details'][1]['content'] + + +def test_section_content_preserves_lists_before_first_card(): + md = textwrap.dedent(""" + # Title + + ## Section One + + - Item A + - Item B + + ### Card Title + Card body + """) + + page = build_component_structure(md, "test.md") + + section = page['sections'][0] + assert '
    ' in section['content'] + assert '
  • Item A
  • ' in section['content'] + assert section['cards'][0]['title'] == 'Card Title' + + +def test_card_content_keeps_lists_when_section_changes(): + md = textwrap.dedent(""" + # Title + + ## Section One + + ### Card One + - First + - Second + + ## Section Two + Details + """) + + page = build_component_structure(md, "test.md") + + first_section = page['sections'][0] + card = first_section['cards'][0] + assert '
      ' in card['content'] + assert '
    • First
    • ' in card['content'] + + +def test_card_body_renders_list_items(): + md = textwrap.dedent(""" + # Title + + ## Section + + ### Card + - Alpha + - Beta + - Gamma + """) + + page = build_component_structure(md, "test.md") + + card = page['sections'][0]['cards'][0] + assert '
        ' in card['content'] + assert '
      • Alpha
      • ' in card['content'] + assert '
      • Gamma
      • ' in card['content'] + + +def test_external_links_add_target_attributes(): + md = textwrap.dedent(""" + # Title + + ## Section + + Visit [Example](https://example.com) now. + """) + + page = build_component_structure(md, "test.md") + + section_html = page['sections'][0]['content'] + soup = BeautifulSoup(section_html, 'html.parser') + anchor = soup.find('a') + assert anchor is not None + assert anchor['href'] == 'https://example.com' + assert anchor.get('target') == '_blank' + assert anchor.get('rel') == ['noopener', 'noreferrer'] + + +def test_unsafe_links_are_neutralized(): + md = textwrap.dedent(""" + # Title + + ## Section + + [Bad](javascript:alert('xss')) + """) + + page = build_component_structure(md, "test.md") + + section_html = page['sections'][0]['content'] + soup = BeautifulSoup(section_html, 'html.parser') + anchor = soup.find('a') + assert anchor is not None + assert anchor['href'] == '#unsafe' + assert 'target' not in anchor.attrs + assert 'rel' not in anchor.attrs + + +def test_index_page_sections_and_headings_extracted(): + text = """ + # Title + + Main intro paragraph. + + ## Section 1 + + Section 1 intro. + + - List item 1 a + - List item 1 b + + ## Section 2 + + Section 2 intro. + + - List item 2 a + - List item 2 b + + """ + md = Markdown(extensions=[HeadingExtension()]) + md.convert(text) + collector = md.treeprocessors['heading_collector'] + headings = [h for h in getattr(collector, 'headings', []) if 'level' in h] + + assert headings[0]['level'] == 1 + assert headings[0]['text'] == 'Title' + assert headings[1]['level'] == 2 + assert headings[1]['text'] == 'Section 1' + assert headings[2]['level'] == 2 + assert headings[2]['text'] == 'Section 2' + + +def test_index_page_main_intro_extracted(): + text = """ + # Title + + This is the main introduction paragraph for the index page. + + ## Section 1 + + Section content. + """ + md = Markdown(extensions=[HeadingExtension()]) + md.convert(text) + collector = md.treeprocessors['heading_collector'] + main_intro = getattr(collector, 'main_intro', '') + + assert 'This is the main introduction paragraph for the index page.' in main_intro + + +def test_heading_extension_collects_headings_and_lists(): + md = Markdown(extensions=[HeadingExtension()]) + md.convert(textwrap.dedent(""" + # Title + + ## Section with **Bold** and [Link](#target) + + - Item A + - [Item B](#b) and more + """)) + + collector = md.treeprocessors['heading_collector'] + headings = getattr(collector, 'headings', []) + + assert headings[0]['level'] == 1 + assert headings[0]['text'] == 'Title' + assert headings[1]['level'] == 2 + assert headings[1]['text'] == 'Section with Bold and Link' + list_entry = next(entry for entry in headings if entry.get('type') == 'ul') + assert list_entry['items'][0] == 'Item A' + assert 'Item B' in list_entry['items'][1] + + +def test_heading_collector_extract_text_handles_children(): + collector = HeadingCollector(Markdown()) + parent = Element('p') + child = SubElement(parent, 'strong') + child.text = 'Bold' + child.tail = ' tail' + nested = SubElement(child, 'em') + nested.text = 'inner' + + text = collector._extract_text(parent) + + assert 'Bold' in text + assert 'tail' in text + assert 'inner' in text + + collector.headings = [] + collector._process_element(parent) + assert collector.headings == [] + + +def test_parse_markdown_file_sets_title_from_filename(tmp_path): + file_path = tmp_path / 'sample_page.md' + file_path.write_text('Just text without heading', encoding='utf-8') + + page = parse_markdown_file(str(file_path)) + + assert page['title'] == 'Sample Page' + + +def test_parse_markdown_file_missing(tmp_path): + missing = tmp_path / 'not_there.md' + + with pytest.raises(FileNotFoundError): + parse_markdown_file(str(missing)) + + +def test_markdown_to_html_lines_handles_empty_and_blank_links(): + assert markdown_to_html_lines('') == '' + + html = markdown_to_html_lines( + 'Before [Empty]() and [External](https://example.com)') + soup = BeautifulSoup(html, 'html.parser') + anchors = soup.find_all('a') + + assert anchors[0]['href'] == '' + assert 'target' not in anchors[0].attrs + assert anchors[1]['target'] == '_blank' + + +def test_markdown_images_normalize_src_and_alt(): + html = markdown_to_html_lines( + '![IT Consulting](assets/icons/it-consulting.svg)') + soup = BeautifulSoup(html, 'html.parser') + image = soup.find('img') + + assert image is not None + assert image['src'] == 'img/it-consulting.svg' + assert image['alt'] == 'IT Consulting' + + +def test_markdown_images_add_alt_fallback(): + html = markdown_to_html_lines('![](logo.svg)') + soup = BeautifulSoup(html, 'html.parser') + image = soup.find('img') + + assert image is not None + assert image['src'] == 'img/logo.svg' + assert image['alt'] == 'logo' + + +def test_markdown_heading_with_inline_image_preserves_image(): + html = markdown_to_html_lines('## Heading ![Graphic](img/graphic.svg)') + soup = BeautifulSoup(html, 'html.parser') + heading = soup.find('h2') + image = heading.find('img') if heading else None + + assert heading is not None + assert heading.get_text(strip=True) == 'Heading' + assert image is not None + assert image['src'] == 'img/graphic.svg' + assert image['alt'] == 'Graphic' + + +def test_build_component_structure_converts_section_images(): + md = textwrap.dedent(""" + # Title + + ## Section With Visual + + Intro text before image. + + ![Vision Diagram](assets/diagrams/vision.png) + """) + + page = build_component_structure(md, "section.md") + section_html = page['sections'][0]['content'] + soup = BeautifulSoup(section_html, 'html.parser') + image = soup.find('img') + + assert image is not None + assert image['src'] == 'img/vision.png' + assert image['alt'] == 'Vision Diagram' + + +def test_build_component_structure_preserves_external_image_src(): + md = textwrap.dedent(""" + # Title + + ## Section + + ![Remote Logo](https://cdn.example.com/logo.svg) + """) + + page = build_component_structure(md, "external.md") + section_html = page['sections'][0]['content'] + soup = BeautifulSoup(section_html, 'html.parser') + image = soup.find('img') + + assert image is not None + assert image['src'] == 'https://cdn.example.com/logo.svg' + assert image['alt'] == 'Remote Logo' diff --git a/tests/test_unsafe_links.py b/tests/test_unsafe_links.py new file mode 100644 index 0000000..a4d9e45 --- /dev/null +++ b/tests/test_unsafe_links.py @@ -0,0 +1,15 @@ +from lib.markdown_parser import markdown_to_html_lines + + +def test_javascript_link_neutralized(): + md = '[bad](javascript:alert(1))' + html = markdown_to_html_lines(md) + assert 'href="#unsafe"' in html + assert 'javascript:' not in html + + +def test_data_link_neutralized(): + md = '[bad](data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==)' + html = markdown_to_html_lines(md) + assert 'href="#unsafe"' in html + assert 'data:' not in html diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..e5672e6 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,101 @@ +import os +from pathlib import Path + +import pytest + +from lib.utils import ( + get_template_files, + render_template, + set_active_page_by_url, + minify_html, + js_minifier, +) + + +def test_get_template_files_filters_partials_and_non_html( + tmp_path: Path) -> None: + template_dir = tmp_path / "templates" + template_dir.mkdir() + (template_dir / "page.html").write_text("

        Page

        ", encoding="utf-8") + (template_dir / "_partial.html").write_text("

        Ignore

        ", encoding="utf-8") + (template_dir / "notes.txt").write_text("ignore", encoding="utf-8") + + result = get_template_files(str(template_dir)) + + assert sorted(result) == ["page.html"] + + +def test_render_template_renders_with_context(tmp_path: Path) -> None: + template_dir = tmp_path / "templates" + template_dir.mkdir() + (template_dir / + "hello.html").write_text("Hello {{ name }}!", encoding="utf-8") + + rendered = render_template( + "hello.html", { + "name": "World"}, template_dir=str(template_dir)) + + assert rendered == "Hello World!" + + +def test_set_active_page_by_url_marks_only_requested_page_active() -> None: + nav = { + "index.html": {"active": False}, + "about.html": {"active": False}, + } + + set_active_page_by_url(nav, "about.html") + + assert nav["about.html"]["active"] is True + assert nav["index.html"]["active"] is False + + +def test_minify_html_removes_comments_and_extra_whitespace() -> None: + html = "
        Text
        \n" + + minified = minify_html(html) + + assert minified == "
        Text
        " + + +def test_js_minifier_strips_comments_and_rewrites_files( + tmp_path: Path) -> None: + output_dir = tmp_path / "site" + js_dir = output_dir / "js" + js_dir.mkdir(parents=True) + js_path = js_dir / "app.js" + js_path.write_text( + "// comment\nvar x = 1; \n/* block */\nfunction test() { console.log(x); }\n", + encoding="utf-8", + ) + # Ensure non-JS files are ignored + (js_dir / "readme.txt").write_text("skip", encoding="utf-8") + + js_minifier(str(output_dir)) + + content = js_path.read_text(encoding="utf-8") + + assert content == "var x = 1; function test() { console.log(x); }" + assert os.path.exists(js_dir / "readme.txt") + + +@pytest.mark.parametrize( + "initial, expected", + [ + ({"a.js": "const a = 1;"}, "const a = 1;"), + ], +) +def test_js_minifier_handles_multiple_invocations( + tmp_path: Path, initial, expected) -> None: + output_dir = tmp_path / "dist" + js_dir = output_dir / "js" + js_dir.mkdir(parents=True) + for filename, content in initial.items(): + (js_dir / filename).write_text(content, encoding="utf-8") + + js_minifier(str(output_dir)) + js_minifier(str(output_dir)) + + for filename in initial: + content = (js_dir / filename).read_text(encoding="utf-8") + assert content == expected