Summary: Talk page for B3 blog.
Maintainer: Petko
Users: +4 (View / Edit)

This space is for User-contributed commentary and notes. Please include your name and a date along with your comment.

Should subheader hide "(# comments)" when comments are disabled?

Suggesting the presence of comments while they are actually not there and/or are not made available, might puzzle the reader and is probably just disfunctional.
The subheader includes the string "(# comments)" even if/when comments are disabled, which I feel should perhaps be avoided (or at least made an option).

Luigi January 1, 2023

Thanks for the suggestion! Agreed - fixed in version 20230101 (just released). Happy new year! --Petko

Thanks to you, and happy new year, Petko! — Luigi January 1, 2023

Bug with subheader on post pages

The subheader / “post metadata” block, e.g. Sunday, December 28, 3017, 2:16 AM by SaidAchmiz in Features (0 comments), works just fine on post listing pages, but on individual post pages it does not work. Instead it gets rendered as “ (0 comments)” (and gets wrapped in a pre block, due to the leading space).

Based on testing, it seems like the problem is somewhere around line 266 of the script, i.e. $header = $B3["TemplateSubHeader"];. This seems never to get qualified on post pages; if I replace it with such code:

  global $pagename;
  $header = Qualify($pagename, $B3["TemplateSubHeader"]);

… then it works. (Yes, the global $pagename is necessary; it does not work otherwise. And yes, it must be there; adding $pagename instead to the list of globals on line 255 does not work. I tested both of these things a half-dozen times, because they seemed to make no sense, but that’s definitely what I am seeing.)

I cannot figure out what is happening here or why…

Said Achmiz December 11, 2021, at 11:37 AM

It seems to work here, here, here (pass="b345"), and here with the latest version 20201019. You may have something different in your installation? --Petko December 12, 2021, at 04:50 PM

How to include   in the template file

In config/local.php I configured a template file for new posts:

SDVA($B3, array(
  'DefaultText' => "page:Blog.Template",

I found that HTML entity   expands to   when the template is included. I also found the following work-around:

SDVA($B3, array(
  # work-around for ' ' expanding to ' ': in Blog.Template use '{$$nbsp}' instead of ' '
  'DefaultText' => "page:Blog.Template nbsp=' '",

SteP November 11, 2020, at 07:46 AM

Robust editing

Since blog posts are made from structured wiki pages, it could be easy to have them getting out of the blog system trails when dedicated anchors are missing from the post page. Could the trailing post anchors [[#b3comments]] and [[#b3end]] defaulting to the end of the page when not found in the blog post in order to preserve the post navigation trail? -- Dfaure November 05, 2020, at 11:50 AM

Trail links with pagename=OtherGroup.OtherPage

In Publish.Publish I have (:b3-list newpostform=1:). All site articles are created in the Publish group. Then in Main.10 I have (:b3-list newpostform=0 pagename=Publish.Publish tags=main:) to list in the Main.10 page all articles tagged 'main'. In Main.10 the list trail link for older pages is …/Publish/Publish?p=2. I would like it to be …/Main/10?p=2 instead. Is this possible and how? It seems to me that the trail links should always stay in the same page and not lead to the pagename in the markup because there B3-list will almost certainly have different arguments (tags, newpostform, etc.) unrelated and possibly conflicting with the ones in the current page. -- SteP October 18, 2020, at 02:58 PM

The initial goal to enable listings in a different page or group was to include the latest few posts in a wiki landing page, in a very brief format -- just titles, -- and let people open the blog group homepage for the actual blog. I now see that your case is also reasonable so I've added $B3['ListSelfLink'] to the configuration for version 20201019. --Petko October 19, 2020, at 02:42 PM

Thank you, Petko, it works fine! --SteP October 19, 2020, at 03:07 PM

[REQ] split #nointro before #more

Feature request (proposed implementation attachedΔ).


I think someone else may need this feature. I want to show a blog post page with a picture on top then some intro text then some more text. I want to list blog entries with only the intro text (no picture). Currently this isn't possible, because the page text is split at the [[#more]] anchor to determine the intro part, which would include the picture attach markup. Therefore, (:b3-list:) would display the picture, which instead I want to show only in the blog post page view.


The attached patch implements an additional cut point, [[#nointro]], to be optionally inserted in the intro paragraph, e.g., before [[#more]]. Then the intro text is split into two parts, $nointro and $intro proper. The latter doesn't include the content of the former.


Optionally add [[#nointro]] to keep (:b3-list:) from showing any markup from the beginning of the page to [[#nointro]], which must be placed inside the introduction text. [[#nointro]] has no effect on the page view.

Would you consider adding this feature? Thank you --SteP January 28, 2020, at 11:08 AM

You could do (:if name {$Name}:)Attach:ClaudiaSchiffer.jpg(:if:) in the page -- existing wiki markup, no need to learn a new one. --Petko January 28, 2020, at 11:53 AM

Ah, yes, much simpler. Thank you. --SteP January 28, 2020, at 03:14 PM

If it is only one picture, you could place the picture before the [[#b3]] anchor floating right:
%rfloat% Attach:MeganFox.jpg
This way it is not processed by B3 and is less to type. --Petko January 29, 2020, at 12:05 PM

Thank you, it is more than one picture. But I repurposed your suggestion for a PTV that my TemplateIncludeIntro sets and my TemplateIncludeIntro uses. I find that placing the PTV at the very top of the template is cleaner and more self-documenting than dispersing it in the template body where an author could accidentally delete it. --SteP January 29, 2020, at 01:11 PM

(:share:) in B3 template

Issue report: When (:share:) is inside the B3 DefaultText template it is expanded to (:share page="Blog.B3-template":). If the expansion is intentional, is there a way to disable it?

In config.php:

SDVA($B3, array(
  'DefaultText' => 'page:Blog.B3-template',




(:b3-list newpostform=1:)

Creating a new blog entry initializes the page to:

Author: Your Name
Date: 2020-01-27 19:33 +0000
Tags: Uncategorized
Comments: Open

(:share page="Blog.B3-template" :)


--SteP January 27, 2020, at 10:01 PM

Thanks, this should be fixed in ShareButtons 20200128. --Petko January 28, 2020, at 07:19 AM

I can confirm it is fixed. Thank you. --SteP January 28, 2020, at 08:41 AM

ShareButtons and RSS feed

Moved to ShareButtons-Talk.

Localized page title

To be able to reuse the in-page (:title:) markup for the blog entry title, I added to my config.php

$B3['TemplateNewPost'] = '(:messages:)
Title: {$Titlespaced}
Author: {author}
Date: {date}
Tags: {tags}
Comments: {comments}



which simply replaces {title} with {$Titlespaced}. This works fine for displaying the page, but not for the RSS feed because it turns all item titles into "{$Titlespaced}". So I had to patch b3.log, see cookbook-b3-RSS-item-title.diff.txtΔ . Can you think of other places where I need similar changes to support Titlespaced? I need Titlespaced because it allows to present localized feeds with the help of Cookbook.MultiLanguageViews. Perhaps you could add this patch to the recipe if you think it can be useful to others. SteP January 24, 2020, at 07:18 PM

Please try version 20200125 -- in your case with different (:title-XX ...:) directives simply remove the "Title:" line. It is only included to simplify the blog entry creation, but not required. With that, your titles in other languages should be readable -- please report.

BTW you probably somehow need to add a ?lang=XX query string to the $B3['LinkRSS'] entry, something like: --Petko January 25, 2020, at 04:20 PM

  $B3['LinkRSS'] = '<a type="application/rss+xml" href="{$PageUrl}?action=b3rss&amp;lang={$Lang}">$[RSS feed]</a>';

Thank you for the changes! I haven't actually tested them (I will be able to in a few hours) but reviewed the new code and I think it should work well for my case. Thank you also for reminding me about adding ?lang=XX.

Later: Tested, works and meets my needs. Thank you! --SteP January 26, 2020, at 02:20 PM

Localized Tag links

Now, there's something else that I did to improve compatibility between B33 and MultiLanguageViews: replace {tag} with a localized tag name in blog listings. I didn't have time to figure out how to pull the tag page $Title, so I pulled a PTV from each page. However, I would rather pull the (:title ...:) for consistency and ease of use. Currently my config.php replaces function B3Var with function modB3Vars, in which I change the case for 'Tags' to:

    foreach ($tags as $tag) {
      $newpage = MakePageName($pn, $tag);
      if ($newpage) {
        # if a PTV $:<lang>TagName is set it's the localized tag name
        global $Lang; #SteP# defined by cookbook MultiLanguageViews
        $value = trim(PageVar($newpage, "$:${Lang}TagName"));
        if ($value>'') $tag = $value;
        $links[] = "[[$newpage|$tag]]";

The above is hooked with

SDVA($FmtPV, array(
  '$B3Tags'   => 'modB3Vars($group, $name, "Tags")',

Thanks for your thoughts. --SteP January 25, 2020, at 04:45 PM

Would this work by simply use $links[] = "[[$newpage|+]]"; on this line? --Petko January 26, 2020, at 07:16 AM

Yes, of course, it works and meets my needs. Thank you. --SteP January 26, 2020, at 08:51 AM

Localized Date/Time stamps

Yet another thing I do on my test site is formatting blog entry dates differently according to the current language. This is how:

  if ($x == 'Date') {
    $stamp = strtotime($value);
    #SteP- $date = strftime($TimeFmt, $stamp);
    # I want textual month representations but don't know if the server keeps all locales installed.
    # So, instead of using strftime %B after i.e. setlocale("en_GB"), I rely on XL pages.
    $dmy = explode('-', strftime('%e-%m-%Y', $stamp));
    $date = "${dmy[0]} $[${dmy[1]}%B] ${dmy[2]}";

    #SteP# changed "date" to "nob3-date" otherwise b3.js will change the date in the browser
    return "%nob3-date stamp-$stamp% $date%%";

hooked with

SDVA($FmtPV, array(
  '$BDate'   => 'modB3Vars($group, $name, "Date")',

To provide equivalent functionality, would you consider adding a way to invoke a user-provided function to format the time in the body of 'Date' in function B3Vars?

The code above consists of two changes but only the first one deals with time formatting. That's the important one for which a function hook could be defined.

The second change is a quick-fix for an issue I experienced in my local test environment: even though I was able to change the date format within function modB3Vars, the time format in the rendered page did not change accordingly. On a hunch I tried changing the class name from %date% to %b3-date% and the localized time format was finally displayed. I suppose this weird interaction is due to my local configuration. For the time being I'm content with this quick-fix, and I will look for the culprit when I have more time. -- SteP January 25, 2020, at 07:08 PM

All dates are already localized by the browser if you keep the $B3['TimeFmt'] variable parsable -- the pub/b3/b3.js script rewrites all dates in the locale of the visitor -- if the system is in French, it will show the date in French (different formats for Canada, France and Switzerland). See samples (B3 uses the default date format without a locale argument). --Petko January 25, 2020, at 07:42 PM

I wasn't clear enough, sorry. By "localized" I meant "translated" according to MultiLanguageViews so that if the currently selected language is "fr" the listing date follows French conventions but if the user selects language "de" the listing date follows German conventions, and so on. All the features we have discussed so far (title, tags, date) aim at integration with a multi-language site in which the same page can be served in different languages. --SteP January 26, 2020, at 06:19 AM

I can see the value of having the month name and/or the weekday name so I'll update the b3.js script to use configurable date options, but this will be done in the browser, not in PHP and not in XLPage. It will also be possible to select a locale but the format sequence will still be what is custom for that language. Also the time stamps will be in the local timezone of the visitor, not of the server. See News.2018-04-14 for the implementation, both on the page date subheader, and on the comments -- hold your mouse cursor over the date to see the original date. Also try launching your browser with a different locale like LANGUAGE=bg_BG.UTF-8 chromium-browser where bg_BG can be fr_FR, it_IT or another locale. You can obviously select to override these functions in your wiki, then indeed use a different wikistyle than %date stamp-...% to prevent the b3.js function to rewrite the dates. I'll upload and document this later today. --Petko January 26, 2020, at 08:29 AM

If I understand correctly, with these changes the dates will be formatted according to the browser's locale. Therefore for a visitor from France (LANGUAGE=FR_fr) and a page for which the MultiLanguageViews language selection is "en" (English) the page text would be English (if the page includes an English translation) but the listing date would be French. Probably this wouldn't be what I need. As you wrote, I can override %date% and the call to B3Vars('Date') to keep the MultiLanguageViews selection authoritative for date formats. --SteP January 26, 2020, at 01:10 PM

You can set a 'locale' entry to force a language if you want, see B3#dates. Something like $B3['DateOptions']['locale'] = $Lang; after including langviews.php and before including b3.php. Try it, and indeed if it doesn't suit you, do your own thing. --Petko January 26, 2020, at 01:26 PM

I think forcing the browser's locale will not help unless the client keeps multiple locales installed. This is similar to a website admin not controlling which locales are installed on the server. This uncertainty is the reason led me to XL pages for the translation of weekdays. Anyway, I tested your new update and it works very well. It is an ideal solution for a single language site. Thank you again for your support. --SteP January 26, 2020, at 02:20 PM

Recent browsers have all locales in them, regardless of what locales are compiled on the system. Those that don't or can't will show the date numerically in the format that would be expected on that computer. --Petko January 26, 2020, at 02:33 PM

Invalid XML

B3 generates invalid XML for its RSS feed when the page title includes an ampersand (or possibly other characters also, I have not checked). (The validator at https://validator.w3.org/feed/ can be used to verify this.) The fix is to change line 659 of b3.php to:

 $ititle = PHSC($attr['Title'], ENT_NOQUOTES);

Said Achmiz June 10, 2019, at 02:01 AM

Thank you, fixed this and also for the Author, and converted to numeric HTML entities per advice from the validator. --Petko June 10, 2019, at 09:08 PM


27 janvier 2018 Magnifique, grand merci

 B3 est compatible avec Cookbook:PerGroupSubDirectories ? Merci, FidelioEspoir

C'est censé fonctionner avec les différentes configurations, quel est le problème? --Petko January 27, 2018, at 08:25 AM Plus de problème. Merci

Il n'y a rien entre B3 et PerGroupSubDirectories qui serait en conflit - mais en le désactivant, si B3 fonctionne on saura que si. Quelle est la configuration de B3? Quelle est la configuration du wiki? Quelle est la version de PmWiki et de PHP? Quels sont les versions des autres modules/recettes installés? Puis-je voir et tester l'installation? -- (e-mail) -> mailto:5ko [snail] 5ko [period] fr Petko January 28, 2018, at 09:09 AM

Fewer HTTP requests

To optimize performance of my sites, I like to make sure that external files such as JS files, stylesheets, etc., only get linked if they are needed on a particular page. (This is especially important for visitors with poor internet connections, who suffer when their browser must make many HTTP requests to load a page.) The B3 recipe loads b3.css and b3.js for all pages, but in fact these files are only needed on B3 blog listing and blog post pages!

Here is what I did to correct this. I replaced lines 164 and 165 of b3.php with this function:

function B3AttachAuxiliaryFiles() {
	static $ran_once = false;
	if (!$ran_once) {
		global $HTMLHeaderFmt, $HTMLFooterFmt, $B3;
		SDVA($HTMLHeaderFmt, array('b3css' => "<link rel='stylesheet' href='{$B3['DirUrl']}/b3.css' type='text/css' />"));
		SDVA($HTMLFooterFmt, array('b3js' => "<script type='text/javascript' src='{$B3['DirUrl']}/b3.js' async></script>"));
	$ran_once = true;

Then, I inserted a call to B3AttachAuxiliaryFiles() into the FmtB3(), FmtB3List(), and FmtB3Comment() functions (near the top, to ensure it would happen no matter which return statement was followed).

This ensures that the JS and CSS files are linked to the page, only if the page has some need for them. I have tested this solution and it seems to work just fine. (I have also done something similar for some other recipes I use/maintain/etc.; this has quite reduced the number of HTTP requests required to load any particular page of one of my wikis.)

Said Achmiz January 05, 2018, at 12:53 AM

Excellent suggestion. I implemented it in 20180105 a little differently, adding the html snippets in the $B3 array (also to allow customization) and directly calling SDVA() from FmtB3() and from FmtB3List(). There should only be calls to FmtB3Comment() from inside a [[#b3]]...[[#b3end]] block, ie FmtB3() will be called anyway, and SDVA() sets the value only when unset, ie once. Thanks. --Petko January 05, 2018, at 07:07 AM

It works, thanks! —Said Achmiz January 05, 2018, at 12:46 PM

PmWeekly question

Great job so far!! Have you automated some of the postings, or are you writing them all up by hand? For example: subversion commit messages, recent updates from the website, etc. — I'm wondering whether you have something on the back end compiling your newsletter drafts before they're published, or if you're putting the information together yourself manually. The one feature I wish were still working is xmlrpc. There was a time that I could edit my entire wiki via Ecto. Easy to use sidebar of groups & pages, tweak the markup, publish, and it would update the website. I could maintain all my customer websites this way quite easily. It never made it past PHP 5 compatibility, and I'm not up on my PHP enough to debug that one. Works great with blogs, but even better with huge wiki sites with many groups. XES September 05, 2017, at 11:56 AM

I use a [[#template]]...[[#templateend]] which you can see here, and I just fill in the blanks (manually, from the different sources and RecentChanges pages). It takes me about 1 hour to compile the post. I've never used xmlrpc. --Petko September 05, 2017, at 12:55 PM

Talk page for the B3 blog recipe (users).