5 users online. Create an account or sign in to join them.Users

Search

This is an advanced extension question (PHP):

  • I’ve got the XML of a data source as a string.
  • I’ve got all page parameters in an array.
  • I’ve got the page template as file.

How do I generate the HTML resulting from these three inputs using core functionality?

Try this:

require_once(TOOLKIT . '/class.xsltpage.php');
$proc = new XSLTPage;
$proc->setXML($xml);
$proc->setXSL($xsl_path);
$proc->setRuntimeParam($param);
$result = $proc->generate();

It assumes you already have $xml & $xsl_path defined and that $param is an array of key/value pairs. It also assumes you have the core defines. Specifically TOOLKIT.

Thanks, Alistair! I’ll try that code.

Scratch the above. It seems to be a problem in the XSL itself.

This is strange though. I now get the following error

Array
(
    [1] => Array
        (
            [number] => 2
            [message] => loadXML(): Start tag expected, '<' not found in Entity, line: 1
            [type] => xsl
            [line] => 72
            [context] => /www/htdocs/stilbluete/workspace/pages/verschicken_nachricht.xsl
        )

    [value] => Array
        (
            [number] => 2
            [message] => loadXML(): Start tag expected, '<' not found in Entity, line: 1
            [type] => xsl
            [line] => 72
            [context] => /www/htdocs/stilbluete/workspace/pages/verschicken_nachricht.xsl
        )

    [0] => 0
    [key] => 0
)

But there is no line 72 in my XSL – the file only contains 56 lines and it is working if I call it via the front-end. (I’m trying to solve this problem, so using the front-end is not a option in this case.)

I found the problem:

  • First of all $proc->setXSL($xsl_path); should become $proc->setXSL($xsl_path, true); if you are trying to pass a file name like I did.
  • Furthermore there seems to be a problem with relative paths in <xsl:import />. Something like <xsl:import href="../utilities/dateandtime.xsl" /> will be misinterpreted as /path/to/your/site/utilities/dateandtime.xsl.

Alistair, is there a way to set the xsl file context to /workspace/pages/ to keep things working?

You might as well use Symphony’s Gateway class to load a frontend page. Or PHP’s cURL function:

public function loadPage($url) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
    curl_setopt($ch, CURLOPT_MAXREDIRS, 10);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    curl_setopt($ch, CURLOPT_HEADER, FALSE);
    $page = curl_exec($ch);
    curl_close($ch);
    return $page;
}

Well, thing is that the page should not be accessible via the front-end (which it would be if I understand your code correctly).

Yes and no. You could for example build a simple XSL logic to prevent access to the page, using the Server Headers extension:

<xsl:choose>
    <xsl:when test="//server-headers/remote-addr = //server-headers/server-addr">
        your page here...
    </xsl:when>
    <xsl:otherwise>
        <h1>Access forbidden</h1>
    </xsl:otherwise>
</xsl:choose>

@Nils:

I’ve got the XML of a data source as a string.

This is something I need to achieve as well in an extension I am working on. Can you tell me in which context you did this (e.g. an extension field), and maybe post some code? This would help me a lot.

@Michael: I was trying to solve some issues I had with Rowan’s Email Filter Template. Have a look at the code of the extension:

    protected function getData($template, $entry_id) {
        $data = new XMLElement('data');

        if (!empty(self::$params)) {
            $params = new XMLElement('param');

            foreach (self::$params as $key => $value) {
                if (is_integer($key)) $key = 'item';

                $key = General::sanitize($key);

                if (is_array($value)) {
                    $child = new XMLElement($key);
                    $this->getDataParam($value, $child);

                } else {
                    if (is_bool($value)) {
                        $value = ($value ? 'yes' : 'no');
                    }

                    $child = new XMLElement($key, General::sanitize((string)$value));
                }

                $params->appendChild($child);
            }

            $data->appendChild($params);
        }

        self::$page->__processDatasources($template['datasources'], $data, array(
            'etf-entry-id'  => $entry_id
        ));

        $dom = new DOMDocument();
        $dom->loadXML($data->generate(true));

        return $dom;
    }

The last six lines are what you are looking for.

Take a look in class.frontendpage.php and you’ll see that if you subscribe to the FrontendOutputPreGenerate delegate you gain access to the page object, and the generated XML and XSLT. With the XML you can run an XPath query against it to grab the XML fragment just for that Data Source. For a code reference see my Firebug Profiler extension which uses a similar idea.

Thank you, Nils and Nick!

Hmmm, seems that I am trying something rather different. I don’t have any page object in my context.

Does anybody know how to simply “read”(i.e. build) the output of a frontend page from within a php script (might be a Symphony field, for example)? Sure, I would need the core defines (which I already have). But apart from that? I am dreaming of some classes to include and a few lines of code…

I used cURL to get the page, but this is not really elegant. Maybe there is a simple solution to generate the page, passing the page’s URL or ID?

Maybe there is a simple solution to generate the page, passing the page’s URL or ID?

file_get_contents() ;-)

The logic for resolving a page (including the param pool etc) is all in class.frontendpage.php and not really exposed via an API. cURL really is the most elegant solution in my opinion. Rowan does this in his Email Templates Filter here.

This is the most elegant solution because it adds no dependencies to the way Symphony implements its pages.

If you want to do this outside of the scope of Symphony itself, look no further than the index.php where you can include symphony/lib/boot/bundle.php and get page output like this:

$output = renderer($renderer)->display(getCurrentPage());

However there is a Symphony way already if you want to grab the output of the current page: the FrontendOutputPostGenerate delegate. From within a field you can instantiate the extension (driver) and call the callback function that is subscribing to this delegate.

If neither of those satisfy, can you be more specific about what you’re trying to do?

Nick, thank you so much for this information. As far as I understand the whole problem, my fist solution (cURL) seems to be the best.

I am working on the Email Newsletters extension (which isn’t vaporware!). It will provide possibilities to include dynamic HTML and pure text pages (which both will be standard Symphony pages and will be loaded with cURL anyway – this part of the extension already works great, and the biest is sending fantastic emails!). But I also want a flexible solution to define and include recipients. (The extension will probably be used in conjunction with the Members extension, but this shouldn’t be a necessity.) To define recipients, I thought of datasources vs pages, and my conclusion was to use XML pages which are built in Symphony. This brings the highest flexibilty (and much less code in the extension). You can even combine datasources or use static XML files to define your recipients.

Of course recipient XML pages should never be accessible from the web. Now, if I want to load these pages via cURL, the only way I see to restrict page access is the method I described above. But to be honest, I am not really sure how safe this is. That’s why I thought about “internal loading”. But it feel that this would only complicate things.

I appreciate any thoughts on this.

I don’t have any page object in my context.

Maybe you should have a look at the FrontendPage class in /symphony/lib/toolkit/class.frontendpage.php.

Nils, the extension is supposed to work in the Symphony backend. If am not totally wrong – and Nick’s thoughts confirmed this – it would be rather complicated to generate frontend pages from the backend side.

P.S.: I meant “no frontend page object”. Well, regarding the background mailer class I indeed haven’t any “page”. This script is started as a PHP background process (using the PHP CLI SAPI).

I don’t say it’s impossible. But I don’t want to have too many dependencies in the extension.

Maybe I don’t fully understand what you are after but it is possible to generate frontend pages in the backend quite easily: http://symphony-cms.com/community/discussions/477/4/#position-69

Create an account or sign in to comment.

Symphony • Open Source XSLT CMS

Server Requirements

  • PHP 5.2 or above
  • PHP's LibXML module, with the XSLT extension enabled (--with-xsl)
  • MySQL 5.0 or above
  • An Apache or Litespeed webserver
  • Apache's mod_rewrite module or equivalent

Compatible Hosts

Sign in

Login details