{# The layout is chosen by the route: the theme's public shell when the product ships one, the product's own base template otherwise. The page borrows the shell's shared vocabulary rather than a look of its own: the hero slot, .container and .btn, and the storefront's class names (store-cols, store-section, store-terminal, store-meta-card, store-pill). A storefront skin that styles them (skin_store) dresses this page like the rest of its pages, and a product without that skin gets plain, readable markup. Nothing here needs either feature to be installed. The few rules below are the page's own, each colour with a neutral fallback for a product that has no brand tokens. What a developer reads here, top to bottom: how to start a product on this line, what the line is made of (its feature model, from the marketplace index), and every version it has had. #} {% extends layout %} {% import "spl_registry/_macros.html" as ui %} {% block title %}{{ spl.name }}{% endblock %} {# The heading, its description and the way on to the configurator. In the shell's hero it wears the hero classes; inside a plain base template it wears only its own, so no hero colour lands on an ordinary background. #} {% macro intro(hero) -%}

{{ spl.name }}

{% if spl.description %}

{{ spl.description }}

{% endif %} {% if configure_url %}

{{ _("Configure this line") }}

{% endif %} {%- endmacro %} {# A group's cardinality in the configurator's own words, so the two pages say the same thing about the same group. #} {% macro group_rule(rule) -%} {%- if rule == "exactly_one" -%}{{ _("Exclusive group. Pick exactly one.") }} {%- elif rule == "at_most_one" -%}{{ _("Exclusive group. Pick one at most.") }} {%- elif rule == "at_least_one" -%}{{ _("Pick at least one.") }} {%- else -%}{{ _("Pick any number, or none.") }} {%- endif -%} {%- endmacro %} {# One feature of the model: its name (a link to its marketplace page when it has one), its released version and what it does. #} {% macro feature_row(feature) -%}

{% if feature.url %}{{ feature.short }}{% else %}{{ feature.short }}{% endif %} {% if feature.version %}v{{ feature.version }}{% endif %} {% if feature.forced_by or feature.forced_group %}{{ _("In every product") }}{% endif %} {% if not feature.published %}{{ _("Not in the marketplace") }}{% endif %}

{% if feature.description %}

{{ feature.description }}

{% endif %}
{%- endmacro %} {% block head %} {% endblock %} {% block hero %} {% if public_shell %}
{{ intro(true) }}
{% endif %} {% endblock %} {% block content %}
{% if not public_shell %}
{{ intro(false) }}
{% endif %}
{# ── Side: how to start, and the line at a glance. First in the document so a phone shows it before the long model; the skin moves it to the right column on a wide screen. ─────────────────────── #}
{# ── The feature model ─────────────────────────────────────── #}

{{ ui.icon("layers") }}{{ _("Feature model") }}

{% set has_files = releases | selectattr("doi") | list | length > 0 %} {% if catalog.status == "ok" %} {% set model = catalog.model %}
  • {{ ngettext("%(num)d feature", "%(num)d features", model.counts.features) }}
  • {{ ngettext("%(num)d always included", "%(num)d always included", model.counts.always) }}
  • {% if model.counts.groups %}
  • {{ ngettext("%(num)d group", "%(num)d groups", model.counts.groups) }}
  • {% endif %}
  • {{ ngettext("%(num)d optional", "%(num)d optional", model.counts.optional) }}
{% if catalog.stale %}

{{ ui.icon("info") }}{{ _("The marketplace index still describes an earlier version of this model. Version %(latest)s appears here once the index is rebuilt.", latest=spl.latest_version) }}

{% endif %} {% if model.unpublished %}
{{ ui.icon("alert-triangle") }}

{{ _("Not in the marketplace yet") }}

{{ ngettext("This model names a feature the marketplace does not list, so it has no page here and the configurator marks it as external.", "This model names %(num)d features the marketplace does not list, so they have no page here and the configurator marks them as external.", model.unpublished | length) }}

{% for short in model.unpublished %}{{ short }}{% if not loop.last %} {% endif %}{% endfor %}

{% endif %} {% if model.forced %}
{{ ui.icon("info") }}

{{ _("Optional in the model, yet in every product") }}

    {% for item in model.forced %} {% set feature %}{{ item.short }}{% endset %} {% if item.by %} {% set by %}{{ item.by }}{% endset %}
  • {{ _("%(feature)s, because %(by)s needs it.", feature=feature, by=by) }}
  • {% else %} {% set group %}{{ item.group }}{% endset %}
  • {{ _("%(feature)s, because every choice in %(group)s needs it.", feature=feature, group=group) }}
  • {% endif %} {% endfor %}
{% endif %}
{% for branch in model.branches %}
{% if branch.key == "always" %}

{{ ui.icon("check-circle") }}{{ _("Always included") }}

{{ _("Every product of this line has these, and makes the choice each group asks for.") }}

{% else %}

{{ ui.icon("plus") }}{{ _("Optional") }}

{{ _("Each product picks the ones it needs.") }}

{% endif %}
    {% for item in branch["items"] %} {% if item.kind == "feature" %}
  • {{ feature_row(item.feature) }}
  • {% else %}
  • {{ ui.icon("git-branch", 14) }}{{ item.owner }} {{ group_rule(item.rule) }}

      {% for member in item.members %}
    • {{ feature_row(member) }}
    • {% endfor %}
  • {% endif %} {% endfor %}
{% endfor %}
{% if model.rules %}

{{ ui.icon("link") }}{{ _("Rules") }}

{{ _("A product that has a feature also gets what that feature needs.") }}

    {% for rule in model.rules %} {% set feature %}{{ rule.feature }}{% endset %} {% set required %}{% for target in rule.needs %}{{ target }}{% if not loop.last %}, {% endif %}{% endfor %}{% endset %}
  • {{ _("%(feature)s needs %(required)s", feature=feature, required=required) }}
  • {% endfor %}
{% endif %} {% elif catalog.status == "not_indexed" %}

{{ _("The marketplace index does not describe this line yet. Its features appear here once the index is rebuilt.") }} {% if has_files %}{{ _("The model file of every version is linked below.") }}{% endif %}

{% else %}

{{ _("The list of features cannot be shown right now.") }} {% if has_files %}{{ _("The model file of every version is linked below.") }}{% endif %}

{% endif %}
{# ── Every version ─────────────────────────────────────────── #}

{{ ui.icon("list") }}{{ _("Versions") }}

{% if releases %}
{% for release in releases %} {% set links = release_links.get(release.id) or {} %} {% set when = release.completed_at or release.created_at %} {% endfor %}
{{ _("Version") }} {{ _("DOI") }} {{ _("Date") }} {{ _("Published by") }} {{ _("Model file") }} {{ _("Verification") }}
{{ release.version }}{% if release.state == 'published' and release.version == spl.latest_version %} {{ _("Current") }}{% endif %} {% if release.doi and links.dataset %} {{ release.doi }} {% elif release.doi %} {{ release.doi }} {% else %} {{ _("No DOI") }} {% endif %} {{ publishers.get(release.id) or _("The marketplace") }} {% if links.file %} {{ ui.icon("download", 14) }}{{ _("Download") }} {{ release.filename }} {% else %} {{ _("No file") }} {% endif %} {% if release.state == 'published' %} {% set check = release.verification %} {{ checks.get(check) or checks.get("pending") }} {% if check == 'mismatch' %}

{{ _("What UVLHub serves for this DOI is not what was sent.") }}

{% endif %} {% else %} {{ labels.get(release.state, release.state) }} {% if release.state == 'unknown' %}

{{ _("This publication never reported back. Whether a DOI was minted for it is not known.") }}

{% endif %} {% endif %}
{% else %}

{{ _("This product line has no releases yet.") }}

{% endif %}
{% if start_command %} {% endif %} {% endblock %}