mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2024-12-14 10:13:05 +02:00
568 lines
20 KiB
Perl
568 lines
20 KiB
Perl
####################################################################################################################################
|
|
# DOC MARKDOWN RENDER MODULE
|
|
####################################################################################################################################
|
|
package pgBackRestDoc::Markdown::DocMarkdownRender;
|
|
use parent 'pgBackRestDoc::Common::DocExecute';
|
|
|
|
use strict;
|
|
use warnings FATAL => qw(all);
|
|
use Carp qw(confess);
|
|
|
|
use Data::Dumper;
|
|
use Exporter qw(import);
|
|
our @EXPORT = qw();
|
|
use File::Basename qw(dirname);
|
|
use File::Copy;
|
|
use Storable qw(dclone);
|
|
|
|
use pgBackRestDoc::Common::DocManifest;
|
|
use pgBackRestDoc::Common::Log;
|
|
use pgBackRestDoc::Common::String;
|
|
|
|
####################################################################################################################################
|
|
# CONSTRUCTOR
|
|
####################################################################################################################################
|
|
sub new
|
|
{
|
|
my $class = shift; # Class name
|
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
my
|
|
(
|
|
$strOperation,
|
|
$oManifest,
|
|
$strRenderOutKey,
|
|
$bExe
|
|
) =
|
|
logDebugParam
|
|
(
|
|
__PACKAGE__ . '->new', \@_,
|
|
{name => 'oManifest'},
|
|
{name => 'strRenderOutKey'},
|
|
{name => 'bExe'}
|
|
);
|
|
|
|
# Create the class hash
|
|
my $self = $class->SUPER::new(RENDER_TYPE_MARKDOWN, $oManifest, $strRenderOutKey, $bExe);
|
|
bless $self, $class;
|
|
|
|
# Return from function and log return values if any
|
|
return logDebugReturn
|
|
(
|
|
$strOperation,
|
|
{name => 'self', value => $self}
|
|
);
|
|
}
|
|
|
|
####################################################################################################################################
|
|
# process
|
|
#
|
|
# Generate the site html
|
|
####################################################################################################################################
|
|
sub process
|
|
{
|
|
my $self = shift;
|
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
my $strOperation = logDebugParam(__PACKAGE__ . '->process');
|
|
|
|
# Working variables
|
|
my $oPage = $self->{oDoc};
|
|
|
|
# Initialize page
|
|
my $strMarkdown = "# " . $oPage->paramGet('title');
|
|
|
|
if (defined($oPage->paramGet('subtitle', false)))
|
|
{
|
|
$strMarkdown .= ' <br/> ' . $oPage->paramGet('subtitle') . '';
|
|
}
|
|
|
|
# my $oHtmlBuilder = new pgBackRestDoc::Html::DocHtmlBuilder("{[project]} - Reliable PostgreSQL Backup",
|
|
# $strTitle . (defined($strSubTitle) ? " - ${strSubTitle}" : ''),
|
|
# $self->{bPretty});
|
|
#
|
|
# # Generate header
|
|
# my $oPageHeader = $oHtmlBuilder->bodyGet()->addNew(HTML_DIV, 'page-header');
|
|
#
|
|
# $oPageHeader->
|
|
# addNew(HTML_DIV, 'page-header-title',
|
|
# {strContent => $strTitle});
|
|
#
|
|
# if (defined($strSubTitle))
|
|
# {
|
|
# $oPageHeader->
|
|
# addNew(HTML_DIV, 'page-header-subtitle',
|
|
# {strContent => $strSubTitle});
|
|
# }
|
|
#
|
|
# # Generate menu
|
|
# my $oMenuBody = $oHtmlBuilder->bodyGet()->addNew(HTML_DIV, 'page-menu')->addNew(HTML_DIV, 'menu-body');
|
|
#
|
|
# if ($self->{strRenderOutKey} ne 'index')
|
|
# {
|
|
# my $oRenderOut = $self->{oManifest}->renderOutGet(RENDER_TYPE_HTML, 'index');
|
|
#
|
|
# $oMenuBody->
|
|
# addNew(HTML_DIV, 'menu')->
|
|
# addNew(HTML_A, 'menu-link', {strContent => $$oRenderOut{menu}, strRef => '{[project-url-root]}'});
|
|
# }
|
|
#
|
|
# foreach my $strRenderOutKey ($self->{oManifest}->renderOutList(RENDER_TYPE_HTML))
|
|
# {
|
|
# if ($strRenderOutKey ne $self->{strRenderOutKey} && $strRenderOutKey ne 'index')
|
|
# {
|
|
# my $oRenderOut = $self->{oManifest}->renderOutGet(RENDER_TYPE_HTML, $strRenderOutKey);
|
|
#
|
|
# $oMenuBody->
|
|
# addNew(HTML_DIV, 'menu')->
|
|
# addNew(HTML_A, 'menu-link', {strContent => $$oRenderOut{menu}, strRef => "${strRenderOutKey}.html"});
|
|
# }
|
|
# }
|
|
#
|
|
# # Generate table of contents
|
|
# my $oPageTocBody;
|
|
#
|
|
# if (!defined($oPage->paramGet('toc', false)) || $oPage->paramGet('toc') eq 'y')
|
|
# {
|
|
# my $oPageToc = $oHtmlBuilder->bodyGet()->addNew(HTML_DIV, 'page-toc');
|
|
#
|
|
# $oPageToc->
|
|
# addNew(HTML_DIV, 'page-toc-title',
|
|
# {strContent => "Table of Contents"});
|
|
#
|
|
# $oPageTocBody = $oPageToc->
|
|
# addNew(HTML_DIV, 'page-toc-body');
|
|
# }
|
|
#
|
|
# # Generate body
|
|
# my $oPageBody = $oHtmlBuilder->bodyGet()->addNew(HTML_DIV, 'page-body');
|
|
|
|
# Render sections
|
|
foreach my $oSection ($oPage->nodeList('section'))
|
|
{
|
|
$strMarkdown = trim($strMarkdown) . "\n\n" . $self->sectionProcess($oSection, 1);
|
|
}
|
|
|
|
$strMarkdown .= "\n";
|
|
|
|
# Return from function and log return values if any
|
|
return logDebugReturn
|
|
(
|
|
$strOperation,
|
|
{name => 'strMarkdown', value => $strMarkdown, trace => true}
|
|
);
|
|
}
|
|
|
|
####################################################################################################################################
|
|
# sectionProcess
|
|
####################################################################################################################################
|
|
sub sectionProcess
|
|
{
|
|
my $self = shift;
|
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
my
|
|
(
|
|
$strOperation,
|
|
$oSection,
|
|
$iDepth
|
|
) =
|
|
logDebugParam
|
|
(
|
|
__PACKAGE__ . '->sectionProcess', \@_,
|
|
{name => 'oSection'},
|
|
{name => 'iDepth'}
|
|
);
|
|
|
|
if ($oSection->paramGet('log'))
|
|
{
|
|
&log(INFO, (' ' x ($iDepth + 1)) . 'process section: ' . $oSection->paramGet('path'));
|
|
}
|
|
|
|
if ($iDepth > 3)
|
|
{
|
|
confess &log(ASSERT, "section depth of ${iDepth} exceeds maximum");
|
|
}
|
|
|
|
my $strMarkdown = '#' . ('#' x $iDepth) . ' ' . $self->processText($oSection->nodeGet('title')->textGet());
|
|
|
|
my $strLastChild = undef;
|
|
|
|
foreach my $oChild ($oSection->nodeList())
|
|
{
|
|
&log(DEBUG, (' ' x ($iDepth + 2)) . 'process child ' . $oChild->nameGet());
|
|
|
|
# Execute a command
|
|
if ($oChild->nameGet() eq 'execute-list')
|
|
{
|
|
my $bShow = $oChild->paramTest('show', 'n') ? false : true;
|
|
my $bFirst = true;
|
|
my $strHostName = $self->{oManifest}->variableReplace($oChild->paramGet('host'));
|
|
my $bOutput = false;
|
|
|
|
if ($bShow)
|
|
{
|
|
$strMarkdown .=
|
|
"\n\n${strHostName} => " . $self->processText($oChild->nodeGet('title')->textGet()) .
|
|
"\n```\n";
|
|
}
|
|
|
|
foreach my $oExecute ($oChild->nodeList('execute'))
|
|
{
|
|
my $bExeShow = !$oExecute->paramTest('show', 'n');
|
|
my $bExeExpectedError = defined($oExecute->paramGet('err-expect', false));
|
|
|
|
if ($bOutput)
|
|
{
|
|
confess &log(ERROR, "only the last command can have output");
|
|
}
|
|
|
|
my ($strCommand, $strOutput) = $self->execute(
|
|
$oSection, $strHostName, $oExecute, {iIndent => $iDepth + 3, bShow => $bShow && $bExeShow});
|
|
|
|
if ($bShow && $bExeShow)
|
|
{
|
|
# Add continuation chars and proper spacing
|
|
$strCommand =~ s/\n/\n /smg;
|
|
|
|
$strMarkdown .= "${strCommand}\n";
|
|
|
|
my $strHighLight = $self->{oManifest}->variableReplace($oExecute->fieldGet('exe-highlight', false));
|
|
my $bHighLightFound = false;
|
|
|
|
if (defined($strOutput))
|
|
{
|
|
$strMarkdown .= "\n--- output ---\n\n";
|
|
|
|
if ($oExecute->fieldTest('exe-highlight-type', 'error'))
|
|
{
|
|
$bExeExpectedError = true;
|
|
}
|
|
|
|
foreach my $strLine (split("\n", $strOutput))
|
|
{
|
|
my $bHighLight = defined($strHighLight) && $strLine =~ /$strHighLight/;
|
|
|
|
if ($bHighLight)
|
|
{
|
|
$strMarkdown .= $bExeExpectedError ? "ERR" : "-->";
|
|
}
|
|
else
|
|
{
|
|
$strMarkdown .= " ";
|
|
}
|
|
|
|
$strMarkdown .= " ${strLine}\n";
|
|
|
|
$bHighLightFound = $bHighLightFound ? true : $bHighLight ? true : false;
|
|
}
|
|
|
|
$bFirst = true;
|
|
}
|
|
|
|
if ($self->{bExe} && $self->isRequired($oSection) && defined($strHighLight) && !$bHighLightFound)
|
|
{
|
|
confess &log(ERROR, "unable to find a match for highlight: ${strHighLight}");
|
|
}
|
|
}
|
|
|
|
$bFirst = false;
|
|
}
|
|
|
|
$strMarkdown .= "```";
|
|
}
|
|
# Add code block
|
|
elsif ($oChild->nameGet() eq 'code-block')
|
|
{
|
|
if ($oChild->paramTest('title'))
|
|
{
|
|
if (defined($strLastChild) && $strLastChild ne 'code-block')
|
|
{
|
|
$strMarkdown .= "\n";
|
|
}
|
|
|
|
$strMarkdown .= "\n_" . $oChild->paramGet('title') . "_:";
|
|
}
|
|
|
|
$strMarkdown .= "\n```";
|
|
|
|
if ($oChild->paramTest('type'))
|
|
{
|
|
$strMarkdown .= $oChild->paramGet('type');
|
|
}
|
|
|
|
$strMarkdown .= "\n" . trim($oChild->valueGet()) . "\n```";
|
|
}
|
|
# Add descriptive text
|
|
elsif ($oChild->nameGet() eq 'p')
|
|
{
|
|
if (defined($strLastChild) && $strLastChild ne 'code-block' && $strLastChild ne 'table')
|
|
{
|
|
$strMarkdown .= "\n";
|
|
}
|
|
|
|
$strMarkdown .= "\n" . $self->processText($oChild->textGet());
|
|
}
|
|
# Add option descriptive text
|
|
elsif ($oChild->nameGet() eq 'option-description')
|
|
{
|
|
# my $strOption = $oChild->paramGet("key");
|
|
# my $oDescription = ${$self->{oReference}->{oConfigHash}}{&CONFIG_HELP_OPTION}{$strOption}{&CONFIG_HELP_DESCRIPTION};
|
|
#
|
|
# if (!defined($oDescription))
|
|
# {
|
|
# confess &log(ERROR, "unable to find ${strOption} option in sections - try adding command?");
|
|
# }
|
|
#
|
|
# $oSectionBodyElement->
|
|
# addNew(HTML_DIV, 'section-body-text',
|
|
# {strContent => $self->processText($oDescription)});
|
|
}
|
|
# Add/remove backrest config options
|
|
elsif ($oChild->nameGet() eq 'backrest-config')
|
|
{
|
|
# my $oConfigElement = $self->backrestConfigProcess($oSection, $oChild, $iDepth + 3);
|
|
#
|
|
# if (defined($oConfigElement))
|
|
# {
|
|
# $oSectionBodyElement->add($oConfigElement);
|
|
# }
|
|
}
|
|
# Add/remove postgres config options
|
|
elsif ($oChild->nameGet() eq 'postgres-config')
|
|
{
|
|
# my $oConfigElement = $self->postgresConfigProcess($oSection, $oChild, $iDepth + 3);
|
|
#
|
|
# if (defined($oConfigElement))
|
|
# {
|
|
# $oSectionBodyElement->add($oConfigElement);
|
|
# }
|
|
}
|
|
# Add a list
|
|
elsif ($oChild->nameGet() eq 'list')
|
|
{
|
|
foreach my $oListItem ($oChild->nodeList())
|
|
{
|
|
$strMarkdown .= "\n\n- " . $self->processText($oListItem->textGet());
|
|
}
|
|
}
|
|
# Add a subsection
|
|
elsif ($oChild->nameGet() eq 'section')
|
|
{
|
|
$strMarkdown = trim($strMarkdown) . "\n\n" . $self->sectionProcess($oChild, $iDepth + 1);
|
|
}
|
|
elsif ($oChild->nameGet() eq 'table')
|
|
{
|
|
my $oTableTitle;
|
|
if ($oChild->nodeTest('title'))
|
|
{
|
|
$oTableTitle = $oChild->nodeGet('title');
|
|
}
|
|
|
|
my $oHeader;
|
|
my @oyColumn;
|
|
|
|
if ($oChild->nodeTest('table-header'))
|
|
{
|
|
$oHeader = $oChild->nodeGet('table-header');
|
|
@oyColumn = $oHeader->nodeList('table-column');
|
|
}
|
|
|
|
if (defined($oTableTitle))
|
|
{
|
|
# Print the label (e.g. Table 1:) in front of the title if one exists
|
|
$strMarkdown .= "\n\n**" . ($oTableTitle->paramTest('label') ?
|
|
($oTableTitle->paramGet('label') . ': ' . $self->processText($oTableTitle->textGet())) :
|
|
$self->processText($oTableTitle->textGet())) . "**\n\n";
|
|
}
|
|
else
|
|
{
|
|
$strMarkdown .= "\n\n";
|
|
}
|
|
|
|
my $strHeaderText = "| ";
|
|
my $strHeaderIndicator = "| ";
|
|
|
|
for (my $iColCellIdx = 0; $iColCellIdx < @oyColumn; $iColCellIdx++)
|
|
{
|
|
my $strAlign = $oyColumn[$iColCellIdx]->paramGet("align", false, 'left');
|
|
|
|
$strHeaderText .= $self->processText($oyColumn[$iColCellIdx]->textGet()) .
|
|
(($iColCellIdx < @oyColumn - 1) ? " | " : " |\n");
|
|
$strHeaderIndicator .= ($strAlign eq 'left' || $strAlign eq 'center') ? ":---" : "---";
|
|
$strHeaderIndicator .= ($strAlign eq 'right' || $strAlign eq 'center') ? "---:" : "";
|
|
$strHeaderIndicator .= ($iColCellIdx < @oyColumn - 1) ? " | " : " |\n";
|
|
}
|
|
|
|
# Markdown requires a table header so if not provided then create an empty header row and default the column alignment
|
|
# left by using the number of columns in the 1st row
|
|
if (!defined($oHeader))
|
|
{
|
|
my @oyRow = $oChild->nodeGet('table-data')->nodeList('table-row');
|
|
foreach my $oRowCell ($oyRow[0]->nodeList('table-cell'))
|
|
{
|
|
$strHeaderText .= " | ";
|
|
$strHeaderIndicator .= ":--- | ";
|
|
}
|
|
$strHeaderText .= "\n";
|
|
$strHeaderIndicator .= "\n";
|
|
}
|
|
|
|
$strMarkdown .= (defined($strHeaderText) ? $strHeaderText : '') . $strHeaderIndicator;
|
|
|
|
# Build the rows
|
|
foreach my $oRow ($oChild->nodeGet('table-data')->nodeList('table-row'))
|
|
{
|
|
my @oRowCellList = $oRow->nodeList('table-cell');
|
|
$strMarkdown .= "| ";
|
|
|
|
for (my $iRowCellIdx = 0; $iRowCellIdx < @oRowCellList; $iRowCellIdx++)
|
|
{
|
|
my $oRowCell = $oRowCellList[$iRowCellIdx];
|
|
|
|
$strMarkdown .= $self->processText($oRowCell->textGet()) .
|
|
(($iRowCellIdx < @oRowCellList -1) ? " | " : " |\n");
|
|
}
|
|
}
|
|
}
|
|
# Add an admonition (e.g. NOTE, WARNING, etc)
|
|
elsif ($oChild->nameGet() eq 'admonition')
|
|
{
|
|
$strMarkdown .= "\n> **" . uc($oChild->paramGet('type')) . ":** " . $self->processText($oChild->textGet());
|
|
}
|
|
# Check if the child can be processed by a parent
|
|
else
|
|
{
|
|
$self->sectionChildProcess($oSection, $oChild, $iDepth + 1);
|
|
}
|
|
|
|
$strLastChild = $oChild->nameGet();
|
|
}
|
|
|
|
# Return from function and log return values if any
|
|
return logDebugReturn
|
|
(
|
|
$strOperation,
|
|
{name => 'strMarkdown', value => $strMarkdown, trace => true}
|
|
);
|
|
}
|
|
|
|
####################################################################################################################################
|
|
# backrestConfigProcess
|
|
####################################################################################################################################
|
|
sub backrestConfigProcess
|
|
{
|
|
my $self = shift;
|
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
my
|
|
(
|
|
$strOperation,
|
|
$oSection,
|
|
$oConfig,
|
|
$iDepth
|
|
) =
|
|
logDebugParam
|
|
(
|
|
__PACKAGE__ . '->backrestConfigProcess', \@_,
|
|
{name => 'oSection'},
|
|
{name => 'oConfig'},
|
|
{name => 'iDepth'}
|
|
);
|
|
|
|
# # Generate the config
|
|
# my $oConfigElement;
|
|
# my ($strFile, $strConfig, $bShow) = $self->backrestConfig($oSection, $oConfig, $iDepth);
|
|
#
|
|
# if ($bShow)
|
|
# {
|
|
# my $strHostName = $self->{oManifest}->variableReplace($oConfig->paramGet('host'));
|
|
#
|
|
# # Render the config
|
|
# $oConfigElement = new pgBackRestDoc::Html::DocHtmlElement(HTML_DIV, "config");
|
|
#
|
|
# $oConfigElement->
|
|
# addNew(HTML_DIV, "config-title",
|
|
# {strContent => "<span class=\"host\">${strHostName}</span>:<span class=\"file\">${strFile}</span>" .
|
|
# " <b>⇒</b> " . $self->processText($oConfig->nodeGet('title')->textGet())});
|
|
#
|
|
# my $oConfigBodyElement = $oConfigElement->addNew(HTML_DIV, "config-body");
|
|
# #
|
|
# # $oConfigBodyElement->
|
|
# # addNew(HTML_DIV, "config-body-title",
|
|
# # {strContent => "${strFile}:"});
|
|
#
|
|
# $oConfigBodyElement->
|
|
# addNew(HTML_DIV, "config-body-output",
|
|
# {strContent => $strConfig});
|
|
# }
|
|
#
|
|
# # Return from function and log return values if any
|
|
# return logDebugReturn
|
|
# (
|
|
# $strOperation,
|
|
# {name => 'oConfigElement', value => $oConfigElement, trace => true}
|
|
# );
|
|
}
|
|
|
|
####################################################################################################################################
|
|
# postgresConfigProcess
|
|
####################################################################################################################################
|
|
sub postgresConfigProcess
|
|
{
|
|
my $self = shift;
|
|
|
|
# # Assign function parameters, defaults, and log debug info
|
|
# my
|
|
# (
|
|
# $strOperation,
|
|
# $oSection,
|
|
# $oConfig,
|
|
# $iDepth
|
|
# ) =
|
|
# logDebugParam
|
|
# (
|
|
# __PACKAGE__ . '->postgresConfigProcess', \@_,
|
|
# {name => 'oSection'},
|
|
# {name => 'oConfig'},
|
|
# {name => 'iDepth'}
|
|
# );
|
|
#
|
|
# # Generate the config
|
|
# my $oConfigElement;
|
|
# my ($strFile, $strConfig, $bShow) = $self->postgresConfig($oSection, $oConfig, $iDepth);
|
|
#
|
|
# if ($bShow)
|
|
# {
|
|
# # Render the config
|
|
# my $strHostName = $self->{oManifest}->variableReplace($oConfig->paramGet('host'));
|
|
# $oConfigElement = new pgBackRestDoc::Html::DocHtmlElement(HTML_DIV, "config");
|
|
#
|
|
# $oConfigElement->
|
|
# addNew(HTML_DIV, "config-title",
|
|
# {strContent => "<span class=\"host\">${strHostName}</span>:<span class=\"file\">${strFile}</span>" .
|
|
# " <b>⇒</b> " . $self->processText($oConfig->nodeGet('title')->textGet())});
|
|
#
|
|
# my $oConfigBodyElement = $oConfigElement->addNew(HTML_DIV, "config-body");
|
|
#
|
|
# # $oConfigBodyElement->
|
|
# # addNew(HTML_DIV, "config-body-title",
|
|
# # {strContent => "append to ${strFile}:"});
|
|
#
|
|
# $oConfigBodyElement->
|
|
# addNew(HTML_DIV, "config-body-output",
|
|
# {strContent => defined($strConfig) ? $strConfig : '<No PgBackRest Settings>'});
|
|
#
|
|
# $oConfig->fieldSet('actual-config', $strConfig);
|
|
# }
|
|
#
|
|
# # Return from function and log return values if any
|
|
# return logDebugReturn
|
|
# (
|
|
# $strOperation,
|
|
# {name => 'oConfigElement', value => $oConfigElement, trace => true}
|
|
# );
|
|
}
|
|
|
|
1;
|