Page 1 of 2

HTML META-Tags with embedded Serendipity

Posted: Sun Aug 17, 2008 1:29 pm
by pagedown
I am using Serendipity with Bulletproof theme and HTML Meta-Tags plugin in embedded mode with the following approach.

Code: Select all

<?php
  ob_start();
  chdir("/home/account/public_html/serendipity/");
  require("index.php");
  chdir("/home/account/public_html/");
  $serendipity_contents = ob_get_contents();
  ob_end_clean();
?>
<head>
<title>Blog title</title>
...
</head>
<body>
<?php
echo $serendipity_contents; // Print the variable
?>
</body>

The Meta-Tag plugin has no effect of course and for every post I always get the Title etc from my wrapper blog page.

I presume there is no way round this and that the solution is to not use this approach at all. I just want to get my normal website header, menu and footer wrapped round my blog. Is there a better way to achieve this which will allow the Meta-Tage plugin to work?

Thanks

Mike

Posted: Sun Aug 17, 2008 3:51 pm
by Don Chambers
The vast majority of bulletproof's index.tpl code does not get loaded for embedded installs.... which means this line is not loaded:

Code: Select all

{serendipity_hookPlugin hook="frontend_header"}
Since that is not loading, no plugin that calls that hook is going to work, including the HTML Meta plugin.

I went through something similar to this recently. The solution was to add this in your head section:

Code: Select all

<?php
     serendipity_plugin_api::hook_event('frontend_header', &$serendipity);
?>
If you want to take that a bit further, I actually did this which seemed to work with the HTML Meta plugin:

Code: Select all

    <title><?php
    if($serendipity['head_title']) {
        echo $serendipity['head_title'];
    } else {
        echo $serendipity['blogTitle'];
    }
    if ($serendipity['head_subtitle']) {
        echo ' | ' . $serendipity['head_subtitle'];
    }
  ?></title>

<?php
     serendipity_plugin_api::hook_event('frontend_header', &$serendipity);
?>
You may also need this prior to the code above:

Code: Select all

global $serendipity; 

Posted: Sun Aug 17, 2008 4:37 pm
by pagedown
Don,

Excellent answer. Works perfectly.

Thanks very much.

Mike

Posted: Sun Aug 17, 2008 5:40 pm
by Don Chambers
No problem Mike - glad it worked.

Posted: Sun Aug 24, 2008 9:43 pm
by Don Chambers
Mike - can you test something for me? I just noticed a problem with the plugin in an embedded installation and need to know if it is this particular installation, or happens with all embedded installs.

The problem is the generation of a page's head_title and head_subtitle, which many template uses as <h1> and <h2> headings. In bulletproof, these tags are generated in the banner:

Code: Select all

<h1><span class="{if $template_option.firbtitle == 'false'}in{/if}visible"><a class="homelink1" href="{$serendipityBaseURL}">{$head_title|@default:$blogTitle|truncate:80:" ..."}</a></span></h1>
<h2><span class="{if $template_option.firbdescr == 'false'}in{/if}visible"><a class="homelink2" href="{$serendipityBaseURL}">{$head_subtitle|@default:$blogDescription}</a></span></h2>
Frontpage & default condition: First line (<h1>) is blog title. Second line (<h2>) is blog description.
Detailed Entry: First line is ENTRY title. Second line is BLOG title.

The problem is seen on detailed entry pages:

The first line is the entry specific <title> element, not the entry title
The second line never changes… it is always the blog description, and never changes to the blog title like it is supposed to because that variable is blank, and the template's default kicks in: $head_subtitle|@default:$blogDescription.

In /plugins/serendipity_event_metadesc/serendipity_event_metadesc.php, on line 153, you will find this:

Code: Select all

                        // If we modified the <title>...
                        if (!empty($this->meta_title)) {
                            // We've messed up the banner.  Put it back the way it was.
                            // Set smarty variables for banner back to normal "entry title - blog description"
                            $serendipity['smarty']->assign(
                                array(
                                'head_title'    => $this->save_title,
                                'head_subtitle' => $this->save_subtitle
                                )
                            );
Can you change it to this, which is only adding 4 html comments to the page output:

Code: Select all

                        // If we modified the <title>...
                        if (!empty($this->meta_title)) {
                            // We've messed up the banner.  Put it back the way it was.
                            // Set smarty variables for banner back to normal "entry title - blog description"
                            echo '<!-- save_title, which should be entry title, is: "' . $this->save_title . '" -->'. "\n";
                            echo '<!-- save_subtitle, which should be blog title, is: "'. $this->save_subtitle . '" -->'. "\n";
                            $serendipity['smarty']->assign(
                                array(
                                'head_title'    => $this->save_title,
                                'head_subtitle' => $this->save_subtitle
                                )
                            );
                            echo '<!-- smarty head_title, which should be entry title, is: "' . $serendipity['head_title'] . '" -->'. "\n";
                            echo '<!-- smarty head_subtitle, which should be blog title is: "' . $serendipity['head_subtitle'] . '" -->'. "\n";
                        }
When I make this change, then view a detailed entry's source, this is unfortunately what I am seeing:

Code: Select all

    <title>entry specific page title element</title>
<!-- save_title, which should be entry title, is: "HTML Meta Plugin test entry" -->
<!-- save_subtitle, which should be blog title, is: "This is the blog's title" -->
<!-- smarty head_title, which should be entry title, is: "entry specific page title element" -->
<!-- smarty head_subtitle, which should be blog title is: "" -->
In other words, the code that should restore the smarty head_title and head_subtitle does not appear to be working... the head_title is still equal to the <title> element, and the head_subtitle is blank, which is a leftover from the code at line 139:

Code: Select all

                            $serendipity['head_title'] = htmlspecialchars($this->meta_title);
                            // Clear the subtitle (many templates use it along with the title)
                            $serendipity['head_subtitle'] = '';
Anyway, if you can check your installation to see if it behaves the same, I would appreciate it.

If anyone has an explanation as to why, or how to fix, I would appreciate that as well! :wink:

Posted: Mon Aug 25, 2008 10:55 pm
by judebert
Garvin, we seem to have an implementation error.

Plugins -- at least the metadesc plugin, and I'm betting the head_nugget plugin, possibly others -- generate output with echo or print when 'frontend_header' is hooked. They may do other stuff, too, but the output is a problem for embedded installations.

The embedding script is expected to provide the <head> and <body> tags. When they capture the output of s9y's index.php, they get the output intended for the <head> with no way to separate it from the <body>. Like <meta> tags: those get output, right on top of the banner <div>, with no way to separate them.

The obvious solution is to remove the frontend_header hook from index.tpl and call it directly from the embedding script. But that's also wrong: metadesc (and possibly other plugins) are counting on frontend_header for more than just output. They restore variables or otherwise attempt to modify Serendipity's behavior. By the time the embedding script can hook anything, the opportunity to modify behavior is long gone, with all the templates processed and the body output finalized.

The less obvious solution is to capture all the output from the frontend_header hook into a variable, like $serendipity['frontend_header_output']. The embedding script can then fulfill its duties as keeper of <head> and <body>, outputting the header data in the <head> or ignoring it, as it sees fit. This will require either changing all the plugins (and notifying plugin authors to never output <head> data directly), or modifying all the templates to capture the hook output when embedded (which I really hate), or simply modifying the plugin API to capture any output when it hooks frontend_header.

Guess which solution I like best.

Or did I miss something obvious? I'd like to start making changes, but I'm holding off for your review.

Posted: Tue Aug 26, 2008 5:07 am
by Don Chambers
And just in case it is not already obvious, Judebert and I have been working on this together today and I fully endorse his recommendation subject to any technical issues we might be missing. Definitely have to keep this one BC, but I think - from what little I know - that the recommended solution accomplishes that objective.

Posted: Tue Aug 26, 2008 11:57 am
by garvinhicking
Hi!

In that cases, plugins that use frontend_header to modify functionality are not coded well. They should use frontend_configure or some other header preferably instead...

Changing plugins to not echo stuff in frontend_header is the wrong way IMHO, because that is the actually expected functionality. We must expect that people who created own plugins used that specifically. Abusing plugins to change s9y variables in frontend_header is definitely the worse approach that should be corrected.

The other solution of embedding is to not unconditionally include the s9y index.php file, but rather follow the steps of index.php in your own embedding script. This way you can enforce calling frontend_header in the PHP workflow, unrelated to template processing, and capture it before any output takes place.

I don't see another solution right now. In fact, I do not recommend embedding like that any more, because it was in the pre-smarty era. Now I usually recommend to call your framework's PHP code through the s9y plugin API and/or through the {php} in smarty templates.

Regards,
Garvin

Posted: Tue Aug 26, 2008 4:51 pm
by judebert
garvinhicking wrote:In that cases, plugins that use frontend_header to modify functionality are not coded well. They should use frontend_configure or some other header preferably instead...
I agree with that. But the metadesc plugin tries to change the <title>; it needs one hook before the <head> section is printed so it can change the variables that eventually become the <title>, and another hook afterwards so it can restore the original values. It uses genpage for the initial switch, so it needs something after <head> but before <body>. frontend_configure comes too early; nothing I know of is called in between except frontend_header. Can you recommend a hook for me to update the plugin with?
garvinhicking wrote:Changing plugins to not echo stuff in frontend_header is the wrong way IMHO, because that is the actually expected functionality. We must expect that people who created own plugins used that specifically. Abusing plugins to change s9y variables in frontend_header is definitely the worse approach that should be corrected.
I can agree with that, but it only works in non-embedded mode. A plugin that outputs <head> information is useless in embedded mode, because the <head> information goes into the <body>. To avoid that behavior, we need to remove the frontend_header hook from the index.tpl for embedded mode; then we need some hook for restoring variables before the body output is finalized.
garvinhicking wrote:The other solution of embedding is to not unconditionally include the s9y index.php file, but rather follow the steps of index.php in your own embedding script. This way you can enforce calling frontend_header in the PHP workflow, unrelated to template processing, and capture it before any output takes place.
Yes, I thought of that... but the index.php is more than 600 lines long! Duplicating it would be a difficult task for anyone. I realize that we don't really recommend embedding, but that's pretty much an active barrier.
garvinhicking wrote:I don't see another solution right now. In fact, I do not recommend embedding like that any more, because it was in the pre-smarty era. Now I usually recommend to call your framework's PHP code through the s9y plugin API and/or through the {php} in smarty templates.
Of course. And translating an existing site to a s9y template is usually trivial; I've actually been wondering if we could automate it. Unfortunately, Don and I are working with legacy code. Most of the solutions we've discussed so far amount to a full port!

The one ray of hope I see is the possibility of an even hook between the <head> and the <body>. Then we could hook frontend_header from the embedding script for <head> output, and modify the plugin to restore its variables the proper way.

Posted: Tue Aug 26, 2008 6:22 pm
by Don Chambers
So here is an extreme simplification of most template's index.tpl file:

Code: Select all

{if $is_embedded != true}
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="{$lang}" lang="{$lang}">
        <head>
            {serendipity_hookPlugin hook="frontend_header"}
        </head>
        <body>
{else}
    {serendipity_hookPlugin hook="frontend_header"}
{/if}
{if $is_embedded != true}
        </body>
    </html>
{/if}
As Judebert stated, by the time the frontend_header hook is called for an embedded installation, <head> is already closed and <body> has already been opened.... no event plugin that emits code intended for <head> can possibly be output there via this index.tpl code. Plugins that come to mind, in addition the the HTML Meta plugin, include the head nugget and google analytics, easypodcasting & lightbox/thickbox. I'm sure there are many more.

I realize I am not saying anything in this post not already covered by Judebert, but I wanted to point out that the frontend_header hook in all templates, including the default and bp, simply does not work for embedded installations that need plugins to emit code in <head>, and for that very reason, I think it should be addressed.

For a total kludge, I can do this in an embedded blog.php file:

Code: Select all

<?php
     serendipity_plugin_api::hook_event('frontend_header', &$serendipity);
?>
And this in the index.tpl file:

Code: Select all

{else}
    {capture}{serendipity_hookPlugin hook="frontend_header"}{/capture}
{/if}
But that seems ridiculous and for all I know, creates further problems..... :wink:

Posted: Tue Aug 26, 2008 6:54 pm
by garvinhicking
Hi!

Hm, I really don't know a solution to this. Changing the way frontend_header uses echo right now is not really an option, it would seriously break many plugins to work properly with all s9y templates. Doing this just for the benefit of an embedded usage which is no longer really suggestable is not weighing so much.

What can be done maybe is add a new, second hook like "frontend_embed_header". All current plugins would simply listen on that new event hook, and do the same they would in their frontend_header hooks, but without emitting anything.

This would then not affect BC; and for embed installs you could use that header instead. But I'm not sure if that can be done, I have not investigated the whole problem yet, but maybe it's a clue to judebert?

Regards,
Garvin

Posted: Tue Aug 26, 2008 7:03 pm
by garvinhicking
Hi!

Another idea: Let the plugins you need to work with check a variable you set in your embed script before, like $serendipity['do_not_use_echo_in_plugins_please_it_hurts_my_heart_really_I_mean_it'] = true.

Check that variable inthe plugins to behave well with your embed script.I would not mind adding such a check to the currently misbehaving plugins, the performance impact is neglectible, and you would still be able to use the official plugins, as long as your embed site sets this variable.

Regards,
Garvin

Posted: Thu Aug 28, 2008 7:31 pm
by judebert
Okay, this is easily feasible.

For optimum efficiency and flexibility, I propose modifying the plugins to add their head output to a local variable (say, $output); then, when they're ready to output, check a variable and either echo/print $output or append it to a global $serendipity variable.

Of course, we already have a variable telling us whether an installation is embedded or not: $serendipity['embed'] (which is available through $serendipity['smarty']->get_template_vars('is_embedded'), too). I propose $serendipity['embedded_head'] for the output variable.

With this change, the embedding script would call require index.php as usual. When the templates hooked frontend_header, the plugins would append their output to $serendipity['embedded_head'] instead of echoing it. When s9y finishes execution, the embedding script can either continue obliviously or output $serendipity['embedded_head'] if it feels like actually using the plugin output.

I did a quick search through the templates and code. Only templates are calling the frontend_header hook, and they never pass the additional data (which would be done by setting the 'addData' attribute). We could utilize this to pass an output variable to the plugins, but this becomes much more cumbersome, requiring changes in the templates and/or the core code as well as the plugins.

If there are no objections to the use of $serendipity['embed'] as the flag variable and $serendipity['embedded_head'] as the output capture variable, I'll get started making changes to the plugins, starting with the ones Don needs soonest.

Posted: Fri Aug 29, 2008 9:36 am
by garvinhicking
Hi!

No objections, good idea of using the existing variable. Even though I liked my proposed name so much ;)

Regards,
garvin

Posted: Fri Aug 29, 2008 3:28 pm
by Don Chambers
$serendipity['do_not_use_echo_in_plugins_please_it_hurts_my_heart_really_I_mean_it'] has to be a new record for the length of a variable name!!! :lol: :lol:

Is this all really worth it? If Judebert proceeds, there could be a lot of plugins that will need to be modified. My "kludge" works for the one embedded installation I was working on, and I do not want to create unnecessary work... however, if the long term benefits exceed the current effort, just let me know how I can help test/implement the change.