diff --git a/config/urls.py b/config/urls.py old mode 100755 new mode 100644 index 8e315a088..498f31a0e --- a/config/urls.py +++ b/config/urls.py @@ -28,6 +28,8 @@ DocLibsTemplateView, ImageView, MarkdownTemplateView, + TermsOfUseView, + PrivacyPolicyView, V3ComponentDemoView, ModernizedDocsView, RedirectToDocsView, @@ -347,13 +349,13 @@ ), path( "privacy/", - MarkdownTemplateView.as_view(), + PrivacyPolicyView.as_view(), name="privacy", kwargs={"markdown_local": "privacy-policy"}, ), path( "terms-of-use/", - MarkdownTemplateView.as_view(), + TermsOfUseView.as_view(), name="terms-of-use", kwargs={"markdown_local": "terms-of-use"}, ), diff --git a/core/mixins.py b/core/mixins.py new file mode 100644 index 000000000..2dadabf25 --- /dev/null +++ b/core/mixins.py @@ -0,0 +1,37 @@ +from waffle import flag_is_active + + +class V3Mixin: + """Renders a v3 template when the 'v3' waffle flag is active. + + Hooks into dispatch() to short-circuit the normal view flow (e.g. + MarkdownTemplateView's markdown rendering) when v3 is active. + + Subclasses declare: + v3_template_name: str — template to render when v3 is active + + And override get_v3_context_data() to supply view-specific context. + """ + + v3_template_name = None + + def dispatch(self, request, *args, **kwargs): + if self.v3_template_name and flag_is_active(request, "v3"): + self._v3_active = True + return self.render_v3_response() + self._v3_active = False + return super().dispatch(request, *args, **kwargs) + + def get_v3_context_data(self, **kwargs): + """Override in subclasses to provide v3-specific context.""" + return {} + + def render_v3_response(self): + """Render the v3 template through Django's standard TemplateView pipeline.""" + context = self.get_context_data(**self.get_v3_context_data()) + return self.render_to_response(context) + + def get_template_names(self): + if getattr(self, "_v3_active", False): + return [self.v3_template_name] + return super().get_template_names() diff --git a/core/views.py b/core/views.py index 98dc39a37..ada4b7afb 100644 --- a/core/views.py +++ b/core/views.py @@ -42,6 +42,7 @@ ) from versions.models import Version, docs_path_to_boost_name +from .mixins import V3Mixin from .asciidoc import convert_adoc_to_html from .boostrenderer import ( convert_img_paths, @@ -240,6 +241,24 @@ def get(self, request, *args, **kwargs): return self.render_to_response(context) +class TermsOfUseView(V3Mixin, MarkdownTemplateView): + """Renders the v3 Terms of Use page when the v3 flag is active, else markdown template.""" + + v3_template_name = "v3/terms_of_use.html" + + def get_v3_context_data(self, **kwargs): + return {"last_updated": "2024-02-22"} + + +class PrivacyPolicyView(V3Mixin, MarkdownTemplateView): + """Renders the v3 Privacy Policy page when the v3 flag is active, else markdown template.""" + + v3_template_name = "v3/privacy_policy.html" + + def get_v3_context_data(self, **kwargs): + return {"last_updated": "2024-02-17"} + + class ContentNotFoundException(Exception): pass @@ -1043,8 +1062,13 @@ class V3ComponentDemoView(TemplateView): template_name = "base.html" def get_context_data(self, **kwargs): - from libraries.models import LibraryVersion - from libraries.utils import build_library_intro_context + from django.urls import reverse + from libraries.models import Library, LibraryVersion + from libraries.utils import ( + build_library_intro_context, + get_commit_data_by_release_for_library, + commit_data_to_stats_bars, + ) CODE_DEMO_BEAST = """int main() { @@ -1213,7 +1237,7 @@ def get_context_data(self, **kwargs): }, }, { - "quote": "I use Boost daily. I absolutely love it. It's wonderful. I could not do my job w/o it. Much of it is in the new C++11 standard too.", + "quote": "I use Boost d1aily. I absolutely love it. It's wonderful. I could not do my job w/o it. Much of it is in the new C++11 standard too.", "author": { "name": "Name Surname", "avatar_url": "/static/img/v3/demo_page/Avatar.png", @@ -1242,4 +1266,36 @@ def get_context_data(self, **kwargs): ) if lv: context["library_intro"] = build_library_intro_context(lv) + + # Commits per release: dropdown of libraries, Beast first and default + raw_library = self.request.GET.get("library") + library_slug = (raw_library or "beast").strip().lower() + # Build dropdown choices: Beast first, then others alphabetically by name + beast = Library.objects.filter(slug="beast").first() + rest = Library.objects.exclude(slug="beast").order_by("name")[:99] + choices = [] + if beast: + choices.append((beast.slug, beast.display_name)) + for lib in rest: + choices.append((lib.slug, lib.display_name)) + context["example_library_choices"] = choices + + library = Library.objects.filter(slug__iexact=library_slug).first() + if library: + commit_data = get_commit_data_by_release_for_library(library) + context["example_library_commits_bars"] = commit_data_to_stats_bars( + commit_data[-10:] if len(commit_data) > 10 else commit_data + ) + context["example_library_name"] = library.display_name + context["example_library_slug"] = library.slug + context["example_library_detail_url"] = reverse( + "library-detail", + kwargs={ + "version_slug": "latest", + "library_slug": library.slug, + }, + ) + else: + context["example_library_not_found"] = library_slug + context["example_library_slug"] = library_slug return context diff --git a/libraries/utils.py b/libraries/utils.py index 5e85cfbe1..b12fe0178 100644 --- a/libraries/utils.py +++ b/libraries/utils.py @@ -14,7 +14,7 @@ from dateutil.parser import ParserError, parse from django.conf import settings -from django.db.models import Count +from django.db.models import Count, F from django.db.models.functions import Lower from django.urls import reverse from django.utils.text import slugify @@ -32,6 +32,53 @@ logger = structlog.get_logger() +STATS_COMMITS_BAR_HEIGHT_MAX_PX = 120 +STATS_COMMITS_BAR_HEIGHT_MIN_PX = 8 + + +def get_commit_data_by_release_for_library(library, limit=20): + """Return list of { release, commit_count } for a library, ordered by release (oldest first). + + Used by the library detail page and by the V3 examples “commits per release” lookup. + """ + from .models import LibraryVersion + + qs = ( + LibraryVersion.objects.filter( + library=library, + version__in=Version.objects.minor_versions(), + ) + .annotate(count=Count("commit"), version_name=F("version__name")) + .order_by("-version__name") + )[:limit] + return [ + {"release": x.version_name.strip("boost-"), "commit_count": x.count} + for x in reversed(list(qs)) + ] + + +def commit_data_to_stats_bars(commit_data): + """Convert commit_data_by_release (list of { release, commit_count }) to stats bar format. + + Returns list of { label, height_px } with heights scaled to STATS_COMMITS_BAR_HEIGHT_*. + """ + if not commit_data: + return [] + counts = [d["commit_count"] for d in commit_data] + max_count = max(counts) or 1 + return [ + { + "label": d["release"], + "height_px": max( + STATS_COMMITS_BAR_HEIGHT_MIN_PX, + round( + (d["commit_count"] / max_count) * STATS_COMMITS_BAR_HEIGHT_MAX_PX + ), + ), + } + for d in commit_data + ] + def decode_content(content): """Decode bytes to string.""" diff --git a/libraries/views.py b/libraries/views.py index a11c146a7..8d4e691bf 100644 --- a/libraries/views.py +++ b/libraries/views.py @@ -2,7 +2,7 @@ import structlog from django.contrib import messages -from django.db.models import F, Count, Prefetch +from django.db.models import Prefetch from django.http import Http404, HttpResponse from django.shortcuts import get_object_or_404, redirect from django.template.response import TemplateResponse @@ -35,6 +35,8 @@ get_documentation_url_redirect, get_prioritized_version, get_version_from_cookie, + get_commit_data_by_release_for_library, + commit_data_to_stats_bars, ) from .constants import LATEST_RELEASE_URL_PATH_STR @@ -247,8 +249,11 @@ def get_context_data(self, **kwargs): else self.object.github_url ) - # Populate the commit graphs - context["commit_data_by_release"] = self.get_commit_data_by_release() + commit_data = get_commit_data_by_release_for_library(self.object) + context["commit_data_by_release"] = commit_data + context["library_commits_stats_bars"] = commit_data_to_stats_bars( + commit_data[-10:] if len(commit_data) > 10 else commit_data + ) try: context["dependency_diff"] = self.get_dependency_diff(library_version) except BoostImportedDataException: @@ -270,23 +275,6 @@ def get_dependency_diff(self, library_version): ) return diffs.get(library_version.library.name, {}) - def get_commit_data_by_release(self): - qs = ( - LibraryVersion.objects.filter( - library=self.object, - version__in=Version.objects.minor_versions(), - ) - .annotate(count=Count("commit"), version_name=F("version__name")) - .order_by("-version__name") - )[:20] - return [ - { - "release": x.version_name.strip("boost-"), - "commit_count": x.count, - } - for x in reversed(list(qs)) - ] - def _prepare_commit_data(self, commit_data, data_type): commit_data_list = [] for data in commit_data: diff --git a/static/css/v3/components.css b/static/css/v3/components.css index a275fe91a..5088d9313 100644 --- a/static/css/v3/components.css +++ b/static/css/v3/components.css @@ -14,8 +14,11 @@ @import './event-cards.css'; @import './content.css'; @import './why-boost-cards.css'; +@import './stats.css'; @import './category-tags.css'; @import './code-block.css'; @import './search-card.css'; +@import './privacy-policy.css'; @import './library-intro-card.css'; +@import './terms-of-use.css'; @import './thread-archive-card.css'; diff --git a/static/css/v3/cpp-highlight.css b/static/css/v3/cpp-highlight.css index ba4a4cffe..d979f0340 100644 --- a/static/css/v3/cpp-highlight.css +++ b/static/css/v3/cpp-highlight.css @@ -1,85 +1,63 @@ - /* ========== C++ Syntax highlighting (cpp-highlight + highlight.js) ========== - * Supports both: (1) cpp-highlight custom classes (.cpp-keyword, .cpp-string, etc.) - * from Antora docs, and (2) standard highlight.js classes (.hljs-keyword, .hljs-string, etc.) - * Color scheme aligned with cpp-highlight.css for consistency. + * Brand Guidelines Syntax palette: Keyword (Blue), Text, Literal (Green), + * Comment (Yellow), Preprocessor (Pink), Attribute (Grey). + * Light/Dark variants from themes.css (--color-syntax-*). */ - .code-block code.cpp-highlight, - .code-block .code-block__inner code.cpp-highlight { - color: inherit; - } - - /* Light theme – cpp-highlight classes */ - .code-block code.cpp-highlight .cpp-keyword, - .code-block .code-block__inner code.cpp-highlight .cpp-keyword { - color: var(--color-syntax-keyword, #00f); - } - - .code-block code.cpp-highlight .cpp-string, - .code-block .code-block__inner code.cpp-highlight .cpp-string { - color: var(--color-syntax-string, #a31515); - } - - .code-block code.cpp-highlight .cpp-preprocessor, - .code-block .code-block__inner code.cpp-highlight .cpp-preprocessor { - color: var(--color-syntax-preprocessor, #6f008a); - } - - .code-block code.cpp-highlight .cpp-comment, - .code-block .code-block__inner code.cpp-highlight .cpp-comment { - color: var(--color-syntax-comment, #008000); - font-style: italic; - } - - .code-block code.cpp-highlight .cpp-attribute, - .code-block .code-block__inner code.cpp-highlight .cpp-attribute { - color: var(--color-syntax-attribute, #9e9e9e); - } - - .code-block code.cpp-highlight .cpp-number, - .code-block .code-block__inner code.cpp-highlight .cpp-number { - color: var(--color-syntax-number, #098658); - } - - .code-block code.cpp-highlight .cpp-function, - .code-block .code-block__inner code.cpp-highlight .cpp-function { - color: var(--color-syntax-function, #267f99); - } - - /* Light theme – highlight.js classes (same colors as cpp-highlight) */ - .code-block .hljs-keyword, - .code-block .hljs-selector-tag, - .code-block .hljs-addition { - color: var(--color-syntax-keyword, #00f); - } - - .code-block .hljs-string { - color: var(--color-syntax-string, #a31515); - } - - .code-block .hljs-meta, - .code-block .hljs-meta .hljs-keyword { - color: var(--color-syntax-preprocessor, #6f008a); - } - - .code-block .hljs-comment, - .code-block .hljs-quote { - color: var(--color-syntax-comment, #008000); - font-style: italic; - } - - .code-block .hljs-attribute, - .code-block .hljs-variable, - .code-block .hljs-template-variable { - color: var(--color-syntax-attribute, #9e9e9e); - } - - .code-block .hljs-number, - .code-block .hljs-literal { - color: var(--color-syntax-number, #098658); - } - - .code-block .hljs-built_in, - .code-block .hljs-class .hljs-title { - color: var(--color-syntax-function, #267f99); - } +.code-block code.cpp-highlight, +.code-block .code-block__inner code.cpp-highlight { + color: inherit; +} + +/* Keyword (Blue): light #38DDFF, dark #1345E8 */ +.code-block code.cpp-highlight .cpp-keyword, +.code-block .code-block__inner code.cpp-highlight .cpp-keyword, +.code-block .hljs-keyword, +.code-block .hljs-selector-tag, +.code-block .hljs-addition { + color: var(--color-syntax-blue); +} + +/* Literal / String / Number (Green): light #72FE92, dark #289D30 */ +.code-block code.cpp-highlight .cpp-string, +.code-block .code-block__inner code.cpp-highlight .cpp-string, +.code-block code.cpp-highlight .cpp-number, +.code-block .code-block__inner code.cpp-highlight .cpp-number, +.code-block .hljs-string, +.code-block .hljs-number, +.code-block .hljs-literal { + color: var(--color-syntax-green); +} + +/* Preprocessor (Pink): light #F358C0, dark #D31FA7 */ +.code-block code.cpp-highlight .cpp-preprocessor, +.code-block .code-block__inner code.cpp-highlight .cpp-preprocessor, +.code-block .hljs-meta, +.code-block .hljs-meta .hljs-keyword { + color: var(--color-syntax-pink); +} + +/* Comment (Yellow): light #FFF173, dark #A3A38C */ +.code-block code.cpp-highlight .cpp-comment, +.code-block .code-block__inner code.cpp-highlight .cpp-comment, +.code-block .hljs-comment, +.code-block .hljs-quote { + color: var(--color-syntax-yellow); + font-style: italic; +} + +/* Attribute (Grey): light #A3A3A3, dark #9E9E9E */ +.code-block code.cpp-highlight .cpp-attribute, +.code-block .code-block__inner code.cpp-highlight .cpp-attribute, +.code-block .hljs-attribute, +.code-block .hljs-variable, +.code-block .hljs-template-variable { + color: var(--color-syntax-comments); +} + +/* Function / built-in: same as Keyword (Blue) for consistency */ +.code-block code.cpp-highlight .cpp-function, +.code-block .code-block__inner code.cpp-highlight .cpp-function, +.code-block .hljs-built_in, +.code-block .hljs-class .hljs-title { + color: var(--color-syntax-blue); +} diff --git a/static/css/v3/foundations.css b/static/css/v3/foundations.css index dcb5fac9b..152239661 100644 --- a/static/css/v3/foundations.css +++ b/static/css/v3/foundations.css @@ -1,12 +1,17 @@ @import './index.css'; -html.v3 body { +html.v3 body, +body.v3 { font-family: var(--font-sans); font-size: var(--font-size-base); line-height: var(--line-height-default); letter-spacing: var(--letter-spacing-tight); } +body.v3 { + background: var(--color-surface-page); +} + html.v3 code { font-family: var(--font-code); } diff --git a/static/css/v3/privacy-policy.css b/static/css/v3/privacy-policy.css new file mode 100644 index 000000000..1826487e5 --- /dev/null +++ b/static/css/v3/privacy-policy.css @@ -0,0 +1,116 @@ +.privacy-policy { + box-sizing: border-box; + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + position: relative; + gap: calc(2 * var(--space-xl)); + width: 100%; + padding-left: var(--space-large); + padding-right: var(--space-large); + padding-top: var(--space-xxl); + padding-bottom: var(--space-xxl); +} + +.privacy-policy__inner { + display: flex; + flex-direction: column; + gap: var(--space-xl); + align-items: flex-start; + justify-content: flex-start; + width: 100%; + min-width: 0; + max-width: 50rem; + margin-left: auto; + margin-right: auto; +} + +.privacy-policy__header { + display: flex; + flex-direction: column; + gap: var(--space-card); + align-items: flex-start; + width: 100%; +} + +.privacy-policy__title { + margin: 0; + color: var(--color-text-primary); + font-family: var(--font-sans); + font-size: var(--font-size-2xl); + font-weight: var(--font-weight-medium); + line-height: var(--line-height-tight); + letter-spacing: var(--letter-spacing-tight); +} + +.privacy-policy__last-updated { + margin: 0; + padding: 0; + color: var(--color-text-secondary); + font-family: var(--font-sans); + font-size: var(--font-size-xs); + font-weight: var(--font-weight-regular); + line-height: var(--line-height-default); + letter-spacing: var(--letter-spacing-tight); +} + +.privacy-policy__intro { + color: var(--color-text-secondary); + font-family: var(--font-sans); + font-size: var(--font-size-base); + font-weight: var(--font-weight-regular); + line-height: var(--line-height-loose); + letter-spacing: var(--letter-spacing-tight); + width: 100%; +} + +.privacy-policy__section { + display: flex; + flex-direction: column; + gap: var(--space-card); + align-items: flex-start; + width: 100%; +} + +.privacy-policy__section-title { + margin: 0; + color: var(--color-text-primary); + font-family: var(--font-sans); + font-size: var(--font-size-large); + font-weight: var(--font-weight-medium); + line-height: var(--line-height-tight); + letter-spacing: var(--letter-spacing-tight); +} + +.privacy-policy__section-body { + color: var(--color-text-secondary); + font-family: var(--font-sans); + font-size: var(--font-size-base); + font-weight: var(--font-weight-regular); + line-height: var(--line-height-loose); + letter-spacing: var(--letter-spacing-tight); + width: 100%; +} + +.privacy-policy__section-body ul { + list-style-type: disc; + padding-left: 1em; + margin: 0 0 1em 0; +} + +.privacy-policy__section-body li { + margin-bottom: var(--space-s); +} + +.privacy-policy__section-body p { + margin: 0 0 1em 0; +} + +.privacy-policy__section-body p:last-child { + margin-bottom: 0; +} + +.privacy-policy__section-body a { + color: var(--color-text-link-accent, var(--color-text-primary)); +} diff --git a/static/css/v3/stats.css b/static/css/v3/stats.css new file mode 100644 index 000000000..3c76f2680 --- /dev/null +++ b/static/css/v3/stats.css @@ -0,0 +1,244 @@ +.stats { + --stats-space: 24px; + font-family: var(--font-sans); + color: var(--color-text-primary); + background: var(--color-bg-secondary); + border: 1px solid var(--color-stroke-weak); + border-radius: var(--border-radius-l); + overflow: hidden; + display: flex; + flex-direction: column; + box-sizing: border-box; +} + +.stats--default { + background: var(--color-bg-secondary); + border-color: var(--color-stroke-weak); +} + +.stats--yellow { + background: var(--color-surface-weak-accent-yellow); + border-color: var(--color-accent-strong-yellow); +} + +.stats--green { + background: var(--color-surface-weak-accent-green); + border-color: var(--color-accent-strong-green); +} + +.stats--teal { + background: var(--color-surface-weak-accent-teal); + border-color: var(--color-accent-strong-teal); +} + +.stats--yellow .stats__bar, +.stats--yellow .stats__set-bar { + background: var(--color-surface-strong-accent-yellow-default); +} + +.stats--green .stats__bar, +.stats--green .stats__set-bar { + background: var(--color-surface-strong-accent-green-default); +} + +.stats--teal .stats__bar, +.stats--teal .stats__set-bar { + background: var(--color-surface-strong-accent-teal-default); +} + +.stats__heading { + margin: 0; + padding: var(--space-large); + font-family: var(--font-display); + font-size: var(--font-size-large); + font-weight: var(--font-weight-medium); + line-height: var(--line-height-tight); + letter-spacing: -0.24px; + color: var(--color-text-primary); +} + +.stats__separator { + height: 0; + border: none; + border-bottom: 1px solid var(--color-border); + margin: 0; + padding: 0; +} + +/* ---------- In Numbers ---------- */ +/* Container width; 1–20 bars, fluid bar width with min from component height. */ +.stats--in-numbers { + --stats-bar-gap: 2px; + width: 100%; + min-height: 360px; + height: 360px; + container-type: size; + box-sizing: border-box; +} + +.stats--in-numbers .stats__description { + margin: 0; + padding: var(--stats-space); + font-size: var(--font-size-small); + font-weight: var(--font-weight-regular); + line-height: var(--line-height-relaxed); + letter-spacing: -0.14px; + color: var(--color-text-primary); +} + +.stats--in-numbers .stats__bars { + display: flex; + flex-wrap: nowrap; + gap: var(--stats-bar-gap); + align-items: flex-end; + padding: 0 var(--stats-space) var(--space-medium) var(--stats-space); + flex: 1 1 0; + min-height: 160px; +} + + + +.stats--in-numbers .stats__bar-wrap { + flex: 1 1 0; + min-width: max(8px, 100cqh * 0.04); + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-end; + height: 100%; +} + +.stats__bar-wrap { + flex: 1 1 0; + min-width: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-end; + height: 100%; +} + +.stats__bar { + width: 100%; + max-width: 100%; + background: var(--color-surface-strong); + border-radius: 0; + flex-shrink: 0; +} + +.stats__bar-label { + margin: 0; + margin-top: var(--space-default); + padding: 0; + font-family: var(--font-code); + font-size: var(--font-size-xs); + font-weight: var(--font-weight-regular); + line-height: var(--line-height-code); + color: var(--color-text-secondary); + white-space: nowrap; +} + +.stats__cta { + display: flex; + gap: var(--space-default); + padding: var(--stats-space); + flex-shrink: 0; +} + +.stats__cta a { + color: inherit; + text-decoration: none; +} + +.stats__cta a:hover { + text-decoration: none; +} + +.stats__cta .btn { + flex: 1 1 0; + min-width: 128px; +} + +.stats--benchmarks { + width: 460px; + height: 420px; +} + +.stats__set { + padding: var(--stats-space); +} + +.stats__set:first-of-type { + padding-top: var(--stats-space); +} + +.stats__set-title { + margin: 0 0 var(--space-s) 0; + font-size: var(--font-size-small); + font-weight: var(--font-weight-medium); + line-height: var(--line-height-relaxed); + letter-spacing: -0.14px; + color: var(--color-text-primary); +} + +.stats__set-rows { + display: flex; + flex-direction: column; + gap: var(--space-xs); +} + +.stats__set-row { + display: flex; + gap: var(--space-default); + align-items: center; + min-height: var(--stats-space); +} + +.stats__set-label { + flex: 0 0 82px; + margin: 0; + font-size: var(--font-size-xs); + font-weight: var(--font-weight-regular); + line-height: var(--line-height-default); + letter-spacing: -0.12px; + color: var(--color-text-primary); + min-width: 0; +} + +.stats__set-bar-wrap { + flex: 1 1 0; + min-width: 0; + display: flex; + align-items: center; + gap: var(--space-default); +} + +.stats__set-bar { + height: var(--stats-space); + background: var(--color-surface-strong); + border-radius: var(--border-radius-xs); + flex-shrink: 0; +} + +/* ---------- Parent-sized (fill) scaling — benchmarks only ---------- */ +.stats-wrap { + display: block; +} + +.stats-wrap--fill-benchmarks { + container-type: size; + width: 100%; + aspect-ratio: 460 / 420; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; +} + +.stats-wrap--fill-benchmarks .stats--benchmarks.stats--fill { + width: 460px; + height: 420px; + flex-shrink: 0; + transform: scale(min(100cqw / 460, 100cqh / 420)); + transform-origin: center; +} diff --git a/static/css/v3/terms-of-use.css b/static/css/v3/terms-of-use.css new file mode 100644 index 000000000..56dbb2b09 --- /dev/null +++ b/static/css/v3/terms-of-use.css @@ -0,0 +1,102 @@ +.terms-page { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: var(--space-xl); + width: 100%; + font-family: var(--font-sans); +} + +.terms-page__title-block { + display: flex; + flex-direction: column; + gap: var(--space-large); + align-items: flex-start; + width: 100%; +} + +.terms-page__title { + font-family: var(--font-sans); + font-weight: var(--font-weight-medium); + font-size: var(--font-size-2xl); + line-height: 1; + letter-spacing: -0.4px; + color: var(--color-text-primary); + margin: 0; +} + +.terms-page__last-updated { + font-family: var(--font-sans); + font-weight: var(--font-weight-regular); + font-size: var(--font-size-xs); + line-height: 1.2; + letter-spacing: -0.12px; + color: var(--color-text-secondary); + margin: 0; +} + +.terms-page__body { + font-family: var(--font-sans); + font-weight: var(--font-weight-regular); + font-size: var(--font-size-base); + line-height: var(--line-height-loose); + letter-spacing: -0.16px; + color: var(--color-text-secondary); + width: 100%; +} + +.terms-page__section { + display: flex; + flex-direction: column; + gap: var(--space-large); + align-items: flex-start; + width: 100%; +} + +.terms-page__section-heading { + font-family: var(--font-sans); + font-weight: var(--font-weight-medium); + font-size: var(--font-size-large); + line-height: 1; + letter-spacing: -0.24px; + color: var(--color-text-primary); + margin: 0; +} + +.terms-page__body p { + margin: 0 0 0.5em 0; +} + +.terms-page__body p:last-child { + margin: 0; +} + +.terms-page__body ul { + list-style: disc; + padding-left: 24px; + margin: 0 0 0.5em 0; +} + +.terms-page__body ul li { + margin-bottom: var(--space-default); +} + +.terms-page__body ul li:last-child { + margin-bottom: 0; +} + +.terms-page__body a { + color: var(--color-text-secondary); + text-decoration: underline; +} + +.terms-page__body a.terms-page__link-plain { + text-decoration: none; +} + +.terms-page__wrapper { + max-width: 800px; + margin: 0 auto; + padding: 88px var(--space-medium) var(--space-xlarge); + box-sizing: border-box; +} diff --git a/static/css/v3/v3-examples-section.css b/static/css/v3/v3-examples-section.css index 5783d8049..7c603392d 100644 --- a/static/css/v3/v3-examples-section.css +++ b/static/css/v3/v3-examples-section.css @@ -173,25 +173,26 @@ html.dark .v3-examples-section__example-box { color: var(--color-text-reversed); } -/* Post card carousel: hide scrollbar, layout wrapper */ -.post-cards-carousel { +.v3-examples-section .cards-carousel { display: flex; flex-direction: column; gap: var(--space-default, 8px); min-width: 0; } -.post-cards-carousel .carousel-buttons { +.v3-examples-section .cards-carousel .carousel-buttons { flex-shrink: 0; } -.post-cards--carousel .post-cards__list { +.v3-examples-section .post-cards--carousel .post-cards__list { scrollbar-width: none; -webkit-overflow-scrolling: touch; } -.post-cards--carousel .post-cards__list::-webkit-scrollbar { +.v3-examples-section .post-cards--carousel .post-cards__list::-webkit-scrollbar { display: none; +} + .v3-examples-section .category-cards .category-cards__gallery-block { margin-bottom: var(--space-large); } @@ -225,3 +226,46 @@ html.dark .v3-examples-section__example-box { font-size: var(--font-size-xs); color: var(--color-text-secondary); } + +/* Stats block – 2 per row in examples only */ +.v3-examples-section__stats-grid { + display: grid; + gap: var(--space-xl, 32px); + grid-template-columns: repeat(2, 1fr); +} + +@media (max-width: 959px) { + .v3-examples-section__stats-grid { + grid-template-columns: 1fr; + } +} + +/* Full-width stats block (outside grid) */ +.v3-examples-section__stats-full-width { + margin-top: var(--space-xl, 32px); +} + +.v3-examples-section__stats-grid--sized-demo { + margin-top: var(--space-xl, 32px); +} + +.v3-examples-section__stats-grid-cell { + display: flex; + flex-direction: column; + min-width: 0; +} + +.v3-examples-section__stats-grid-cell--full { + width: 100%; +} + +.v3-examples-section__stats-grid-cell--free { + align-items: flex-start; +} + +.v3-examples-section__stats-demo-label { + margin: 0 0 var(--space-s, 8px) 0; + font-size: var(--font-size-xs); + font-weight: var(--font-weight-medium); + color: var(--color-text-secondary); +} diff --git a/static/img/v3/Community-page/community-background.png b/static/img/v3/Community-page/community-background.png new file mode 100644 index 000000000..55649837a Binary files /dev/null and b/static/img/v3/Community-page/community-background.png differ diff --git a/static/img/v3/Community-page/community-foreground.png b/static/img/v3/Community-page/community-foreground.png new file mode 100644 index 000000000..5ae77d688 Binary files /dev/null and b/static/img/v3/Community-page/community-foreground.png differ diff --git a/static/img/v3/Community-page/community-placeholder.png b/static/img/v3/Community-page/community-placeholder.png new file mode 100644 index 000000000..673f01d06 Binary files /dev/null and b/static/img/v3/Community-page/community-placeholder.png differ diff --git a/static/img/v3/home-page/home-page-background.png b/static/img/v3/home-page/home-page-background.png new file mode 100644 index 000000000..c121c7f08 Binary files /dev/null and b/static/img/v3/home-page/home-page-background.png differ diff --git a/static/img/v3/home-page/home-page-foreground.png b/static/img/v3/home-page/home-page-foreground.png new file mode 100644 index 000000000..a11faed5a Binary files /dev/null and b/static/img/v3/home-page/home-page-foreground.png differ diff --git a/static/img/v3/home-page/home-page-placeholder.png b/static/img/v3/home-page/home-page-placeholder.png new file mode 100644 index 000000000..b96202c8f Binary files /dev/null and b/static/img/v3/home-page/home-page-placeholder.png differ diff --git a/static/img/v3/learn/learn-contribute-to.png b/static/img/v3/learn/learn-contribute-to.png new file mode 100644 index 000000000..1c2373bb9 Binary files /dev/null and b/static/img/v3/learn/learn-contribute-to.png differ diff --git a/static/img/v3/learn/learn-learn-how-to.png b/static/img/v3/learn/learn-learn-how-to.png new file mode 100644 index 000000000..4e9edb40c Binary files /dev/null and b/static/img/v3/learn/learn-learn-how-to.png differ diff --git a/static/img/v3/login-page/group-shot-background.png b/static/img/v3/login-page/group-shot-background.png new file mode 100644 index 000000000..7c3448826 Binary files /dev/null and b/static/img/v3/login-page/group-shot-background.png differ diff --git a/static/img/v3/login-page/group-shot-foreground.png b/static/img/v3/login-page/group-shot-foreground.png new file mode 100644 index 000000000..6571e0f1b Binary files /dev/null and b/static/img/v3/login-page/group-shot-foreground.png differ diff --git a/static/img/v3/login-page/group-shot-placehoder.png b/static/img/v3/login-page/group-shot-placehoder.png new file mode 100644 index 000000000..2db107018 Binary files /dev/null and b/static/img/v3/login-page/group-shot-placehoder.png differ diff --git a/static/img/v3/solo-images/beaver-computer-blow-up.png b/static/img/v3/solo-images/beaver-computer-blow-up.png new file mode 100644 index 000000000..787e9724d Binary files /dev/null and b/static/img/v3/solo-images/beaver-computer-blow-up.png differ diff --git a/static/img/v3/solo-images/capy-hot-tub.png b/static/img/v3/solo-images/capy-hot-tub.png new file mode 100644 index 000000000..4a2ad030b Binary files /dev/null and b/static/img/v3/solo-images/capy-hot-tub.png differ diff --git a/static/img/v3/solo-images/cow-solo.png b/static/img/v3/solo-images/cow-solo.png new file mode 100644 index 000000000..48a8e9a82 Binary files /dev/null and b/static/img/v3/solo-images/cow-solo.png differ diff --git a/static/img/v3/solo-images/gator-robot.png b/static/img/v3/solo-images/gator-robot.png new file mode 100644 index 000000000..ced2254df Binary files /dev/null and b/static/img/v3/solo-images/gator-robot.png differ diff --git a/static/img/v3/solo-images/moose-server.png b/static/img/v3/solo-images/moose-server.png new file mode 100644 index 000000000..3e4c04470 Binary files /dev/null and b/static/img/v3/solo-images/moose-server.png differ diff --git a/templates/base.html b/templates/base.html index 44226a278..cd2436977 100644 --- a/templates/base.html +++ b/templates/base.html @@ -354,7 +354,7 @@ el.setAttribute('content', el.getAttribute('data-' + m)); }); }" - class="h-screen bg-gray-200 dark:bg-black{% if DEBUG %} DEBUG{% endif %}" {% block body_id %}{% endblock %}> + class="h-screen bg-gray-200 dark:bg-black{% if DEBUG %} DEBUG{% endif %}{% flag "v3" %} v3{% endflag %}" {% block body_id %}{% endblock %}> {% flag "v3" %}
v3 flag enabled
diff --git a/templates/libraries/detail.html b/templates/libraries/detail.html index 36800d2e7..f4cf43a7d 100644 --- a/templates/libraries/detail.html +++ b/templates/libraries/detail.html @@ -1,5 +1,5 @@ {% extends "base.html" %} -{% load i18n static avatar_tags version_select %} +{% load i18n static avatar_tags version_select waffle_tags %} {% block title %}{% if object %}{{ object.display_name }} {% else %} {{ object.name }} {% endif %}({{ selected_version.display_name }}){% endblock %} {% block description %} @@ -103,7 +103,17 @@
-
{# Commits per Release #}
+ {% flag "v3" %} + {% if library_commits_stats_bars %} + {% if documentation_url %} + {% include "v3/includes/_stats_in_numbers.html" with heading="Commits per release" description="Commit count by Boost release for this library." bars=library_commits_stats_bars theme="default" primary_cta_label="Source Code" primary_cta_url=github_url secondary_cta_label="Documentation" secondary_cta_url=documentation_url %} + {% else %} + {% include "v3/includes/_stats_in_numbers.html" with heading="Commits per release" description="Commit count by Boost release for this library." bars=library_commits_stats_bars theme="default" primary_cta_label="Source Code" primary_cta_url=github_url %} + {% endif %} + {% else %} +
{# Commits per Release #}
+ {% endif %} + {% endflag %}
diff --git a/templates/v3/examples/_v3_example_section.html b/templates/v3/examples/_v3_example_section.html index c8226acef..560f3d35f 100644 --- a/templates/v3/examples/_v3_example_section.html +++ b/templates/v3/examples/_v3_example_section.html @@ -128,8 +128,6 @@

Detail card carousel

- -

Detail card carousel with autoplay

@@ -202,6 +200,44 @@

Library Intro Card

{% endif %} + {% if example_library_choices %} +
+

Commits per release (by library)

+
+
+ + +
+ {% if example_library_not_found %} +

Library “{{ example_library_not_found }}” not found.

+ {% elif example_library_commits_bars %} +
+ {% include "v3/includes/_stats_in_numbers.html" with heading="Commits per release" description="Commit count by Boost release for this library." bars=example_library_commits_bars theme="default" primary_cta_label="View library" primary_cta_url=example_library_detail_url %} + {% include "v3/includes/_stats_in_numbers.html" with heading="Commits per release" description="Commit count by Boost release for this library." bars=example_library_commits_bars theme="yellow" primary_cta_label="View library" primary_cta_url=example_library_detail_url %} + {% include "v3/includes/_stats_in_numbers.html" with heading="Commits per release" description="Commit count by Boost release for this library." bars=example_library_commits_bars theme="green" primary_cta_label="View library" primary_cta_url=example_library_detail_url %} + {% include "v3/includes/_stats_in_numbers.html" with heading="Commits per release" description="Commit count by Boost release for this library." bars=example_library_commits_bars theme="teal" primary_cta_label="View library" primary_cta_url=example_library_detail_url %} +
+
+

Full width

+ {% include "v3/includes/_stats_in_numbers.html" with heading="Commits per release" description="Takes full width of container; up to 20 bars." bars=example_library_commits_bars theme="green" primary_cta_label="View library" primary_cta_url=example_library_detail_url %} +
+
+
+

5 bars only

+ {% with example_bars_5=example_library_commits_bars|slice:":5" %} + {% include "v3/includes/_stats_in_numbers.html" with heading="Commits per release" description="Same data limited to 5 bars." bars=example_bars_5 theme="teal" primary_cta_label="View library" primary_cta_url=example_library_detail_url %} + {% endwith %} +
+
+ {% endif %} +
+
+ {% endif %} +

Form inputs

diff --git a/templates/v3/includes/_stats_benchmarks.html b/templates/v3/includes/_stats_benchmarks.html new file mode 100644 index 000000000..25533c184 --- /dev/null +++ b/templates/v3/includes/_stats_benchmarks.html @@ -0,0 +1,35 @@ +{% comment %} + V3 Stats by benchmark – card with heading and one or more sets of horizontal bar rows. + Bar width is precomputed: each row must have width_pct (percentage 0–100). + + Variables: + heading (required) — section heading + theme (optional) — "default" | "yellow" | "green" | "teal", default "default" + sets (required) — list of dicts: title, rows. Each row: label, value, width_pct (precomputed in view). + sized_by_parent (optional) — if true, wrap in fill wrapper so size is determined by parent (keeps aspect ratio) + + Usage: + {% include "v3/includes/_stats_benchmarks.html" with heading="Benchmark (custom)" sets=stats_benchmark_sets theme="teal" %} +{% endcomment %} +{% if sized_by_parent %}
{% endif %} +
+
+

{{ heading }}

+
+ {% for set in sets %} +
+

{{ set.title }}

+
+ {% for row in set.rows %} +
+

{{ row.label }}

+
+ +
+
+ {% endfor %} +
+
+ {% endfor %} +
+{% if sized_by_parent %}
{% endif %} diff --git a/templates/v3/includes/_stats_in_numbers.html b/templates/v3/includes/_stats_in_numbers.html new file mode 100644 index 000000000..c32b98a12 --- /dev/null +++ b/templates/v3/includes/_stats_in_numbers.html @@ -0,0 +1,41 @@ +{% comment %} + V3 Stats by number – card with heading, description, vertical bar chart, and primary/secondary CTA. + Takes container width by default; shows up to 20 bars (bars beyond 20 are not shown). + + Variables: + heading (required) — section heading + description (required) — short description below heading + theme (optional) — "default" | "yellow" | "green" | "teal", default "default" + bars (required) — list of dicts with label, height_px (e.g. [{"label": "1.70.0", "height_px": 78}, ...]) + primary_cta_label (required) — primary button text + primary_cta_url (required) — primary button URL + secondary_cta_label (optional) — secondary button text; if set, secondary_cta_url required + secondary_cta_url (optional) — secondary button URL + + Usage: + {% include "v3/includes/_stats_in_numbers.html" with heading="Boost in numbers" description="Some detail about a graph in here" bars=stats_bars theme="default" primary_cta_label="Boost Org on GitHub" primary_cta_url="#" secondary_cta_label="Button" secondary_cta_url="#" %} +{% endcomment %} +
+

{{ heading }}

+
+

{{ description }}

+ +
+
+ {% if theme == 'default' or not theme %} + {{ primary_cta_label }} + {% else %} + {{ primary_cta_label }} + {% endif %} + {% if secondary_cta_label %} + {{ secondary_cta_label }} + {% endif %} +
+
diff --git a/templates/v3/privacy_policy.html b/templates/v3/privacy_policy.html new file mode 100644 index 000000000..d4f602d9f --- /dev/null +++ b/templates/v3/privacy_policy.html @@ -0,0 +1,59 @@ +{% extends "base.html" %} +{% load static %} + +{% block title %}Privacy Policy{% endblock %} + +{% block content %} +
+
+
+

Privacy Policy

+

Last Updated: {{ last_updated }}

+
+
+

This privacy notice for The C Plus Plus Alliance, Inc. describes how and why we might collect, store, use, and/or share your information when you use our services ("Services").

+

The C Plus Plus Alliance is committed to protecting the privacy and accuracy of confidential information to the extent possible, subject to provisions of state and federal law. Other than as required by laws that guarantee public access to certain types of information, or in response to subpoenas or other legal instruments that authorize access, personal information is not actively shared.

+

In particular, we do not re-distribute or sell personal information collected on our web servers.

+
+
+

Information collected

+
+

The website collects the following analytics:

+
    +
  • Internet Protocol (IP) address of computer being used
  • +
  • web pages requested
  • +
  • referring web page
  • +
  • browser used
  • +
  • date and time
  • +
+

We also collect any personal information that you voluntarily provide to us either while registering for an account or while using the site. This includes information such as your name, email address, and profile photo among other things.

+

We do not collect any sensitive information.

+
+
+
+

Cookies

+
+

The website may use cookies in order to deliver web content specific to individual users' interests or as functional cookies for the authentication purposes of the site. Sensitive personal information is not stored within cookies.

+
+
+
+

Log and Usage Data

+
+

We collect and store log and usage data for diagnostic and performance purposes. This may include the same analytics information above along with additional information related to your usage of the site.

+
+
+
+

Privacy Statement Revisions

+
+

We may update this privacy notice from time to time. The updated version will be indicated by an updated "Revised" date and the updated version will be effective as soon as it is accessible. If we make material changes to this privacy notice, we may notify you either by prominently posting a notice of such changes or by directly sending you a notification. We encourage you to review this privacy notice frequently to be informed of how we are protecting your information.

+
+
+
+

Questions

+
+

Questions or concerns? Reading this privacy notice will help you understand your privacy rights and choices. If you do not agree with our policies and practices, please do not use our Services. If you still have any questions or concerns, please contact us at info@cppalliance.org.

+
+
+
+
+{% endblock %} diff --git a/templates/v3/terms_of_use.html b/templates/v3/terms_of_use.html new file mode 100644 index 000000000..f2904537b --- /dev/null +++ b/templates/v3/terms_of_use.html @@ -0,0 +1,98 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block title %}Terms of Use{% endblock %} +{% block description %}Terms of Use for The C Plus Plus Alliance, Inc. Services.{% endblock %} + +{% block content %} +
+
+
+

Terms of Use

+

Last Updated: {{ last_updated|default:"2024-02-22" }}

+
+ +
+

We are The C Plus Plus Alliance, Inc., a company registered in California, United States at 5716 Corsa Ave suite 110, Westlake Village, CA 91362, USA.

+

We operate this website, as well as any other related products and services that refer or link to these legal terms ("the Services").

+

These Legal Terms constitute a legally binding agreement made between you and The C Plus Plus Alliance, Inc., concerning your access to and use of the Services. You agree that by accessing the Services, you have read, understood, and agreed to be bound by all of these Legal Terms.

+

IF YOU DO NOT AGREE WITH ALL OF THESE LEGAL TERMS, THEN YOU ARE EXPRESSLY PROHIBITED FROM USING THE SERVICES AND YOU MUST DISCONTINUE USE IMMEDIATELY.

+

We reserve the right, in our sole discretion, to make changes or modifications to these Legal Terms from time to time. We will alert you about any changes by updating the "Last updated" date of these Legal Terms, and you waive any right to receive specific notice of each such change. It is your responsibility to periodically review these Legal Terms to stay informed of updates. You will be subject to, and will be deemed to have been made aware of and to have accepted, the changes in any revised Legal Terms by your continued use of the Services after the date such revised Legal Terms are posted.

+

We will make our best effort to notify registered users who have provided us with a valid email address of any changes to our Terms of Use via email. While we aim to ensure timely and accurate notifications, we do not guarantee that all changes will be communicated.

+
+ +
+

Intellectual Property

+
+

Boost libraries, documentation, source code, and this website are provided under the terms of the Boost Software License.

+

You grant The C Plus Plus Alliance, Inc. a royalty-free and non-exclusive license to display, use, copy, transmit, and broadcast the content you upload and publish. For issues regarding intellectual property claims, you should contact The C Plus Plus Alliance, Inc.

+
+
+ +
+

User Accounts

+
+

As a user of this website you have the option, but not the requirement, to register with us and provide additional information. You are responsible for ensuring the accuracy of this information, and you are responsible for maintaining the safety and security of your identifying information.

+

You are also responsible for all activities that occur under your account or password.

+

If you think there are any possible issues regarding the security of your account on the website, inform us immediately so we may address them accordingly.

+

We reserve all rights to terminate accounts, edit or remove content and cancel orders at our sole discretion.

+
+
+ +
+

Prohibited Activities

+
+

As a user of the Services, you agree not to:

+
    +
  • Use any information obtained from the Services in order to harass, abuse, or harm another person.
  • +
  • Use the Services in a manner inconsistent with any applicable laws or regulations.
  • +
  • Attempt to impersonate another user or person or use the username of another user.
  • +
  • Interfere with, disrupt, or create an undue burden on the Services or the networks or services connected to the Services.
  • +
  • Harass, annoy, intimidate, or threaten any of our users, employees or agents engaged in providing any portion of the Services to you.
  • +
  • Attempt to bypass any measures of the Services designed to prevent or restrict access to the Services, or any portion of the Services.
  • +
  • Register another account after you have been terminated or suspended from the Services.
  • +
+

In addition your contributions must meet these requirements:

+
    +
  • They are not obscene, lewd, lascivious, filthy, violent, harassing, libelous, slanderous, or otherwise objectionable (as determined by us).
  • +
  • They do not violate any applicable law, regulation, or rule.
  • +
  • They do not include any offensive comments including but not limited to race, national origin, gender, sexual preference, or physical handicap.
  • +
  • Your contributions must adhere to all relevant laws regarding child pornography and any which can or does result in the harming of a minor is strictly prohibited.
  • +
+
+
+ +
+

Third-Party Websites and Content

+

The Services may contain links to other websites ("Third-Party Websites") as well as articles, photographs, text, graphics, pictures, designs, music, sound, video, information, applications, software, and other content or items belonging to or originating from third parties ("Third-Party Content"). Such Third-Party Websites and Third-Party Content are not investigated, monitored, or checked for accuracy, appropriateness, or completeness by us, and we are not responsible for any Third-Party Websites accessed through the Services or any Third-Party Content posted on, available through, or installed from the Services, including the content, accuracy, offensiveness, opinions, reliability, privacy practices, or other policies of or contained in the Third-Party Websites or the Third-Party Content.

+
+ +
+

Modifications and Interruptions

+
+

We reserve the right to change, modify, or remove the contents of the Services at any time or for any reason at our sole discretion without notice.

+

We cannot guarantee the Services will be available at all times. We may experience hardware, software, or other problems or need to perform maintenance related to the Services, resulting in interruptions, delays, or errors. We reserve the right to change, revise, update, suspend, discontinue, or otherwise modify the Services at any time or for any reason without notice to you.

+
+
+ +
+

Governing Law

+

These Legal Terms and your use of the Services are governed by and construed in accordance with the laws of the State of California applicable to agreements made and to be entirely performed within the State of California, without regard to its conflict of law principles.

+
+ +
+

Disclaimer

+

THE SERVICES ARE PROVIDED ON AN AS-IS AND AS-AVAILABLE BASIS. YOU AGREE THAT YOUR USE OF THE SERVICES WILL BE AT YOUR SOLE RISK. TO THE FULLEST EXTENT PERMITTED BY LAW, WE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, IN CONNECTION WITH THE SERVICES AND YOUR USE THEREOF, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. WE MAKE NO WARRANTIES OR REPRESENTATIONS ABOUT THE ACCURACY OR COMPLETENESS OF THE SERVICES' CONTENT OR THE CONTENT OF ANY WEBSITES OR MOBILE APPLICATIONS LINKED TO THE SERVICES AND WE WILL ASSUME NO LIABILITY OR RESPONSIBILITY FOR ANY (1) ERRORS, MISTAKES, OR INACCURACIES OF CONTENT AND MATERIALS, (2) PERSONAL INJURY OR PROPERTY DAMAGE, OF ANY NATURE WHATSOEVER, RESULTING FROM YOUR ACCESS TO AND USE OF THE SERVICES, (3) ANY UNAUTHORIZED ACCESS TO OR USE OF OUR SECURE SERVERS AND/OR ANY AND ALL PERSONAL INFORMATION AND/OR FINANCIAL INFORMATION STORED THEREIN, (4) ANY INTERRUPTION OR CESSATION OF TRANSMISSION TO OR FROM THE SERVICES, (5) ANY BUGS, VIRUSES, TROJAN HORSES, OR THE LIKE WHICH MAY BE TRANSMITTED TO OR THROUGH THE SERVICES BY ANY THIRD PARTY, AND/OR (6) ANY ERRORS OR OMISSIONS IN ANY CONTENT AND MATERIALS OR FOR ANY LOSS OR DAMAGE OF ANY KIND INCURRED AS A RESULT OF THE USE OF ANY CONTENT POSTED, TRANSMITTED, OR OTHERWISE MADE AVAILABLE VIA THE SERVICES. WE DO NOT WARRANT, ENDORSE, GUARANTEE, OR ASSUME RESPONSIBILITY FOR ANY PRODUCT OR SERVICE ADVERTISED OR OFFERED BY A THIRD PARTY THROUGH THE SERVICES, ANY HYPERLINKED WEBSITE, OR ANY WEBSITE OR MOBILE APPLICATION FEATURED IN ANY BANNER OR OTHER ADVERTISING, AND WE WILL NOT BE A PARTY TO OR IN ANY WAY BE RESPONSIBLE FOR MONITORING ANY TRANSACTION BETWEEN YOU AND ANY THIRD-PARTY PROVIDERS OF PRODUCTS OR SERVICES. AS WITH THE PURCHASE OF A PRODUCT OR SERVICE THROUGH ANY MEDIUM OR IN ANY ENVIRONMENT, YOU SHOULD USE YOUR BEST JUDGMENT AND EXERCISE CAUTION WHERE APPROPRIATE.

+
+ +
+

Contact us

+
+

In order to resolve a complaint regarding the Services or to receive further information regarding use of the Services, please contact us at:

+

The C Plus Plus Alliance, Inc.
5716 Corsa Ave suite 110
Westlake Village, CA 91362
United States

+

Phone: (+1) 305 216 5538
info@cppalliance.org

+
+
+
+
+{% endblock %}