Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: build

on:
pull_request:
push:
branches: [master]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.1'
bundler-cache: true

- name: Build site
run: bundle exec jekyll build --destination _site

# Internal links only: external URLs across 150+ posts go stale for reasons
# unrelated to a pull request, and would turn this into a flaky gate.
- name: Check internal links
run: |
gem install html-proofer -v '~> 5.0'
htmlproofer _site \
--checks Links \
--ignore-urls "/^https?:\/\//,/^mailto:/" \
--assume-extension .html
43 changes: 43 additions & 0 deletions _data/vendors.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Approved commercial-support vendors for the Open Identity Platform.
#
# Source of truth: https://github.com/OpenIdentityPlatform/.github/wiki/Approved-Vendor-List
# Keep this file in sync when that wiki page changes.
# Order is strict alphabetical. Listing here is NOT an endorsement and implies no ranking.
#
# `region` is the vendor's HEADQUARTERS location (a verifiable fact), used by the region
# filter and the timezone hint on /support. Most vendors serve clients worldwide.
#
# `email` is rendered as a plain mailto: link, so it must be an address the vendor is
# happy to publish (a sales/info inbox, not a personal one).
- name: "3A Systems LLC"
slug: "3a-systems"
country: "Russia"
region: "cis"
region_label: "CIS"
languages: "Russian, English"
website: "https://www.3a-systems.ru"
email: "sales@3a-systems.ru"
- name: "Identity Fusion, Inc."
slug: "identity-fusion"
country: "United States / United Kingdom"
region: "americas"
region_label: "Americas"
languages: "English"
website: "https://www.identityfusion.com"
email: "info@identityfusion.com"
- name: "Orchitech Solutions s.r.o."
slug: "orchitech"
country: "Czechia"
region: "europe"
region_label: "Europe"
languages: "Czech, English"
website: "https://orchi.tech"
email: "info@orchitech.cz"
- name: "OSSTech Corporation"
slug: "osstech"
country: "Japan"
region: "apac"
region_label: "Asia-Pacific"
languages: "Japanese, English"
website: "https://www.osstech.co.jp"
email: "info@osstech.co.jp"
27 changes: 27 additions & 0 deletions _includes/analytics-events.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!-- GA4 conversion events. Delegated listener: any element with data-ga-event fires
that GA4 event, passing its remaining data-ga-* attributes as parameters
(data-ga-vendor -> vendor, data-ga-location -> location, ...).

Consent Mode is "basic" (see cookie-notice.html): until the visitor approves,
gtag.js is never downloaded, so an event has no destination — gtag() would only
append it to the in-memory dataLayer queue. We therefore gate on analyticsLoaded
rather than relying on the tag to discard the hit. Runs in the capture phase so
the event is queued before same-page navigation unloads the document. -->
<script>
(function () {
document.addEventListener('click', function (e) {
var el = e.target.closest ? e.target.closest('[data-ga-event]') : null;
if (!el) return;
var name = el.getAttribute('data-ga-event');
if (!name || !window.analyticsLoaded) return;
var params = {};
for (var i = 0; i < el.attributes.length; i++) {
var attr = el.attributes[i];
if (attr.name.indexOf('data-ga-') === 0 && attr.name !== 'data-ga-event') {
params[attr.name.slice(8).replace(/-/g, '_')] = attr.value;
}
}
gtag('event', name, params);
}, true);
})();
</script>
119 changes: 119 additions & 0 deletions _includes/cookie-notice.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<!-- Cookie notice + Google Analytics 4 (gtag.js) loader. Consent Mode "basic": nothing is
requested from Google and no data leaves the browser until the visitor approves. The
choice is stored in the `cookie-notice-dismissed` cookie as "true" (approved), "false"
(declined) or unset (not asked yet). Any element with a data-cookie-settings attribute
re-opens the notice, so consent can be changed or withdrawn as easily as it was given.

Lives at the end of <body>, not in <head>: a <div> in <head> makes the parser close
<head> early and silently relocate everything after it (see the note in header.html,
which keeps the styling). Styles are in <head>; the notice starts hidden, so it is
never shown unstyled. -->
<div id="cookie-notice"><span>We would like to use third party cookies and scripts to improve the functionality of this website.</span><button type="button" id="cookie-notice-accept" class="btn btn-primary btn-sm">Approve</button><button type="button" id="cookie-notice-decline" class="btn btn-light btn-sm">Decline</button><a href="/privacy" class="btn btn-light btn-sm">More info</a></div>
<script>
var GA_MEASUREMENT_ID = 'G-Q5PEKRFRH8';
var CONSENT_COOKIE = 'cookie-notice-dismissed';

function createCookie(name,value,days) {
var expires = "";
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days*24*60*60*1000));
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + value + expires + "; path=/";
}
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function eraseCookie(name) {
createCookie(name,"",-1);
}

// gtag is defined globally so any page can queue events before the library exists.
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}

// Loads gtag.js on demand. Called only once the visitor has approved, so no request
// reaches Google beforehand. This site runs no ads, so the ad_* signals stay denied.
// analyticsLoaded is a global: analytics-events.html reads it to decide whether an
// interaction event has anywhere to go.
var analyticsLoaded = false;
function loadAnalytics() {
if (analyticsLoaded) {
gtag('consent', 'update', { analytics_storage: 'granted' });
return;
}
analyticsLoaded = true;
gtag('consent', 'default', {
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
analytics_storage: 'granted'
});
gtag('js', new Date());
gtag('config', GA_MEASUREMENT_ID);
var tag = document.createElement('script');
tag.async = true;
tag.src = 'https://www.googletagmanager.com/gtag/js?id=' + GA_MEASUREMENT_ID;
document.head.appendChild(tag);
}

// Withdrawal: stop measuring and clear the analytics cookies Google has already set.
function disableAnalytics() {
if (analyticsLoaded) {
gtag('consent', 'update', { analytics_storage: 'denied' });
}
var host = document.location.hostname;
var labels = host.split('.');
var domains = ['', '; domain=' + host, '; domain=.' + host];
if (labels.length > 2) {
domains.push('; domain=.' + labels.slice(-2).join('.'));
}
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var name = cookies[i].split('=')[0].replace(/^\s+/, '');
if (name.indexOf('_ga') !== 0 && name.indexOf('_gid') !== 0) continue;
for (var d = 0; d < domains.length; d++) {
document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/' + domains[d];
}
}
}

var cookieNotice = document.getElementById('cookie-notice');
function showCookieNotice() { cookieNotice.style.display = 'block'; }
function hideCookieNotice() { cookieNotice.style.display = 'none'; }

var storedConsent = readCookie(CONSENT_COOKIE);
if (storedConsent === 'true') {
loadAnalytics();
} else if (storedConsent !== 'false') {
showCookieNotice();
}

document.getElementById('cookie-notice-accept').addEventListener("click",function() {
createCookie(CONSENT_COOKIE,'true',31);
hideCookieNotice();
loadAnalytics();
});
document.getElementById('cookie-notice-decline').addEventListener("click",function() {
createCookie(CONSENT_COOKIE,'false',31);
hideCookieNotice();
disableAnalytics();
});

// "Manage cookie settings" links (see /privacy) re-open the notice.
document.addEventListener('click', function (e) {
var trigger = e.target.closest ? e.target.closest('[data-cookie-settings]') : null;
if (!trigger) return;
e.preventDefault();
showCookieNotice();
});

</script>
43 changes: 39 additions & 4 deletions _includes/footer.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,47 @@
<footer class="footer bg-primary text-white">
<div class="container">
<div class="row">
<div class="col-8">
<a class="text-white text-center align-middle" href="{{site.repo_url}}">&copy; {{site.title}}</a>
<div class="row footer-nav g-4 pb-2">
<div class="col-6 col-md-3">
<div class="footer-heading">Products</div>
<ul class="footer-list">
{% for product in site.data.products %}
<li><a href="{{ product.details_link }}">{{ product.name }}</a></li>
{% endfor %}
</ul>
</div>
<div class="col-6 col-md-3">
<div class="footer-heading">Resources</div>
<ul class="footer-list">
<li><a href="https://doc.openidentityplatform.org/" rel="noopener">Documentation</a></li>
<li><a href="/blog/">Blog</a></li>
<li><a href="/#projects">Downloads</a></li>
<li><a href="/privacy">Privacy</a></li>
</ul>
</div>
<div class="col-6 col-md-3">
<div class="footer-heading">Community</div>
<ul class="footer-list">
<li><a href="https://github.com/orgs/OpenIdentityPlatform/discussions" rel="noopener">Discussions</a></li>
<li><a href="{{ site.repo_url }}" rel="noopener">GitHub organization</a></li>
<li><a href="https://github.com/orgs/OpenIdentityPlatform/repositories" rel="noopener">Contribute</a></li>
</ul>
</div>
<div class="col-6 col-md-3">
<div class="footer-heading">Support</div>
<ul class="footer-list">
<li><a href="/support">Support &amp; Services</a></li>
<li><a href="/forgerock-alternative">ForgeRock Migration</a></li>
</ul>
</div>
</div>
<div class="row align-items-center footer-bottom pt-3">
<div class="col-8">
<a class="text-white align-middle" href="{{site.repo_url}}">&copy; {{site.title}}</a>
<span class="footer-meta d-none d-md-inline">&middot; {{ site.data.products | size }} open-source products &middot; CDDL licensed</span>
</div>
<div class="col-4 text-end">
<a class="text-white pe-1" href="https://github.com/orgs/OpenIdentityPlatform/discussions" aria-label="Open Identity Platform Community Discussions"><svg style="fill:white;" height="36" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8.501 4.001H10.5V24H8.501V4.001zm6.999 0V24h-2V4.001h2zM3.5 0h2.001v15H3.5V0zm15 4.001h2V15h-2V4.001z"/></svg></a>
<span target="_blank" class="align-middle" style="font-size: 36px; display: inline-block;" >
<span class="align-middle" style="font-size: 36px; display: inline-block;">
<a target="_blank" href="https://github.com/OpenIdentityPlatform" aria-label="Open Identity Platform GitHub"><i class="fa-brands fa-github"></i></a>
</span>
</div>
Expand Down
52 changes: 6 additions & 46 deletions _includes/header.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
<link rel="icon" href="/favicon.ico" type="image/x-icon">

<link rel="dns-prefetch" href="https://www.googletagmanager.com" />
<link rel="dns-prefetch" href="https://cdn.jsdelivr.net" />
<link rel="dns-prefetch" href="https://cdnjs.cloudflare.com" />
<link rel="dns-prefetch" href="https://translate.google.com" />
Expand All @@ -44,54 +43,15 @@
<link rel="stylesheet" href="{{'/assets/css/highlight.css'}}" >
<link rel="stylesheet" href="{{'/assets/css/main.css'}}" >

<!-- Global site tag (gtag.js) - Google Analytics -->

<script async src="https://www.googletagmanager.com/gtag/js?id=G-Q5PEKRFRH8"></script>
<!-- Cookie-notice styling. The notice markup and its consent logic live in
cookie-notice.html, included at the end of <body>: a <div> here would implicitly
close <head>, pushing everything after it — including the JSON-LD emitted by
structured-data.html — into the body. Only the styles belong in <head>. -->
<style>
#cookie-notice {padding: 0.5rem 1rem; display: none; text-align: center; position: fixed; bottom: 0; width: calc(100%); background: #222; color: rgba(255,255,255,0.8); z-index: 1;}
#cookie-notice a {display: inline-block; cursor: pointer; margin-left: 0.5rem;}
#cookie-notice a, #cookie-notice button {display: inline-block; cursor: pointer; margin-left: 0.5rem;}
@media (max-width: 767px) {
#cookie-notice span {display: block; padding-top: 3px; margin-bottom: 1rem;}
#cookie-notice a {position: relative; bottom: 4px;}
#cookie-notice a, #cookie-notice button {position: relative; bottom: 4px;}
}
</style>
<div id="cookie-notice"><span>We would like to use third party cookies and scripts to improve the functionality of this website.</span><a id="cookie-notice-accept" class="btn btn-primary btn-sm">Approve</a><a href="/privacy" class="btn btn-primary btn-sm">More info</a></div>
<script>
function createCookie(name,value,days) {
var expires = "";
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days*24*60*60*1000));
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + value + expires + "; path=/";
}
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
function eraseCookie(name) {
createCookie(name,"",-1);
}

if(readCookie('cookie-notice-dismissed')=='true') {
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-Q5PEKRFRH8');
} else {
document.getElementById('cookie-notice').style.display = 'block';
}
document.getElementById('cookie-notice-accept').addEventListener("click",function() {
createCookie('cookie-notice-dismissed','true',31);
document.getElementById('cookie-notice').style.display = 'none';
location.reload();
});

</script>
11 changes: 7 additions & 4 deletions _includes/navbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<script type="text/javascript" src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">Products <span class="visually-hidden">(current)</span></a>
<a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Products</a>
<div class="dropdown-menu">
{% for product in site.data.products %}
<a class="dropdown-item" href="{{product.details_link}}">{{product.short_description}}</a>
Expand All @@ -32,12 +32,15 @@
</li>
<li class="nav-item">
<a target="_blank" class="nav-link" href="https://doc.openidentityplatform.org">Documentation</a>
</li>
</li>
<li class="nav-item {{page.navbar.support_active}}">
<a class="nav-link" href="/support"{% if page.navbar.support_active == 'active' %} aria-current="page"{% endif %}>Support</a>
</li>
<li class="nav-item {{page.navbar.blog_active}}">
<a class="nav-link" href="/blog/">Blog <span class="visually-hidden">(current)</span></a>
<a class="nav-link" href="/blog/"{% if page.navbar.blog_active == 'active' %} aria-current="page"{% endif %}>Blog</a>
</li>
<li class="nav-item">
<a target="_blank" class="nav-link" href="{{site.repo_url}}"><i class="fa-brands fa-github"></i> Fork us<span class="visually-hidden">(current)</span></a>
<a target="_blank" class="nav-link" href="{{site.repo_url}}"><i class="fa-brands fa-github"></i> Fork us</a>
</li>
</ul>
</div>
Expand Down
1 change: 1 addition & 0 deletions _includes/scripts.html
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
{% include analytics-events.html %}
Loading
Loading