Vanilla 1 is no longer supported or maintained. If you need a copy, you can get it here.
HackerOne users: Testing against this community violates our program's Terms of Service and will result in your bounty being denied.

variable scope, $Head->AddString, argh

ithcyithcy New
edited April 2006 in Vanilla 1.0 Help
so i'm writing a new extension which extends the HtmlFormatter extension. i need to add strings (some css generated on the fly) to the document head and i'm trying to do that with the Head->AddString method, but php's variable scoping (or $Head->AddString) is giving me headaches.

<?php /* Extension Name: MyFormatter Extension Url: Description: skeleton of an html formatter extension. Version: 0.1 Author: ithcy Author Url: */ class MyFormatter extends HtmlFormatter { function Parse($String, $Object, $FormatPurpose) { if($FormatPurpose == FORMAT_STRING_FOR_DISPLAY) return $this->MyFormatter($String); else return $String; } public function MyFormatter($txt) { global $Head; $Head->AddString("\n<!-- test -->\n"); return $txt . "<br>this text touched by MyFormatter."; } } $MyFormatter = $Context->ObjectFactory->NewObject($Context, "MyFormatter"); $Context->StringManipulator->AddManipulator("Html", $MyFormatter); ?>

this code almost works -- it appends "touched by MyFormatter" to every Html-formatted comment, but it only inserts "<!-- test -->" into the head once.
shouldn't there be as many "<!-- test -->" added to the head as there are Html-formatted comments on the page?

what am i missing here?


  • MarkMark Vanilla Staff
    I'd guess that the formatter is being called once before the head is rendered, and then the rest of them are being called *after* the head has been rendered.
  • hmm. how can i fix that?
  • You can't; the head is apparently done rendering before the first comment gets filtered.
  • MarkMark Vanilla Staff
    It would require a hefty reworking of the commentgrid control so that all comments are built in the constructor and then just echo'd in the render event. Not really something I'm interested in doing for a variety of reasons.

    What are you trying to accomplish? Maybe there's another way...
  • long story: i'm integrating geshi syntax highlighting. i am doing this by searching for a custom attribute called "lang" within <code> tags. for example:
    <code lang="perl">print "x";</code> would invoke geshi's perl highlighter on the contents of that code element. (geshi actually turns the whole code block into a styled pre or div.)

    geshi can use css to highlight code. it generates the css on the fly, one stylesheet per language. if there are multiple code blocks in different languages on the page, multiple stylesheets are needed.

    as i've written it now, to save page weight, i don't render all the stylesheets that geshi can produce at once - i only call up the ones for the languages that are used on the page.

    i've actually made it work perfectly by rendering the stylesheets within the comments div, but it's against standards to have inline stylesheets in the body, isn't it?
  • MarkMark Vanilla Staff
    edited April 2006
    Hmm. Well, another option would be to attach to the Constructor delegate of the CommentGrid and access the dataset directly. Just be sure to rewind the dataset when you're finished looping through it (so it can be properly looped when it gets to the render method).

    something like ...

    function CommentGrid_IncludeGeshiScripts(&$CommentGrid) { global $Head; while ($Row = $CommentGrid->Context->Database->GetRow($CommentGrid->CommentData)) { // Perform any actions you need to on the head control based on the $Row data } $CommentGrid->Context->Database->RewindDataSet($CommentGrid->CommentData); } $Context->AddToDelegate('CommentGrid', 'Constructor', 'CommentGrid_IncludeGeshiScripts');
  • MarkMark Vanilla Staff
    Did that do it for you, ithcy? I think that's the fix you need...
  • ithcyithcy New
    edited April 2006
    i'm about to try it now. thanks mark!

    //so can i get rid of the Parse function altogether, or how do i work that?

    (note: i have been reading the docs. i'm just feeling slow this week.)
  • ithcyithcy New
    edited April 2006
    oh. i see. hmm. i'm figuring it out.

    //is there a way to update the comment bodies in that function before they get passed to Parse?
  • MarkMark Vanilla Staff
    edited April 2006
    Hmmm. That's tough. I'd say no to editing comment bodies in that function because you'd have to alter a dataset directly. Let me think about it. I bet there's a way to attach to a delegate in the comment object and allow you to make the changes you want...
  • ithcyithcy New
    edited April 2006
    it's okay. your fix seems to have done it.

    of course there is just one more thing. now that this formatter is working, it seems to be preventing both Html Formatter, and another extension which also extends Html Formatter, from executing.

    do i need to do anything special to make sure all formatters execute? i thought AddManipulator with a NewObject with a unique name would be enough.
  • MarkMark Vanilla Staff
    Hmmm. What did you model it after? The ExtendedTextFormatter should be a decent guide on how to build on top of existing formatters...
  • ithcyithcy New
    edited April 2006
    this is the ExtendedTextFormatter way:
    $GeSHiFormatter = $Context->ObjectFactory->NewObject($Context, "GeSHiFormatter"); $Context->StringManipulator->Formatters["Html"]->AddChildFormatter($GeSHiFormatter);

    this is my way (based on HtmlPlusPlus extension):
    $GeSHiFormatter = $Context->ObjectFactory->NewObject($Context, "GeSHiFormatter"); $Context->StringManipulator->AddManipulator("Html", $GeSHiFormatter);

    when i do it my way, GeshiColor works but HtmlPlusPlus and HtmlFormatter don't work.
    when i do it the ExtendedTextFormatter way, GeshiColor doesn't work.
  • do you want to see the whole of the code?
  • MarkMark Vanilla Staff
    Sure. Zip up everything in your extension's folder and email it over. I'll install it locally and see if I can get it to go...

    If you can provide a sample of the type of comments ppl will enter as well, that would be spanky.
  • sent :)

    thanks again.
  • MarkMark Vanilla Staff
    edited April 2006
    Okay, so the problem was actually with the HtmlFormatter. SirNot had been mastering the art of preventing xss attacks, and in doing so I guess he accidentally removed the ParseChildren line of the Parse method. Either that or I'm a total moron and I forgot to put it in there in the first place.

    Regardless, I added it in and it is available in svn. So, if you use this method...

    $GeSHiFormatter = $Context->ObjectFactory->NewObject($Context, "GeSHiFormatter"); $Context->StringManipulator->Formatters["Html"]->AddChildFormatter($GeSHiFormatter);

    ... it will work. The only problem is the language="javascript" because the htmlformatter doesn't even allow the word javascript to be written - it replaces some characters in the word with their html charcode equivalent. So <code language="javascript> will not work. You may have to do some more tinkering in your extension for that...
  • grr. still not working, unless i disable HtmlPlusPlus.

    AddChildFormatter works now, though.

    i am starting to think that the problem is in HtmlPlusPlus. it's an old extension and some of its code probably needs to be updated to match 1.0.
  • MarkMark Vanilla Staff
    Well, I didn't play with the plusplus one, but I got geshi working...
  • did it work when you enabled plusplus? i hope not because i'm about to engage in some defenestration and that might put me over the edge.
This discussion has been closed.