PDF Custom detail template fails after update to v3.9

@troester

Any ideas? Actually what IP I need to enter in the Local IP setting of the Allow PDF Localhost thing? And any ideas where this is happening in the code to go and investigate it?

If you've already added the local IP, I'm not sure what to suggest. Usually (probably 90% of the time), CURL calls back to the server itself will show up as being from 127.0.0.1, which is what I'm using by default to detect if it's a local call. That "local IP" option is there for servers are set up differently, that will show the local NAT'ed address (like 192.168.x.x or 10.x.x.x), or in some cases, the external IP. No way for me to know. You'd have to put a debug call in there, and do something like ...

var_dump($_SERVER);exit;

... to see what IP it's using.

-- hugh
 
Hi Hugh @cheesegrits : Since my last post, I went that far and have done what you suggest.
The $_SERVER['REMOTE_ADDR'] shows my own IP.

To clarify - in case you have missed it... The problem I am facing is with Mail PDF Attachment of Form's data after submission.
The Details view has no public/guest viewing access. So upon form submission the view can not be generated because of ACL and returns nothing to the PDF output of the attachment.

Does the mail PDF Attachment get generated after a CURL call?
So far I haven't see this in the code but I don't know if there is something deeper... I have seen that you have already whitelisted the 127.0.0.1
But what I see is that the Form Email plugin runs on onAfterProcess, it calls the pdfAttachment method which then asks the view from the controller etc.
Things seem to happen without a CURL call... so the $_SERVER['REMOTE_ADDR'] shows my current IP.
Maybe things work with CURL for the List PDF plugin and the $_SERVER['REMOTE_ADDR'] / Local IP will be different there.

In order to make things work for the email plugin I have to somehow force the $allowPDF to be true in the FabrikFEModelList::canViewDetails();
 
Does the mail PDF Attachment get generated after a CURL call?

I think I answered that one here:

https://fabrikar.com/forums/index.p...back-to-the-details-layout.49381/#post-258264

In the email plugin, it's not doing a CURL call, it's running the details view direct.

And if you have "Allow PDF Localhost" (allow_pdf_localhost_view) enabled, the permissions should already work. The email plugin PDF code calls the listmodel setLocalPDF(), which sets $this->localPdf in the list model, which canViewDetails() checks

https://github.com/Fabrik/fabrik/blob/master/components/com_fabrik/models/list.php#L4180

So with $this->localPdf set true, the canViewDetails() code ignores the IP, and just allows it.

--hugh
 
Email plugin is setting $this->localPdf, but it's reset somewhere.
At list.php#L4180 $this->localPdf it's always false, so always running the "whitelist" part.
 
@cheesegrits
Well, I think I have tracked the issue here...

As @troester mention $this->localPdf is always false;

This is because, although you run $model->getListModel()->setLocalPdf(); from inside the email plugin, this property is being set to a different list object than the one that we finally use to output the content.

This is because later you call $controller->display(), which calls the details controller display ... and at the end you end up with a new object there, with the $localPdf property set to its default false value.

....

I have to go somewhere at the moment, but be back soone ... and I will post here more info on the issue and a possible fix - as I have spent much time to track this down - but I don't have much time right now to write it with more details.
 
Well... debugging this one was a tough story...

The problem comes from this here: FabrikFEModelForm::render() method - (components/com_fabrik/models/form.php line ~ 3015)

###update:
forgot to mention briefly the execution flow as a continuation of my previous post above:

When the email plugin call the $controller->display() (this is the details controller),
then the details controller display ends up to the forms view.base display - which calls the forms controller render() method.
###


I am pasting partially the method here:

Code:
public function render()
{
    $profiler = JProfiler::getInstance('Application');
    JDEBUG ? $profiler->mark('formmodel render: start') : null;

    // $$$rob required in paolo's site when rendering modules with ajax option turned on
    $this->listModel = null;
    $this->setRowId($this->getRowId());

    /*
    * $$$ hugh - need to call this here as we set $this->editable here, which is needed by some plugins
    * hmmmm, this means that getData() is being called from checkAccessFromListSettings(),
    * so plugins running onBeforeLoad will have to unset($formModel->_data) if they want to
    * do something funky like change the rowid being loaded.  Not a huge problem, but caught me out
    * when a custom PHP onBeforeLoad plugin I'd written for a client suddenly broke.
    */
    $this->checkAccessFromListSettings();

At that point the listModel is set to NULL.

Then checkAccessFromListSettings() is called, which does this: $listModel = $this->getListModel();

So there you have: getListModel()
Code:
public function getListModel()
{
    if (!isset($this->listModel))
    {
        $this->listModel = JModelLegacy::getInstance('List', 'FabrikFEModel');
        $item = $this->getForm();
        $this->listModel->loadFromFormId($item->id);
        $this->listModel->setFormModel($this);
    }

    return $this->listModel;
}

which checks if $this->listModel isset. Since you have set the $this->listModel in the render() method 2 steps before, it creates a new instance.
------

So I don't know why the $listModel needs to be set to NULL in the render() function. The comment mention that this is needed for paolo's site.

It seems like if we remove that line, the localPdf thing works (however this Paolo's site case is very new discovery on my research) and the tests I have done on it are limited - it's possible that this work because of other modifications I have already done in the code during my debugging.

Although the ideal would be to be able to set the $localPdf from the plugin that needs this, there are some other quick ways to handle this by keeping this paolo's site thing in the code. *If those aren't sufficient or not guarantee security then to keep paolos's site handling, some functions would need additional checks and some parameters, so they would behave differently depending on the use-case/call each time.

So,
One possible way is to add this check in the FabrikFEModelForm::checkAccessFromListSettings()

Code:
$listModel = $this->getListModel();

// Bypass details view ACL for Local PDF
if ($this->app->input->get('format', 'html') === 'pdf')
{
    $config = JComponentHelper::getParams('com_fabrik');

    if ($config->get('allow_pdf_localhost_view', '0') === '1')
    $listModel->setLocalPdf();
}


or things could possibly be handled in the list Model canViewDetails(): Maybe with an extra field in the fabrik config for the $_SERVER['SERVER_ADDR'] so it also gets whitelisted and then check something like:
Code:
if (in_array($_SERVER['REMOTE_ADDR'], $whitelist ) || in_array($_SERVER['SERVER_ADDR'], $whitelist) ) {
    $allowPDF = true;
}


That's all from me so far... let me know what you think @troester, @cheesegrits
 
Last edited:
Another idea could be only run the paolo's site $listModel = null thing, only under certain special conditions... not sure what those conditions should be, or... possibly check the condition if we are in pdf format through the J input... and don't unset the listModel property there...
 
in_array($_SERVER['SERVER_ADDR'], $whitelist
Hmm, no, that's what I tried. But then any call via URL with format=pdf&rowid=X would create a PDF (so nearly the same as setting list view record access to public).
 
@troester it looks like for being able to generate a PDF when posting from public access, the ACL must be bypassed anyway - Even if that follows a setting in the config, when PDF generation happens the ACL is opened at this point.

Well not exactly - I think if the localPDF is set from the plugin call then it should be okay.
But that's where the paolo's site issue break things.
 
Last edited:
Maybe the easy and quick fix is to change the FabrikFEModelForm::render() to something like:

The comment there says it's needed when rendering modules with ajax - I don't know what is the exact use case for this... but at least for generating and attaching the PDF on form submission, this will avoid the generation of a new listModel instance and will use the one created earlier which has the $localPdf set to true.

Code:
public function render()
{
    $profiler = JProfiler::getInstance('Application');
    JDEBUG ? $profiler->mark('formmodel render: start') : null;

    // $$$rob required in paolo's site when rendering modules with ajax option turned on
    if ($this->app->input->get('format', 'html') !== 'pdf') {
        $this->listModel = null;
    }
    // .....

I am going to apply this one on my project for now and will wait for your feedback and final solution.
 
I did some extra history research - unsetting the formModel's listModel inside the render() function exists before Fabrik's arrival to github...
 
We are in need of some funding.
More details.

Thank you.

Members online

Back
Top