*
*/
namespace ComboStrap;
use Doku_Event;
use dokuwiki\Extension\Event;
use dokuwiki\Menu\PageMenu;
use dokuwiki\Menu\SiteMenu;
use dokuwiki\Menu\UserMenu;
use dokuwiki\plugin\config\core\Configuration;
use dokuwiki\plugin\config\core\Writer;
use Exception;
/**
* Class TplUtility
* @package ComboStrap
* Utility class
*/
class TplUtility
{
/**
* Constant for the function {@link msg()}
* -1 = error, 0 = info, 1 = success, 2 = notify
*/
const LVL_MSG_ERROR = -1;
const LVL_MSG_INFO = 0;
const LVL_MSG_SUCCESS = 1;
const LVL_MSG_WARNING = 2;
const LVL_MSG_DEBUG = 3;
const TEMPLATE_NAME = 'strap';
const CONF_HEADER_SLOT_PAGE_NAME = "headerSlotPageName";
const CONF_FOOTER_SLOT_PAGE_NAME = "footerSlotPageName";
/**
* @deprecated for {@link TplUtility::CONF_BOOTSTRAP_VERSION_STYLESHEET}
*/
const CONF_BOOTSTRAP_VERSION = "bootstrapVersion";
/**
* @deprecated for {@link TplUtility::CONF_BOOTSTRAP_VERSION_STYLESHEET}
*/
const CONF_BOOTSTRAP_STYLESHEET = "bootstrapStylesheet";
/**
* Stylesheet and Boostrap should have the same version
* This conf is a mix between the version and the stylesheet
*
* majorVersion.0.0 - stylesheetname
*/
const CONF_BOOTSTRAP_VERSION_STYLESHEET = "bootstrapVersionStylesheet";
/**
* The separator in {@link TplUtility::CONF_BOOTSTRAP_VERSION_STYLESHEET}
*/
const BOOTSTRAP_VERSION_STYLESHEET_SEPARATOR = " - ";
const DEFAULT_BOOTSTRAP_VERSION_STYLESHEET = "5.0.1" . self::BOOTSTRAP_VERSION_STYLESHEET_SEPARATOR . "bootstrap";
/**
* Jquery UI
*/
const CONF_JQUERY_DOKU = 'jQueryDoku';
const CONF_REM_SIZE = "remSize";
const CONF_GRID_COLUMNS = "gridColumns";
const CONF_USE_CDN = "useCDN";
const CONF_PRELOAD_CSS = "preloadCss"; // preload all css ?
const BS_4_BOOTSTRAP_VERSION_STYLESHEET = "4.5.0 - bootstrap";
const CONF_SIDEKICK_OLD = "sidekickbar";
const CONF_SIDEKICK_SLOT_PAGE_NAME = "sidekickSlotPageName";
const CONF_SLOT_HEADER_PAGE_NAME_VALUE = "slot_header";
/**
* @deprecated see {@link TplUtility::CONF_HEADER_SLOT_PAGE_NAME}
*/
const CONF_HEADER_OLD = "headerbar";
/**
* @deprecated
*/
const CONF_HEADER_OLD_VALUE = TplUtility::CONF_HEADER_OLD;
/**
* @deprecated see {@link TplUtility::CONF_FOOTER_SLOT_PAGE_NAME}
*/
const CONF_FOOTER_OLD = "footerbar";
/**
* Disable the javascript of Dokuwiki
* if public
* https://combostrap.com/frontend/optimization
*/
const CONF_DISABLE_BACKEND_JAVASCRIPT = "disableBackendJavascript";
/**
* A parameter switch to allows the update
* of conf in test
*/
const COMBO_TEST_UPDATE = "combo_update_conf";
/**
* Do we show the rail bar for anonymous user
*/
const CONF_PRIVATE_RAIL_BAR = "privateRailbar";
/**
* When do we toggle from offcanvas to fixed railbar
*/
const CONF_BREAKPOINT_RAIL_BAR = "breakpointRailbar";
/**
* Breakpoint naming
*/
const BREAKPOINT_EXTRA_SMALL_NAME = "extra-small";
const BREAKPOINT_SMALL_NAME = "small";
const BREAKPOINT_MEDIUM_NAME = "medium";
const BREAKPOINT_LARGE_NAME = "large";
const BREAKPOINT_EXTRA_LARGE_NAME = "extra-large";
const BREAKPOINT_EXTRA_EXTRA_LARGE_NAME = "extra-extra-large";
const BREAKPOINT_NEVER_NAME = "never";
/**
* Name of the main footer slot
*/
public const SLOT_MAIN_FOOTER_NAME = "slot_main_footer";
/**
* Name of the main header slot
*/
public const SLOT_MAIN_HEADER_NAME = "slot_main_header";
/**
* @var array|null
*/
private static $TEMPLATE_INFO = null;
private static $COMBO_INFO = null;
/**
* Print the breadcrumbs trace with Bootstrap class
*
* @param string $sep Separator between entries
* @return bool
* @author Nicolas GERARD
*
*
*/
static function renderTrailBreadcrumb($sep = '�')
{
global $conf;
global $lang;
//check if enabled
if (!$conf['breadcrumbs']) return false;
$crumbs = breadcrumbs(); //setup crumb trace
echo '' . PHP_EOL;
return true;
}
/**
* @param string $text add a comment into the HTML page
*/
private static function addAsHtmlComment($text)
{
print_r('');
}
private static function getApexDomainUrl()
{
return self::getTemplateInfo()["url"];
}
private static function getTemplateInfo()
{
if (self::$TEMPLATE_INFO == null) {
self::$TEMPLATE_INFO = confToHash(__DIR__ . '/../template.info.txt');
}
return self::$TEMPLATE_INFO;
}
private static function getComboInfo()
{
if (self::$COMBO_INFO == null) {
self::$COMBO_INFO = confToHash(__DIR__ . '/../../../plugins/combo/plugin.info.txt');
}
return self::$COMBO_INFO;
}
public static function getFullQualifyVersion()
{
return "v" . self::getTemplateInfo()['version'] . " (" . self::getTemplateInfo()['date'] . ")";
}
private static function getStrapUrl()
{
return self::getTemplateInfo()["strap"];
}
public static function registerHeaderHandler()
{
/**
* In test, we may test for 4 and for 5
* on the same test, making two request
* This two requests will register the event two times
* To avoid that we use a global variable
*/
global $COMBO_STRAP_METAHEADER_HOOK_ALREADY_REGISTERED;
if ($COMBO_STRAP_METAHEADER_HOOK_ALREADY_REGISTERED !== true) {
global $EVENT_HANDLER;
$method = array('\Combostrap\TplUtility', 'handleBootstrapMetaHeaders');
/**
* A call to a method is via an array and the hook declare a string
* @noinspection PhpParamsInspection
*/
$EVENT_HANDLER->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', null, $method);
$COMBO_STRAP_METAHEADER_HOOK_ALREADY_REGISTERED = true;
}
}
/**
* Add the preloaded CSS resources
* at the end
*/
public static function addPreloadedResources()
{
// For the preload if any
global $preloadedCss;
//
// Note: Adding this css in an animationFrame
// such as https://github.com/jakearchibald/svgomg/blob/master/src/index.html#L183
// would be difficult to test
if (isset($preloadedCss)) {
foreach ($preloadedCss as $link) {
$htmlLink = 'toXhtml();
if (class_exists("ComboStrap\PageEdit") && $html !== null) {
$html = PageEdit::replaceAll($html);
}
} catch (Exception $e) {
$html = "Rendering the slot, returns an error. {$e->getMessage()}";
}
return $html;
} else {
$comboVersion = self::getComboInfo()['version'];
if ($comboVersion !== "1.25") {
TplUtility::msg("The combo plugin is not installed, sidebars automatic bursting will not work", self::LVL_MSG_INFO, "sidebars");
}
return tpl_include_page($slotId, 0, 1);
}
}
private static function getBootStrapMajorVersion()
{
return self::getBootStrapVersion()[0];
}
public static function getSideKickSlotPageName()
{
return TplUtility::getMigratedSlotNameConfValue(
TplUtility::CONF_SIDEKICK_SLOT_PAGE_NAME,
"slot_sidekick",
TplUtility::CONF_SIDEKICK_OLD,
"sidekickbar",
"sidekick_slot"
);
}
public static function getHeaderSlotPageName()
{
return TplUtility::getMigratedSlotNameConfValue(
TplUtility::CONF_HEADER_SLOT_PAGE_NAME,
TplUtility::CONF_SLOT_HEADER_PAGE_NAME_VALUE,
TplUtility::CONF_HEADER_OLD,
TplUtility::CONF_HEADER_OLD_VALUE,
"header_slot"
);
}
public static function getFooterSlotPageName()
{
return self::getMigratedSlotNameConfValue(
TplUtility::CONF_FOOTER_SLOT_PAGE_NAME,
"slot_footer",
TplUtility::CONF_FOOTER_OLD,
"footerbar",
"footer_slot"
);
}
/**
* @param string $key the key configuration
* @param string $value the value
* @return bool
*/
public static function updateConfiguration($key, $value)
{
/**
* Hack to avoid updating during {@link \TestRequest}
* when not asked
* Because the test request environment is wiped out only on the class level,
* the class / test function needs to specifically say that it's open
* to the modification of the configuration
*/
global $_REQUEST;
if (defined('DOKU_UNITTEST') && !isset($_REQUEST[self::COMBO_TEST_UPDATE])) {
/**
* This hack resolves two problems
*
* First one
* this is a test request
* the local.php file has a the `DOKU_TMP_DATA`
* constant in the file and updating the file
* with this method will then update the value of savedir to DOKU_TMP_DATA
* we get then the error
* The datadir ('pages') at DOKU_TMP_DATA/pages is not found
*
*
* Second one
* if in a php test unit, we send a php request two times
* the headers have been already send and the
* {@link msg()} function will send them
* causing the {@link TplUtility::outputBuffer() output buffer check} to fail
*/
global $MSG_shown;
if (isset($MSG_shown) || headers_sent()) {
return false;
} else {
return true;
}
}
$configuration = new Configuration();
$settings = $configuration->getSettings();
$key = "tpl____strap____" . $key;
if (isset($settings[$key])) {
$setting = &$settings[$key];
$setting->update($value);
/**
* We cannot update the setting
* via the configuration object
* We are taking another pass
*/
$writer = new Writer();
if (!$writer->isLocked()) {
try {
$writer->save($settings);
return true;
} catch (Exception $e) {
TplUtility::msg("An error occurred while trying to save automatically the configuration ($key) to the value ($value). Error: " . $e->getMessage());
return false;
}
} else {
TplUtility::msg("The configuration file was locked. The upgrade configuration ($key) value could not be not changed to ($value)");
return false;
}
} else {
/**
* When we run test,
* strap is not always the active template
* and therefore the configurations are not loaded
*/
global $conf;
if ($conf['template'] == TplUtility::TEMPLATE_NAME) {
TplUtility::msg("The configuration ($key) is unknown and was therefore not change to ($value)");
}
}
return false;
}
/**
* Helper to migrate from bar to slot
* @return mixed|string
*/
public static function getMigratedSlotNameConfValue($newConf, $newDefaultValue, $oldConf, $oldDefaultValue, $canonical)
{
$name = tpl_getConf($newConf, null);
if ($name != null) {
return $name;
}
$name = tpl_getConf($oldConf, null);
if ($name != null) {
return $name;
}
/**
* Try to find an old page with the old default value
*/
global $conf;
$startPageName = $conf["start"];
$startPagePath = wikiFN($startPageName);
$directory = dirname($startPagePath);
$childrenDirectories = glob($directory . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR);
foreach ($childrenDirectories as $childrenDirectory) {
$directoryName = pathinfo($childrenDirectory)['filename'];
$dokuFilePath = $directoryName . ":" . $oldDefaultValue;
if (page_exists($dokuFilePath)) {
// store for cache
global $conf;
$conf['tpl']['strap'][$oldConf] = $oldDefaultValue;
return $oldDefaultValue;
}
}
// Performance issue on the upgrade
// the get function is called really often to see if the page is a main page
// and it causes performance issue
// $updated = TplUtility::updateConfiguration($newConf, $name);
// if ($updated) {
// TplUtility::msg("The $newConf configuration was set with the value $name", self::LVL_MSG_INFO, $canonical);
// }
return $newDefaultValue;
}
/**
* Output buffer checks
*
* It should be null before printing otherwise
* you may get a text before the HTML header
* and it mess up the whole page
*/
public static function outputBuffer()
{
$length = ob_get_length();
$ob = "";
if ($length > 0) {
$ob = ob_get_contents();
ob_clean();
global $ACT;
if ($ACT === "show" && !empty($ob)) {
/**
* If you got this problem check that this is not a character before a `
Railbar
$railBarListItems
EOF;
if ($breakpoint != TplUtility::BREAKPOINT_NEVER_NAME) {
$zIndexRailbar = 1000; // A navigation bar (below the drop down because we use it in the search box for auto-completion)
$railBarFixed = <<
$railBarListItems
EOF;
return <<getListItems('action');
$liPageTools = (new PageMenu())->getListItems();
$liSiteTools = (new SiteMenu())->getListItems('action');
// FYI: The below code outputs all menu in mobile (in another HTML layout)
// echo (new \dokuwiki\Menu\MobileMenu())->getDropdown($lang['tools']);
return <<
' . PHP_EOL;
// Print the parts if there is more than one
global $ID;
$idParts = explode(':', $ID);
if (count($idParts) > 1) {
// Print the parts without the last one ($count -1)
$page = "";
for ($i = 0; $i < count($idParts) - 1; $i++) {
$page .= $idParts[$i] . ':';
// Skip home page of the namespace
// if ($page == $conf['start']) continue;
// The last part is the active one
// if ($i == $count) {
// $htmlOutput .= '
';
// } else {
// $htmlOutput .= '
';
// }
$htmlOutput .= '
';
// html_wikilink because the page has the form pagename: and not pagename:pagename
$htmlOutput .= html_wikilink($page);
$htmlOutput .= '
' . PHP_EOL;
// close the breadcrumb
$htmlOutput .= '' . PHP_EOL;
return $htmlOutput;
}
/*
* Function return the page name from an id
* @author Nicolas GERARD
*
* @param string $sep Separator between entries
* @return bool
*/
function getPageTitle($id)
{
// page names
$name = noNSorNS($id);
if (useHeading('navigation')) {
// get page title
$title = p_get_first_heading($id, METADATA_RENDER_USING_SIMPLE_CACHE);
if ($title) {
$name = $title;
}
}
return $name;
}
/**
* This is a fork of tpl_actionlink where I have added the class parameters
*
* Like the action buttons but links
*
* @param string $type action command
* @param string $pre prefix of link
* @param string $suf suffix of link
* @param string $inner innerHML of link
* @param bool $return if true it returns html, otherwise prints
* @param string $class the class to be added
* @return bool|string html or false if no data, true if printed
* @see tpl_get_action
*
* @author Adrian Lang
*/
function renderActionLink($type, $class = '', $pre = '', $suf = '', $inner = '', $return = false)
{
global $lang;
$data = tpl_get_action($type);
if ($data === false) {
return false;
} elseif (!is_array($data)) {
$out = sprintf($data, 'link');
} else {
/**
* @var string $accesskey
* @var string $id
* @var string $method
* @var bool $nofollow
* @var array $params
* @var string $replacement
*/
extract($data);
if (strpos($id, '#') === 0) {
$linktarget = $id;
} else {
$linktarget = wl($id, $params);
}
$caption = $lang['btn_' . $type];
if (strpos($caption, '%s')) {
$caption = sprintf($caption, $replacement);
}
$akey = $addTitle = '';
if ($accesskey) {
$akey = 'accesskey="' . $accesskey . '" ';
$addTitle = ' [' . strtoupper($accesskey) . ']';
}
$rel = $nofollow ? 'rel="nofollow" ' : '';
$out = $pre . tpl_link(
$linktarget, (($inner) ? $inner : $caption),
'class="nav-link action ' . $type . ' ' . $class . '" ' .
$akey . $rel .
'title="' . hsc($caption) . $addTitle . '"', true
) . $suf;
}
if ($return) return $out;
echo $out;
return true;
}
/**
* @return array
* Return the headers needed by this template
*
* @throws Exception
*/
static function getBootstrapMetaHeaders()
{
// The version
$bootstrapVersion = TplUtility::getBootStrapVersion();
if ($bootstrapVersion === false) {
/**
* Strap may be called for test
* by combo
* In this case, the conf may not be reloaded
*/
self::reloadConf();
$bootstrapVersion = TplUtility::getBootStrapVersion();
if ($bootstrapVersion === false) {
throw new Exception("Bootstrap version should not be false");
}
}
$scriptsMeta = self::buildBootstrapMetas($bootstrapVersion);
// if cdn
$useCdn = tpl_getConf(self::CONF_USE_CDN);
// Build the returned Js script array
$jsScripts = array();
foreach ($scriptsMeta as $key => $script) {
$path_parts = pathinfo($script["file"]);
$extension = $path_parts['extension'];
if ($extension === "js") {
$src = DOKU_BASE . "lib/tpl/strap/bootstrap/$bootstrapVersion/" . $script["file"];
if ($useCdn) {
if (isset($script["url"])) {
$src = $script["url"];
}
}
$jsScripts[$key] =
array(
'src' => $src,
'defer' => null
);
if (isset($script['integrity'])) {
$jsScripts[$key]['integrity'] = $script['integrity'];
$jsScripts[$key]['crossorigin'] = 'anonymous';
}
}
}
$css = array();
$cssScript = $scriptsMeta['css'];
$href = DOKU_BASE . "lib/tpl/strap/bootstrap/$bootstrapVersion/" . $cssScript["file"];
if ($useCdn) {
if (isset($script["url"])) {
$href = $script["url"];
}
}
$css['css'] =
array(
'href' => $href,
'rel' => "stylesheet"
);
if (isset($script['integrity'])) {
$css['css']['integrity'] = $script['integrity'];
$css['css']['crossorigin'] = 'anonymous';
}
return array(
'script' => $jsScripts,
'link' => $css
);
}
/**
* @return array - A list of all available stylesheets
* This function is used to build the configuration as a list of files
*/
static function getStylesheetsForMetadataConfiguration()
{
$cssVersionsMetas = self::getStyleSheetsFromJsonFileAsArray();
$listVersionStylesheetMeta = array();
foreach ($cssVersionsMetas as $bootstrapVersion => $cssVersionMeta) {
foreach ($cssVersionMeta as $fileName => $values) {
$listVersionStylesheetMeta[] = $bootstrapVersion . TplUtility::BOOTSTRAP_VERSION_STYLESHEET_SEPARATOR . $fileName;
}
}
return $listVersionStylesheetMeta;
}
/**
*
* @param $version - return only the selected version if set
* @return array - an array of the meta JSON custom files
*/
static function getStyleSheetsFromJsonFileAsArray($version = null)
{
$jsonAsArray = true;
$stylesheetsFile = __DIR__ . '/../bootstrap/bootstrapStylesheet.json';
$styleSheets = json_decode(file_get_contents($stylesheetsFile), $jsonAsArray);
if ($styleSheets == null) {
self::msg("Unable to read the file {$stylesheetsFile} as json");
}
$localStyleSheetsFile = __DIR__ . '/../bootstrap/bootstrapLocal.json';
if (file_exists($localStyleSheetsFile)) {
$localStyleSheets = json_decode(file_get_contents($localStyleSheetsFile), $jsonAsArray);
if ($localStyleSheets == null) {
self::msg("Unable to read the file {$localStyleSheets} as json");
}
foreach ($styleSheets as $bootstrapVersion => &$stylesheetsFiles) {
if (isset($localStyleSheets[$bootstrapVersion])) {
$stylesheetsFiles = array_merge($stylesheetsFiles, $localStyleSheets[$bootstrapVersion]);
}
}
}
if (isset($version)) {
if (!isset($styleSheets[$version])) {
self::msg("The bootstrap version ($version) could not be found in the custom CSS file ($stylesheetsFile, or $localStyleSheetsFile)");
} else {
$styleSheets = $styleSheets[$version];
}
}
/**
* Select Rtl or Ltr
* Stylesheet name may have another level
* with direction property of the language
*
* Bootstrap needs another stylesheet
* See https://getbootstrap.com/docs/5.0/getting-started/rtl/
*/
global $lang;
$direction = $lang["direction"];
if (empty($direction)) {
$direction = "ltr";
}
$directedStyleSheets = [];
foreach ($styleSheets as $name => $styleSheetDefinition) {
if (isset($styleSheetDefinition[$direction])) {
$directedStyleSheets[$name] = $styleSheetDefinition[$direction];
} else {
$directedStyleSheets[$name] = $styleSheetDefinition;
}
}
return $directedStyleSheets;
}
/**
*
* Build from all Bootstrap JSON meta files only one array
* @param $version
* @return array
*
*/
static function buildBootstrapMetas($version)
{
$jsonAsArray = true;
$bootstrapJsonFile = __DIR__ . '/../bootstrap/bootstrapJavascript.json';
$bootstrapMetas = json_decode(file_get_contents($bootstrapJsonFile), $jsonAsArray);
// Decodage problem
if ($bootstrapMetas == null) {
self::msg("Unable to read the file {$bootstrapJsonFile} as json");
return array();
}
if (!isset($bootstrapMetas[$version])) {
self::msg("The bootstrap version ($version) could not be found in the file $bootstrapJsonFile");
return array();
}
$bootstrapMetas = $bootstrapMetas[$version];
// Css
$bootstrapCssFile = TplUtility::getStyleSheetConf();
$bootstrapCustomMetas = self::getStyleSheetsFromJsonFileAsArray($version);
if (!isset($bootstrapCustomMetas[$bootstrapCssFile])) {
self::msg("The bootstrap custom file ($bootstrapCssFile) could not be found in the custom CSS files for the version ($version)");
} else {
$bootstrapMetas['css'] = $bootstrapCustomMetas[$bootstrapCssFile];
}
return $bootstrapMetas;
}
/**
* @param Doku_Event $event
* @param $param
* Function that handle the META HEADER event
* * It will add the Bootstrap Js and CSS
* * Make all script and resources defer
* @throws Exception
*/
static function handleBootstrapMetaHeaders(Doku_Event &$event, $param)
{
$debug = tpl_getConf('debug');
if ($debug) {
self::addAsHtmlComment('Request: ' . json_encode($_REQUEST));
}
$newHeaderTypes = array();
$bootstrapHeaders = self::getBootstrapMetaHeaders();
$eventHeaderTypes = $event->data;
foreach ($eventHeaderTypes as $headerType => $headerData) {
switch ($headerType) {
case "link":
// index, rss, manifest, search, alternate, stylesheet
// delete edit
$bootstrapCss = $bootstrapHeaders[$headerType]['css'];
$headerData[] = $bootstrapCss;
// preload all CSS is an heresy as it creates a FOUC (Flash of non-styled element)
// but we know it only now and this is it
$cssPreloadConf = tpl_getConf(self::CONF_PRELOAD_CSS);
$newLinkData = array();
foreach ($headerData as $linkData) {
switch ($linkData['rel']) {
case 'edit':
break;
case 'preload':
/**
* Preload can be set at the array level with the critical attribute
* If the preload attribute is present
* We get that for instance for css animation style sheet
* that are not needed for rendering
*/
if (isset($linkData["as"])) {
if ($linkData["as"] === "style") {
$newLinkData[] = TplUtility::captureStylePreloadingAndTransformToPreloadCssTag($linkData);
continue 2;
}
}
$newLinkData[] = $linkData;
break;
case 'stylesheet':
if ($cssPreloadConf) {
$newLinkData[] = TplUtility::captureStylePreloadingAndTransformToPreloadCssTag($linkData);
continue 2;
}
$newLinkData[] = $linkData;
break;
default:
$newLinkData[] = $linkData;
break;
}
}
$newHeaderTypes[$headerType] = $newLinkData;
break;
case "script":
/**
* Do we delete the dokuwiki javascript ?
*/
$scriptToDeletes = [];
if (empty($_SERVER['REMOTE_USER']) && tpl_getConf(TplUtility::CONF_DISABLE_BACKEND_JAVASCRIPT, 0)) {
$scriptToDeletes = [
//'JSINFO', Don't delete Jsinfo !! It contains metadata information (that is used to get context)
'js.php'
];
if (TplUtility::getBootStrapMajorVersion() == "5") {
// bs 5 does not depends on jquery
$scriptToDeletes[] = "jquery.php";
}
}
/**
* The new script array
*/
$newScriptData = array();
// A variable to hold the Jquery scripts
// jquery-migrate, jquery, jquery-ui ou jquery.php
// see https://www.dokuwiki.org/config:jquerycdn
$jqueryDokuScripts = array();
foreach ($headerData as $scriptData) {
foreach ($scriptToDeletes as $scriptToDelete) {
if (isset($scriptData["_data"]) && !empty($scriptData["_data"])) {
$haystack = $scriptData["_data"];
} else {
$haystack = $scriptData["src"];
}
if (preg_match("/$scriptToDelete/i", $haystack)) {
continue 2;
}
}
$critical = false;
if (isset($scriptData["critical"])) {
$critical = $scriptData["critical"];
unset($scriptData["critical"]);
}
// defer is only for external resource
// if this is not, this is illegal
if (isset($scriptData["src"])) {
if (!$critical) {
$scriptData['defer'] = null;
}
}
if (isset($scriptData["type"])) {
$type = strtolower($scriptData["type"]);
if ($type == "text/javascript") {
unset($scriptData["type"]);
}
}
// The charset attribute on the script element is obsolete.
if (isset($scriptData["charset"])) {
unset($scriptData["charset"]);
}
// Jquery ?
$jqueryFound = false;
// script may also be just an online script without the src attribute
if (array_key_exists('src', $scriptData)) {
$jqueryFound = strpos($scriptData['src'], 'jquery');
}
if ($jqueryFound === false) {
$newScriptData[] = $scriptData;
} else {
$jqueryDokuScripts[] = $scriptData;
}
}
// Add Jquery at the beginning
$boostrapMajorVersion = TplUtility::getBootStrapMajorVersion();
if ($boostrapMajorVersion == "4") {
if (
empty($_SERVER['REMOTE_USER'])
&& tpl_getConf(self::CONF_JQUERY_DOKU) == 0
) {
// We take the Jquery of Bootstrap
$newScriptData = array_merge($bootstrapHeaders[$headerType], $newScriptData);
} else {
// Logged in
// We take the Jqueries of doku and we add Bootstrap
$newScriptData = array_merge($jqueryDokuScripts, $newScriptData); // js
// We had popper of Bootstrap
$newScriptData[] = $bootstrapHeaders[$headerType]['popper'];
// We had the js of Bootstrap
$newScriptData[] = $bootstrapHeaders[$headerType]['js'];
}
} else {
// There is no JQuery in 5
// We had the js of Bootstrap and popper
// Add Jquery before the js.php
$newScriptData = array_merge($jqueryDokuScripts, $newScriptData); // js
// Then add at the top of the top (first of the first) bootstrap
// Why ? Because Jquery should be last to be able to see the missing icon
// https://stackoverflow.com/questions/17367736/jquery-ui-dialog-missing-close-icon
$bootstrap[] = $bootstrapHeaders[$headerType]['popper'];
$bootstrap[] = $bootstrapHeaders[$headerType]['js'];
$newScriptData = array_merge($bootstrap, $newScriptData);
}
$newHeaderTypes[$headerType] = $newScriptData;
break;
case "meta":
$newHeaderData = array();
foreach ($headerData as $metaData) {
// Content should never be null
// Name may change
// https://www.w3.org/TR/html4/struct/global.html#edef-META
if (!key_exists("content", $metaData)) {
$message = "Strap - The head meta (" . print_r($metaData, true) . ") does not have a content property";
msg($message, -1, "", "", MSG_ADMINS_ONLY);
if (defined('DOKU_UNITTEST')
) {
throw new \RuntimeException($message);
}
} else {
$content = $metaData["content"];
if (empty($content)) {
$messageEmpty = "Strap - the below head meta has an empty content property (" . print_r($metaData, true) . ")";
msg($messageEmpty, -1, "", "", MSG_ADMINS_ONLY);
if (defined('DOKU_UNITTEST')
) {
throw new \RuntimeException($messageEmpty);
}
} else {
$newHeaderData[] = $metaData;
}
}
}
$newHeaderTypes[$headerType] = $newHeaderData;
break;
case "noscript": // https://github.com/ComboStrap/dokuwiki-plugin-gtm/blob/master/action.php#L32
case "style":
$newHeaderTypes[$headerType] = $headerData;
break;
default:
$message = "Strap - The header type ($headerType) is unknown and was not controlled.";
$newHeaderTypes[$headerType] = $headerData;
msg($message, -1, "", "", MSG_ADMINS_ONLY);
if (defined('DOKU_UNITTEST')
) {
throw new \RuntimeException($message);
}
}
}
if ($debug) {
self::addAsHtmlComment('Script Header : ' . json_encode($newHeaderTypes['script']));
}
$event->data = $newHeaderTypes;
}
/**
* Returns the icon link as created by https://realfavicongenerator.net/
*
*
*
* @return string
*/
static function renderFaviconMetaLinks()
{
$return = '';
// FavIcon.ico
$possibleLocation = array(':wiki:favicon.ico', ':favicon.ico', 'images/favicon.ico');
$return .= '' . NL;
// Icon Png
$possibleLocation = array(':wiki:favicon-32x32.png', ':favicon-32x32.png', 'images/favicon-32x32.png');
$return .= '';
$possibleLocation = array(':wiki:favicon-16x16.png', ':favicon-16x16.png', 'images/favicon-16x16.png');
$return .= '';
// Apple touch icon
$possibleLocation = array(':wiki:apple-touch-icon.png', ':apple-touch-icon.png', 'images/apple-touch-icon.png');
$return .= '' . NL;
return $return;
}
static function renderPageTitle()
{
global $conf;
global $ID;
$title = tpl_pagetitle($ID, true) . ' |' . $conf["title"];
// trigger event here
Event::createAndTrigger('TPL_TITLE_OUTPUT', $title, '\ComboStrap\TplUtility::callBackPageTitle', true);
return true;
}
/**
* Print the title that we get back from the event TPL_TITLE_OUTPUT
* triggered by the function {@link tpl_strap_title()}
* @param $title
*/
static function callBackPageTitle($title)
{
echo $title;
}
/**
*
* Set a template conf value
*
* To set a template configuration, you need to first load them
* and there is no set function in template.php
*
* @param $confName - the configuration name
* @param $confValue - the configuration value
*/
static function setConf($confName, $confValue)
{
/**
* Env variable
*/
global $conf;
$template = $conf['template'];
if ($template != "strap") {
throw new \RuntimeException("This is not the strap template, in test, active it in setup");
}
/**
* Make sure to load the configuration first by calling getConf
*/
$actualValue = tpl_getConf($confName);
if ($actualValue === false) {
self::reloadConf();
// Check that the conf was loaded
if (tpl_getConf($confName) === false) {
throw new \RuntimeException("The configuration (" . $confName . ") returns no value or has no default");
}
}
$conf['tpl'][$template][$confName] = $confValue;
}
/**
* When running multiple test, the function {@link tpl_getConf()}
* does not reload the configuration twice
*/
static function reloadConf()
{
/**
* Env variable
*/
global $conf;
$template = $conf['template'];
$tconf = tpl_loadConfig();
if ($tconf !== false) {
foreach ($tconf as $key => $value) {
if (isset($conf['tpl'][$template][$key])) continue;
$conf['tpl'][$template][$key] = $value;
}
}
}
/**
* Send a message to a manager and log it
* Fail if in test
* @param string $message
* @param int $level - the level see LVL constant
* @param string $canonical - the canonical
*/
static function msg($message, $level = self::LVL_MSG_ERROR, $canonical = "strap")
{
$strapUrl = self::getStrapUrl();
$prefix = "Strap";
$prefix = '' . ucfirst($canonical) . '';
$htmlMsg = $prefix . " - " . $message;
if ($level != self::LVL_MSG_DEBUG) {
msg($htmlMsg, $level, '', '', MSG_MANAGERS_ONLY);
}
/**
* Print to a log file
* Note: {@link dbg()} dbg print to the web page
*/
$prefix = 'strap';
if ($canonical != null) {
$prefix .= ' - ' . $canonical;
}
$msg = $prefix . ' - ' . $message;
dbglog($msg);
if (defined('DOKU_UNITTEST') && ($level == self::LVL_MSG_WARNING || $level == self::LVL_MSG_ERROR)) {
throw new \RuntimeException($msg);
}
}
/**
* @param bool $prependTOC
* @return false|string - Adapted from {@link tpl_content()} to return the HTML
* Call {@link html_show()} from {@link Show::tplContent()}
*/
static function tpl_content(bool $prependTOC = true)
{
global $ACT;
global $REV;
global $DATE_AT;
global $INFO;
$INFO['prependTOC'] = $prependTOC;
try {
ob_start();
if (
class_exists("ComboStrap\Page")
&& $ACT === "show" // show only
&& ($REV === 0 && $DATE_AT === "") // ro revisions
) {
/**
* The code below replace the other block
* to take the snippet management into account
* (ie we write them when the {@link HtmlDocument::storeContent() document is stored into cache)
*/
global $ID;
/**
* The action null does nothing.
* See {@link Event::trigger()}
*/
Event::createAndTrigger('TPL_ACT_RENDER', $ACT, null);
/**
* The code below replace {@link html_show()}
*/
$html_output = Page::createPageFromId($ID)
->toXhtml();
// section editing show only if not a revision and $ACT=show
// which is the case in this block
$showEdit = true;
$html_output = html_secedit($html_output, $showEdit);
/**
* Add the buffer (eventually)
*
* Not needed with our code, may be with other plugins, it should not as the
* syntax plugin should use the {@link \Doku_Renderer::$doc)
*
*/
$html_output .= ob_get_clean();
} else {
$comboVersion = self::getComboInfo()['version'];
if ($comboVersion >= "1.25") {
TplUtility::msg("The strap template has been deprecated. From the version 1.25, Combo should be used with the standard dokuwiki template.", self::LVL_MSG_WARNING);
}
Event::createAndTrigger('TPL_ACT_RENDER', $ACT, 'tpl_content_core');
$html_output = ob_get_clean();
}
/**
* The action null does nothing.
* See {@link Event::trigger()}
*/
Event::createAndTrigger('TPL_CONTENT_DISPLAY', $html_output, null);
return $html_output;
} catch (Exception $e) {
$message = "Unfortunately, an error has occurred during the rendering of the main content. The error was logged.";
dbglog($message . " Error: " . $e->getTraceAsString());
return $message;
}
}
static function getPageHeader(): string
{
$navBarPageName = TplUtility::getHeaderSlotPageName();
if ($id = page_findnearest($navBarPageName)) {
$header = TplUtility::renderSlot($id);
} else {
$domain = self::getApexDomainUrl();
$header = '
Welcome to the Strap template.
If you don\'t known the ComboStrap, it\'s recommended to follow the Getting Started Guide.
Otherwise, to create a menu bar in the header, create a page with the id (' . html_wikilink(':' . $navBarPageName) . ') and the menubar component.