From 6d7ddc3e0a6fc62fee002fbd689cf4205b726ff8 Mon Sep 17 00:00:00 2001 From: David Steele Date: Sat, 7 Mar 2026 15:35:11 +0700 Subject: [PATCH] Add support for sponsors in the documentation. This allows logos to be displayed for sponsors in HTML on the homepage. The markdown will continue to list sponsors in text but the list will be pulled from the new XML. --- README.md | 10 +++++-- doc/RELEASE.md | 4 +++ doc/doc.pl | 1 + doc/lib/pgBackRestDoc/Html/DocHtmlElement.pm | 2 ++ doc/lib/pgBackRestDoc/Html/DocHtmlPage.pm | 15 ++++++++++ doc/lib/pgBackRestDoc/Html/DocHtmlSite.pm | 17 +++++++++++ .../Markdown/DocMarkdownRender.pm | 22 +++++++++++++++ doc/release.pl | 4 +-- doc/resource/html/default.css | 28 +++++++++++++++++++ doc/resource/sponsor/supabase.svg | 15 ++++++++++ doc/xml/dtd/doc.dtd | 8 +++++- doc/xml/index.xml | 17 +++++++++-- 12 files changed, 136 insertions(+), 7 deletions(-) create mode 100644 doc/resource/sponsor/supabase.svg diff --git a/README.md b/README.md index 432c08378..dc6a6f6f7 100644 --- a/README.md +++ b/README.md @@ -110,8 +110,14 @@ pgBackRest is completely free and open source under the [MIT](https://github.com Creating a robust disaster recovery policy with proper replication and backup strategies can be a very complex and daunting task. You may find that you need help during the architecture phase and ongoing support to ensure that your enterprise continues running smoothly. There are a variety of PostgreSQL support companies that can provide this service. +## Sponsorship + +pgBackRest would not exist without sponsors. Writing new features, fixing bugs, reviewing contributions, answering questions from the community, and maintenance all take a considerable amount of time. Please consider a [sponsorship](https://github.com/sponsors/dwsteele) if you use pgBackRest in your enterprise. + +Current sponsors: [Supabase](https://supabase.com). + +Past sponsors: [Crunchy Data](https://crunchydata.com), [Resonate](https://resonate.com). + ## Recognition -[Crunchy Data](http://www.crunchydata.com) and [Resonate](http://www.resonate.com) have both generously supported the development of pgBackRest. - [Armchair](https://thenounproject.com/icon/armchair-129971) graphic by [Alexander Skowalsky](https://thenounproject.com/sandorsz). diff --git a/doc/RELEASE.md b/doc/RELEASE.md index 80c729e9d..95c5d467c 100644 --- a/doc/RELEASE.md +++ b/doc/RELEASE.md @@ -35,6 +35,10 @@ to: pgbackrest/test/test.pl --code-count ``` +## Update sponsors + +Add new sponsors and move to past sponsors when sponsorship has lapsed. + ## Build release documentation ``` pgbackrest/doc/release.pl --build diff --git a/doc/doc.pl b/doc/doc.pl index ae61c4e55..6d5fa3e5c 100755 --- a/doc/doc.pl +++ b/doc/doc.pl @@ -339,6 +339,7 @@ eval "${strBasePath}/resource/html/" . $oManifest->variableGet('project-favicon') : undef, defined($oManifest->variableGet('project-logo')) ? "${strBasePath}/resource/" . $oManifest->variableGet('project-logo') : undef, + "${strBasePath}/resource/sponsor", !$bNoExe ); diff --git a/doc/lib/pgBackRestDoc/Html/DocHtmlElement.pm b/doc/lib/pgBackRestDoc/Html/DocHtmlElement.pm index 10a0e144f..40a726989 100644 --- a/doc/lib/pgBackRestDoc/Html/DocHtmlElement.pm +++ b/doc/lib/pgBackRestDoc/Html/DocHtmlElement.pm @@ -24,6 +24,8 @@ use constant HTML_PRE => 'pre'; push @EXPORT, qw(HTML_PRE); use constant HTML_DIV => 'div'; push @EXPORT, qw(HTML_DIV); +use constant HTML_IMG => 'img'; + push @EXPORT, qw(HTML_IMG); use constant HTML_SPAN => 'span'; push @EXPORT, qw(HTML_SPAN); use constant HTML_TABLE => 'table'; diff --git a/doc/lib/pgBackRestDoc/Html/DocHtmlPage.pm b/doc/lib/pgBackRestDoc/Html/DocHtmlPage.pm index cfa6d64ea..fec124f3c 100644 --- a/doc/lib/pgBackRestDoc/Html/DocHtmlPage.pm +++ b/doc/lib/pgBackRestDoc/Html/DocHtmlPage.pm @@ -499,6 +499,21 @@ sub sectionProcess $oList->addNew(HTML_LI, 'list-unordered', {strContent => $self->processText($oListItem->textGet())}); } } + # Add a sponsor list + elsif ($oChild->nameGet() eq 'sponsor-list') + { + my $oSponsorList = $oSectionBodyElement->addNew(HTML_DIV, "sponsor-list"); + + foreach my $oSponsor ($oChild->nodeList()) + { + my $oSponsorLink = $oSponsorList->addNew( + HTML_DIV, "sponsor")->addNew(HTML_A, undef, {strRef => $oSponsor->paramGet('url')}); + $oSponsorLink->addNew( + HTML_IMG, "sponsor-img", + {strExtra => 'src="sponsor/' . $oSponsor->paramGet('img') . '" alt="' . $oSponsor->valueGet() . '"'}); + $oSponsorLink->addNew(HTML_DIV, "sponsor-title", {strContent => $oSponsor->valueGet()}); + } + } # Add a subtitle elsif ($oChild->nameGet() eq 'subtitle') { diff --git a/doc/lib/pgBackRestDoc/Html/DocHtmlSite.pm b/doc/lib/pgBackRestDoc/Html/DocHtmlSite.pm index 975542d9b..db9a15707 100644 --- a/doc/lib/pgBackRestDoc/Html/DocHtmlSite.pm +++ b/doc/lib/pgBackRestDoc/Html/DocHtmlSite.pm @@ -47,6 +47,7 @@ sub new $self->{strCssFile}, $self->{strFaviconFile}, $self->{strProjectLogoFile}, + $self->{strSponsorPath}, $self->{bExe} ) = logDebugParam @@ -58,6 +59,7 @@ sub new {name => 'strCssFile'}, {name => 'strFaviconFile', required => false}, {name => 'strProjectLogoFile', required => false}, + {name => 'strSponsorPath'}, {name => 'bExe'} ); @@ -110,6 +112,21 @@ sub process copy($self->{strProjectLogoFile}, $strProjectLogoFileDestination) or confess &log(ERROR, "unable to copy $self->{strProjectLogoFile} to ${strProjectLogoFileDestination}"); } + + # Copy sponsor logos + my $strSponsorDestinationPath = "$self->{strHtmlPath}/sponsor"; + my @strySponsorList = $self->{oManifest}->storage()->list($self->{strSponsorPath}); + + $self->{oManifest}->storage()->pathCreate($strSponsorDestinationPath, {bIgnoreExists => true}); + + foreach my $strSponsor (@strySponsorList) + { + next if $strSponsor eq '.'; + + copy("$self->{strSponsorPath}/${strSponsor}", "${strSponsorDestinationPath}/${strSponsor}") + or confess &log( + ERROR, "unable to copy $self->{strSponsorPath}/${strSponsor} to ${strSponsorDestinationPath}/${strSponsor}"); + } } foreach my $strPageId ($self->{oManifest}->renderOutList(RENDER_TYPE_HTML)) diff --git a/doc/lib/pgBackRestDoc/Markdown/DocMarkdownRender.pm b/doc/lib/pgBackRestDoc/Markdown/DocMarkdownRender.pm index f18f0bdf8..aa151b539 100644 --- a/doc/lib/pgBackRestDoc/Markdown/DocMarkdownRender.pm +++ b/doc/lib/pgBackRestDoc/Markdown/DocMarkdownRender.pm @@ -251,6 +251,28 @@ sub sectionProcess $strMarkdown .= "\n\n- " . $self->processText($oListItem->textGet()); } } + # Add a sponsor list + elsif ($oChild->nameGet() eq 'sponsor-list') + { + my $bFirst = true; + + foreach my $oSponsor ($oChild->nodeList()) + { + if ($bFirst) + { + $strMarkdown .= ' '; + $bFirst = false; + } + else + { + $strMarkdown .= ', '; + } + + $strMarkdown .= '[' . $oSponsor->valueGet() . '](' . $oSponsor->paramGet('url') . ')'; + } + + $strMarkdown .= '.'; + } # Add a subsection elsif ($oChild->nameGet() eq 'section') { diff --git a/doc/release.pl b/doc/release.pl index 6e0d418de..a6ff9d012 100755 --- a/doc/release.pl +++ b/doc/release.pl @@ -282,7 +282,7 @@ eval &log(INFO, '...Deploy to repository'); executeTest("rm -rf ${strDeployPath}/prior/${strVersion}"); executeTest("mkdir ${strDeployPath}/prior/${strVersion}"); - executeTest("cp ${strDocHtml}/* ${strDeployPath}/prior/${strVersion}"); + executeTest("cp -r ${strDocHtml}/* ${strDeployPath}/prior/${strVersion}"); # Generate docs for the main website if (!$bDev) @@ -297,7 +297,7 @@ eval &log(INFO, '...Deploy to repository'); executeTest("rm -rf ${strDeployPath}/dev"); executeTest("find ${strDeployPath} -maxdepth 1 -type f -exec rm {} +"); - executeTest("cp ${strDocHtml}/* ${strDeployPath}"); + executeTest("cp -r ${strDocHtml}/* ${strDeployPath}"); executeTest("cp ${strDocPath}/../README.md ${strDeployPath}"); executeTest("cp ${strDocPath}/../LICENSE ${strDeployPath}"); } diff --git a/doc/resource/html/default.css b/doc/resource/html/default.css index 86b0ae5a0..5938b0be4 100644 --- a/doc/resource/html/default.css +++ b/doc/resource/html/default.css @@ -564,6 +564,34 @@ li.list-unordered margin-top: .5em; } +/******************************************************************************* +Sponsor +*******************************************************************************/ +.sponsor-list +{ + overflow: hidden; + margin-bottom: 1.5em; + margin-right: 1.5em; +} + +.sponsor +{ + float:left; + text-align: center; + margin-top: 1.5em; + margin-left: 1.5em; +} + +.sponsor-img +{ + width: 75px; +} + +.sponsor-title +{ + white-space: nowrap; +} + /******************************************************************************* Admonition *******************************************************************************/ diff --git a/doc/resource/sponsor/supabase.svg b/doc/resource/sponsor/supabase.svg new file mode 100644 index 000000000..ad802ac16 --- /dev/null +++ b/doc/resource/sponsor/supabase.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/doc/xml/dtd/doc.dtd b/doc/xml/dtd/doc.dtd index 82e563fad..4cdbe5296 100644 --- a/doc/xml/dtd/doc.dtd +++ b/doc/xml/dtd/doc.dtd @@ -113,7 +113,7 @@ @@ -140,6 +140,12 @@ + + + + + + diff --git a/doc/xml/index.xml b/doc/xml/index.xml index 8419bafe9..8e0d33908 100644 --- a/doc/xml/index.xml +++ b/doc/xml/index.xml @@ -192,12 +192,25 @@

Creating a robust disaster recovery policy with proper replication and backup strategies can be a very complex and daunting task. You may find that you need help during the architecture phase and ongoing support to ensure that your enterprise continues running smoothly. There are a variety of support companies that can provide this service.

+ +
+ Sponsorship + +

would not exist without sponsors. Writing new features, fixing bugs, reviewing contributions, answering questions from the community, and maintenance all take a considerable amount of time. Please consider a sponsorship if you use in your enterprise.

+ +

Current sponsors:

+ + + Supabase + + +

Past sponsors: Crunchy Data, Resonate.

+
+
Recognition -

Crunchy Data and Resonate have both generously supported the development of .

-

Armchair graphic by Alexander Skowalsky.