diff --git a/_config.yml b/_config.yml
index 5751f3bef68..15cba35f990 100644
--- a/_config.yml
+++ b/_config.yml
@@ -29,6 +29,8 @@ search:
# https://just-the-docs.com/docs/configuration/#aux-links
aux_links:
+ Tags:
+ - /tags/
Recommendations:
- /recommendations
Contribute:
diff --git a/_includes/product-icon.html b/_includes/product-icon.html
new file mode 100644
index 00000000000..d83eef9fb0d
--- /dev/null
+++ b/_includes/product-icon.html
@@ -0,0 +1,8 @@
+{%- assign product_icon_url = include.product.iconUrl %}
+{%- assign product_icon_description = include.product.title %}
+{%- assign product_icon_size = include.size %}
+{%- unless product_icon_url %}
+ {%- assign product_icon_url = '/assets/default-product-logo.svg' | relative_url %}
+ {%- assign product_icon_description = 'No product' %}
+{%- endunless %}
+
diff --git a/_layouts/product-list.html b/_layouts/product-list.html
new file mode 100644
index 00000000000..69b7ccf0753
--- /dev/null
+++ b/_layouts/product-list.html
@@ -0,0 +1,16 @@
+---
+layout: default
+---
+
{{ page.title }}
+
+{% for product in page.products %}
+
+
+ {% include product-icon.html product=product size=30 %}
+ {{ product.title }}
+
+
+ {{ product.content | extract_element:'blockquote' | first | extract_element:'p' }}
+
+
+{% endfor %}
diff --git a/_layouts/product-tags.html b/_layouts/product-tags.html
new file mode 100644
index 00000000000..331a68c4aad
--- /dev/null
+++ b/_layouts/product-tags.html
@@ -0,0 +1,13 @@
+---
+layout: default
+---
+
+{{ page.title }}
+
+
+{% for tag_with_weight in page.tags %}
+ {% assign tag = tag_with_weight | split:'|' | first %}
+ {% assign weight = tag_with_weight | split:'|' | last %}
+ - {{ tag }} ({{ weight }})
+{% endfor %}
+
diff --git a/_layouts/product.html b/_layouts/product.html
index e30ec9000cf..66a983d7ca3 100644
--- a/_layouts/product.html
+++ b/_layouts/product.html
@@ -6,7 +6,9 @@
{{ page.title }}
- {% for tag in page.tags %}{{ tag }}{% endfor %}
+ {%- for tag in page.tags %}
+ {{ tag }}
+ {%- endfor %}
@@ -19,14 +21,7 @@ {{ page.title }}
- {%- assign iconUrl = page.iconUrl %}
- {%- assign iconDescription = page.title %}
- {%- unless iconUrl %}
- {%- assign iconDescription = 'No product' %}
- {%- assign iconUrl = '/assets/default-product-logo.svg' | relative_url %}
- {%- endunless %}
-
-
+ {% include product-icon.html product=page size=50 %}
{{content | extract_element:'blockquote' | first | extract_element:'p' }}
diff --git a/_plugins/generate-tag-pages.rb b/_plugins/generate-tag-pages.rb
new file mode 100644
index 00000000000..07adbee1cb1
--- /dev/null
+++ b/_plugins/generate-tag-pages.rb
@@ -0,0 +1,84 @@
+# This script create product pages for the website.
+
+require 'jekyll'
+
+module EndOfLife
+
+ class ProductPagesGenerator < Jekyll::Generator
+ safe true
+ priority :lowest
+
+ TOPIC = "Tag pages:"
+
+ def generate(site)
+ @site = site
+ start = Time.now
+ Jekyll.logger.info TOPIC, "Generating..."
+
+ products = site.pages.select { |page| page.data['layout'] == 'product' }
+
+ products_by_tag = products_by_tag(products)
+ site.pages << TagsPage.new(site, products_by_tag)
+ products_by_tag.each do |tag, products_for_tag|
+ site.pages << TagPage.new(site, tag, products_for_tag)
+ end
+
+ Jekyll.logger.info TOPIC, "Done in #{(Time.now - start).round(3)} seconds."
+ end
+
+ def products_by_tag(products)
+ products_by_tag = {}
+ products.each do |product|
+ product.data['tags'].each { |tag| add_to_map(products_by_tag, tag, product) }
+ end
+ products_by_tag
+ end
+
+ def add_to_map(map, key, page)
+ if map.has_key? key
+ map[key] << page
+ else
+ map[key] = [page]
+ end
+ end
+ end
+
+ class TagsPage < Jekyll::Page
+ def initialize(site, products_by_tag)
+ @site = site
+ @base = site.source
+ @dir = "tags"
+ @name = "index.html"
+
+ tags = products_by_tag.map { |tag, value| "#{tag}|#{value.size()}" }.sort
+ @data = {
+ "title" => "Product tags",
+ "layout" => "product-tags",
+ "permalink" => "/tags/",
+ "tags" => tags,
+ "nav_exclude"=> true
+ }
+
+ self.process(@name)
+ end
+ end
+
+ class TagPage < Jekyll::Page
+ def initialize(site, tag, products)
+ @site = site
+ @base = site.source
+ @dir = "tags"
+ @name = "#{tag}.html"
+
+ @data = {
+ "title" => "Products tagged with '#{tag}'",
+ "layout" => "product-list",
+ "permalink" => "/tags/#{tag}",
+ "products" => products.sort_by { |product| product.data['title'] },
+ "nav_exclude"=> true
+ }
+
+ self.process(@name)
+ end
+ end
+end
diff --git a/_sass/custom/custom.scss b/_sass/custom/custom.scss
index da3405031d3..13c4501cfc6 100644
--- a/_sass/custom/custom.scss
+++ b/_sass/custom/custom.scss
@@ -81,3 +81,86 @@ a {
#version-command {
overflow: scroll;
}
+
+
+// Based on https://dev.to/alvaromontoro/create-a-tag-cloud-with-html-and-css-1e90
+ul.tag-cloud {
+ list-style: none;
+ padding-left: 0;
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: center;
+ line-height: 2.5rem;
+
+ li::before { content: none !important; } // override JtD CSS
+ li:nth-child(2n+1) a { --color: #181; }
+ li:nth-child(3n+1) a { --color: #33a; }
+ li:nth-child(4n+1) a { --color: #c38; }
+
+ --tag-size-xs: 2;
+ --tag-size-s: 4;
+ --tag-size-m: 6;
+ --tag-size-l: 8;
+ --tag-size-xl: 10;
+ a[data-weight="1"] { --size: var(--tag-size-xs); }
+ a[data-weight="2"] { --size: var(--tag-size-xs); }
+ a[data-weight="3"] { --size: var(--tag-size-xs); }
+ a[data-weight="4"] { --size: var(--tag-size-xs); }
+ a[data-weight="5"] { --size: var(--tag-size-xs); }
+ a[data-weight="6"] { --size: var(--tag-size-xs); }
+ a[data-weight="7"] { --size: var(--tag-size-xs); }
+ a[data-weight="8"] { --size: var(--tag-size-xs); }
+ a[data-weight="9"] { --size: var(--tag-size-xs); }
+ a[data-weight="10"] { --size: var(--tag-size-xs); }
+ a[data-weight="11"] { --size: var(--tag-size-s); }
+ a[data-weight="12"] { --size: var(--tag-size-s); }
+ a[data-weight="13"] { --size: var(--tag-size-s); }
+ a[data-weight="14"] { --size: var(--tag-size-s); }
+ a[data-weight="15"] { --size: var(--tag-size-s); }
+ a[data-weight="16"] { --size: var(--tag-size-s); }
+ a[data-weight="17"] { --size: var(--tag-size-s); }
+ a[data-weight="18"] { --size: var(--tag-size-s); }
+ a[data-weight="19"] { --size: var(--tag-size-s); }
+ a[data-weight="20"] { --size: var(--tag-size-s); }
+ a[data-weight="21"] { --size: var(--tag-size-m); }
+ a[data-weight="22"] { --size: var(--tag-size-m); }
+ a[data-weight="23"] { --size: var(--tag-size-m); }
+ a[data-weight="24"] { --size: var(--tag-size-m); }
+ a[data-weight="25"] { --size: var(--tag-size-m); }
+ a[data-weight="26"] { --size: var(--tag-size-m); }
+ a[data-weight="27"] { --size: var(--tag-size-m); }
+ a[data-weight="28"] { --size: var(--tag-size-m); }
+ a[data-weight="29"] { --size: var(--tag-size-m); }
+ a[data-weight="30"] { --size: var(--tag-size-m); }
+ a[data-weight="31"] { --size: var(--tag-size-l); }
+ a[data-weight="32"] { --size: var(--tag-size-l); }
+ a[data-weight="33"] { --size: var(--tag-size-l); }
+ a[data-weight="34"] { --size: var(--tag-size-l); }
+ a[data-weight="35"] { --size: var(--tag-size-l); }
+ a[data-weight="36"] { --size: var(--tag-size-l); }
+ a[data-weight="37"] { --size: var(--tag-size-l); }
+ a[data-weight="38"] { --size: var(--tag-size-l); }
+ a[data-weight="39"] { --size: var(--tag-size-l); }
+ a[data-weight="40"] { --size: var(--tag-size-l); }
+ a[data-weight="41"] { --size: var(--tag-size-l); }
+ a[data-weight="42"] { --size: var(--tag-size-l); }
+ a[data-weight="43"] { --size: var(--tag-size-l); }
+ a[data-weight="44"] { --size: var(--tag-size-l); }
+ a[data-weight="45"] { --size: var(--tag-size-l); }
+ a[data-weight="46"] { --size: var(--tag-size-l); }
+ a[data-weight="47"] { --size: var(--tag-size-l); }
+ a[data-weight="48"] { --size: var(--tag-size-l); }
+ a[data-weight="49"] { --size: var(--tag-size-l); }
+ a[data-weight="50"] { --size: var(--tag-size-l); }
+
+ a {
+ --size: var(--tag-size-l);
+ color: var(--color);
+ font-size: calc(var(--size) * 0.25rem + 0.5rem);
+ display: block;
+ padding: 0.125rem 0.25rem;
+ text-decoration: none;
+ position: relative;
+ }
+}
\ No newline at end of file
diff --git a/products/etcd.md b/products/etcd.md
index 55ad89b273d..b329880c3e4 100644
--- a/products/etcd.md
+++ b/products/etcd.md
@@ -1,11 +1,11 @@
---
title: etcd
category: db
-tags: cncf db
+tags: cncf
iconSlug: etcd
permalink: /etcd
versionCommand: etcdctl version
-releasePolicyLink:
+releasePolicyLink:
https://github.com/etcd-io/etcd/blob/main/Documentation/contributor-guide/branch_management.md#stable-branches
changelogTemplate: https://github.com/etcd-io/etcd/releases/tag/v__LATEST__
activeSupportColumn: false
@@ -74,5 +74,5 @@ etcd follows [SemVer](https://semver.org/). Backwards-compatible bugs are
fixed for the latest two stable releases. A patch release to each supported
release branch, incorporating any bug fixes, happens every two weeks.
-The list of currently maintained releases is
+The list of currently maintained releases is
[available on GitHub](https://github.com/etcd-io/etcd/blob/main/Documentation/contributor-guide/release.md#release-management).
diff --git a/products/pan-gp.md b/products/pan-gp.md
index 3a7566a34b0..850f9714590 100644
--- a/products/pan-gp.md
+++ b/products/pan-gp.md
@@ -96,5 +96,9 @@ releases:
---
-[Palo Alto Networks](https://www.paloaltonetworks.com/) [GlobalProtect App](https://docs.paloaltonetworks.com/globalprotect) is the software client for the VPN service on Palo Alto Networks PAN-OS firewalls and Prisma Access service. The app can be installed on a variety of operating systems including Windows, macOS, Android, iOS, and Linux.
+> [Palo Alto Networks GlobalProtect App](https://docs.paloaltonetworks.com/globalprotect) is the
+> software client for the VPN service on Palo Alto Networks PAN-OS firewalls and Prisma Access
+> service. The app can be installed on a variety of operating systems including Windows, macOS,
+> Android, iOS, and Linux.
+
Software updates are provided as part of a valid support agreement.
diff --git a/products/symfony.md b/products/symfony.md
index b8d14ff741e..2a200e6addc 100644
--- a/products/symfony.md
+++ b/products/symfony.md
@@ -181,6 +181,9 @@ releases:
---
+> [Symfony](https://symfony.com/) is a free and open-source PHP web application framework and a set
+> of reusable PHP component libraries.
+
Symfony manages its releases through a time-based model. A new Symfony minor version comes out every
six months, one in May and one in November.
@@ -188,7 +191,7 @@ Starting from the Symfony 3.x branch, the number of minor versions is limited to
(X.0, X.1, X.2, X.3 and X.4). The last minor version of a branch (e.g. 3.4, 4.4, 5.4) is considered
a long-term support version and the other ones are considered standard versions:
-| Version Type | Bugs are fixed for | Security issues are fixed for |
-| :---------------------- | :----------------- | :---------------------------- |
-| Standard | 8 months | [8 months](https://symfony.com/blog/symfony-maintenance-changes-for-standard-releases)|
-| Long-Term Support (LTS) | 3 years | 4 years |
+| Version Type | Bugs are fixed for | Security issues are fixed for |
+|:------------------------|:-------------------|:---------------------------------------------------------------------------------------|
+| Standard | 8 months | [8 months](https://symfony.com/blog/symfony-maintenance-changes-for-standard-releases) |
+| Long-Term Support (LTS) | 3 years | 4 years |