how do I set up a contextSwitch to generate a csv file - zend-framework

In my controller init method I have this
public function init()
{
//initialise the context switch action helper
$this->_helper->getHelper('contextSwitch')
->addContext('csv', array('suffix' => 'csv',
'headers' => array(
'Context-Type' => 'application/csv',
'Context-Disposition' => 'inline; filename="fooo.csv"',
'Pragma' => 'no-cache',
'Expires' => '0',
)))
->addActionContext('stockreport', 'csv')
->initContext();
}
In my stockreportAction I have disabled the layout and view render as follows.
public function stockreportAction()
{
$this->_helper->layout->disableLayout();
$this->_helper->viewRenderer->setNoRender();
echo 'foobar';
}
I get a blank page when I call this file, I would like to generate this so in the example 'foobar' appears as a downloadable csv file. Could anyone advise

Zend_Magic is not implemented yet.
It is your responsibility to generate csv or any other output format.
in short: context switcher modify view script suffix, so view script for your context is stockreport.csv.phtml (if csv context is active, stockreport.phtml otherwise).
But in your example context switcher used in a wrong way. You don't switch context here.
You don't use viewscripts, you don't check if context active in your action.
Why your output is empty is a different question - probably at some point you clear content in Response object or there somewhere is a fatal error & error output disabled.
Update:
Also headers are Content-Type and Content-Disposition

Related

CSRF field is missing when I embed my form with a requestAction in CakePHP 3

I want to embed a contact form in multiple places on my website.
I developed a contact form in a contact() function within my MessagesController.php:
// MessagesController.php
public function contact()
{
$this->set('title', 'Contact');
$message = $this->Messages->newEntity();
... // shortened for brevity
$this->set(compact('message'));
$this->set('_serialize', ['message']);
}
I loaded the CSRF component in the initialize() function of the AppController.php:
// AppController.php
public function initialize()
{
parent::initialize();
$this->loadComponent('Csrf');
... // shortened for brevity
}
The form is rendered with a contact.ctp and it works fine.
I followed CakePHP's cookbook which suggests using requestAction() within an element, then echoing the element where I want it:
// contact_form.ctp
<?php
echo $this->requestAction(
['controller' => 'Messages', 'action' => 'contact']
);
?>
And:
// home.ctp
<?= $this->element('contact_form'); ?>
The problem is that the form is rendered fine, but the CSRF hidden field is missing. It should be automatically added to the form since the CSRF component is called in the AppController.php.
I guess either using an element with a requestAction() isn't the solution for this particular case, or I am doing something wrong.
Any ideas? Thanks in advance for the input!
Request parameters need to be passed manually
requestAction() uses a new \Cake\Network\Request instance, and it doesn't pass the _Token and _csrf parameters to it, so that's why things break.
While you could pass them yourself via the $extra argument, like
$this->requestAction(
['controller' => 'Messages', 'action' => 'contact'],
[
'_Token' => $this->request->param('_Token'),
'_csrf' => $this->request->param('_csrf')
]
);
Use a cell instead
I would suggest using a cell instead, which is way more lightweight than requesting an action, also it operates in the current request and thus will work with the CSRF component out of the box.
You'd pretty much just need to copy your controller action code (as far as the code is concerned that you are showing), and add a loadModel() call to load the Messages table, something like
src/View/Cell/ContactFormCell.php
namespace App\View\Cell;
use Cake\View\Cell;
class ContactFormCell extends Cell
{
public function display()
{
$this->loadModel('Messages');
$this->set('title', 'Contact');
$message = $this->Messages->newEntity();
// ... shortened for brevity
$this->set(compact('message'));
$this->set('_serialize', ['message']);
}
}
Create the form in the corresponding cell template
src/Template/Cell/ContactForm/display.ctp
<?php
echo $this->Form->create(
/* ... */,
// The URL needs to be set explicitly, as the form is being
// created in the context of the current request
['url' => ['controller' => 'Messages', 'action' => 'contact']]
);
// ...
And then wherever you want to place the form, just use <?= $this->cell('ContactForm') ?>.
See also
API > \Cake\Routing\RequestActionTrait::requestAction()
Cookbook > Views > Cells

Call Config::Load doesn't work in Fuelphp

I have a class in the path 'app/classes' called 'Helper.php'. And also I have a config file 'custom.php' in 'app/config'.
The problem is, when I call the config file, this return FALSE.
class Helper {
public static function actions_header ($ractive) {
return Config::load('custom');
}
}
The custom config file
return array(
'sidebar_entities' => array (
array(
'name' => 'Dashboard',
'icon' => 'icon-dashboard',
'url' => 'dashboard'
),
array(
'name' => 'Álbumes',
'icon' => 'icon-music',
'url' => 'albums'
)
)
);
You probably need something like this:
// load the config
Config::load('custom', true); // true - so you load the config to group 'custom'
// return array of items
return Config::get('custom');
I have not tested this, but something like this should work.
Tried to repeat the same code and everything works fine for me. FuelPHP 1.7.
Up until 2012-08-28, load() only returned the loaded data on the initial call. If you called load() and the file is already loaded, it returned false. Since then, it will not load the file again, but return what is already loaded.
So the question is: how old is the version of Fuel you are using? Given the date of the change, that would be < 1.3, which is very old...
I have run into this issue as well. I run the following code. (I am running Fuel 1.6)
Config::load('config_file_name') //returns config array
Config::load('config_file_name') //returns false
Then I run the following to load a sub array of the config file.
Config::get('config_file_name.attr') //returns nothing
Turns out I just didn't understand the fuel documentation. Thanks #huglester, your answer made it all make sense for some reason.
The documentation says:
// This merges the "custom" config file in with the root config.
Config::load('custom');
// This loads the "custom" config file in a group named "custom".
Config::load('custom', true);
So, when you run Config::load('config_file_name'), you can access the config sub arrays by using Config::get('attr'). This is because the 'config_file_name' is merged with the root config.
If you want to use Config::get('config_file_name.attr'), then you need to load the config file using Config::load('config_file_name', true)

Using the new 'data' option for CakeEmail attachments with CakePHP 2.4.x

The CakeEmail help page states that the data option has been added as of 2.4, so you no long have to have a physical file to add an attachment to an email.
I've got the following code:
$Email->from(array($this->Session->read('Auth.User.email') => $this->Session->read('Auth.User.name')))
->to($this->request->data['email-to'])
->subject($this->request->data['email-subject'])
->attachments(array('attachement1.pdf', array('data' => $pdf)))
->send($this->request->data['email-message']);
But whenever I run that I get an Internal Error saying File Not Found: "".
I had a look at the source code (which I'm beginning to learn is often more useful than reading the documentation!): https://github.com/cakephp/cakephp/blob/master/lib/Cake/Network/Email/CakeEmail.php
Changing my code to:
$Email = new CakeEmail('default');
$Email->from(array($this->Session->read('Auth.User.email') => $this->Session->read('Auth.User.name')))
->to($this->request->data['email-to'])
->subject($this->request->data['email-subject'])
->attachments(array('attachement1.pdf' => array('data' => $pdf, 'mimetype' => 'application/pdf')))
->send($this->request->data['email-message']);
Notice on the attachments line, the array is assigned to the filename variable, rather than passed in as a parameter!
For completeness, if anyone else is reading this and is wondering how I am generating my PDF with CakePDF:
// Create PDF for attachment
$CakePdf = new CakePdf();
$CakePdf->template('claim', 'default');
//get the pdf string returned
$pdf = $CakePdf->output();

Zend adding parameter to URL before generating view

The title might be misleading but I'm trying to do something very simple but cant figure it out.
Lets say I have a Question controller and show action and question id is the primary key with which I look up question details - so the URL looks like this
http://www.example.com/question/show/question_id/101
This works fine - So when the view is generated - the URL appears as shown above.
Now in the show action, what I want to do is, append the question title (which i get from database) to the URL - so when the view is generated - the URL shows up as
http://www.example.com/question/show/question_id/101/how-to-make-muffins
Its like on Stack overflow - if you take any question page - say
http://stackoverflow.com/questions/5451200/
and hit enter
The question title gets appended to the url as
http://stackoverflow.com/questions/5451200/make-seo-sensitive-url-avoid-id-zend-framework
Thanks a lot
You will have to add a custom route to your router, unless you can live with an url like:
www.example.com/question/show/question_id/101/{paramName}/how-to-make-muffins
You also, if you want to ensure that this parameter is always showing up, need to check if the parameter is set in the controller and issue a redirect if it is missing.
So, in your bootstrap file:
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
public function _initRoutes ()
{
// Ensure that the FrontController has been bootstrapped:
$this->bootstrap('FrontController');
$fc = $this->getResource('FrontController');
/* #var $router Zend_Controller_Router_Rewrite */
$router = $fc->getRouter();
$router->addRoutes( array (
'question' => new Zend_Controller_Router_Route (
/* :controller and :action are special parameters, and corresponds to
* the controller and action that will be executed.
* We also say that we should have two additional parameters:
* :question_id and :title. Finally, we say that anything else in
* the url should be mapped by the standard {name}/{value}
*/
':controller/:action/:question_id/:title/*',
// This argument provides the default values for the route. We want
// to allow empty titles, so we set the default value to an empty
// string
array (
'controller' => 'question',
'action' => 'show',
'title' => ''
),
// This arguments contains the contraints for the route parameters.
// In this case, we say that question_id must consist of 1 or more
// digits and nothing else.
array (
'question_id' => '\d+'
)
)
));
}
}
Now that you have this route, you can use it in your views like so:
<?php echo $this->url(
array(
'question_id' => $this->question['id'],
'title' => $this->question['title']
),
'question'
);
// Will output something like: /question/show/123/my-question-title
?>
In your controller, you need to ensure that the title-parameter is set, or redirect to itself with the title set if not:
public function showAction ()
{
$question = $this->getQuestion($this->_getParam('question_id'));
if(!$this->_getParam('title', false)) {
$this->_helper->Redirector
->setCode(301) // Tell the client that this resource is permanently
// residing under the full URL
->gotoRouteAndExit(
array(
'question_id' => $question['id'],
'title' => $question['title']
)
);
}
[... Rest of your code ...]
}
This is done using a 301 redirect.
Fetch the question, filter out and/or replace URL-illegal characters, then construct the new URL. Pass it to the Redirector helper (in your controller: $this->_redirect($newURL);)

Arbitrary Form Processing with Drupal

I am writing a module for my organization to cache XML feeds to static files to an arbitrary place on our webserver. I am new at Drupal development, and would like to know if I am approaching this the right way.
Basically I:
Expose a url via the menu hook, where a user can enter in a an output directory on the webserver and press the "dump" button and then have PHP go to drupal and get the feed xml. I don't need help with that functionality, because I actually have a prototype working in Python (outside of Drupal)..
Provide a callback for the form where I can do my logic, using the form parameters.
Here's the menu hook:
function ncbi_cache_files_menu() {
$items = array();
$items['admin/content/ncbi_cache_files'] = array(
'title' => 'NCBI Cache File Module',
'description' => 'Cache Guide static content to files',
'page callback' => 'drupal_get_form',
'page arguments' => array( 'ncbi_cache_files_show_submit'),
'access arguments' => array( 'administer site configuration' ),
'type' => MENU_NORMAL_ITEM,
);
return $items;
}
I generate the form in:
function ncbi_cache_files_show_submit() {
$DEFAULT_OUT = 'http://myorg/foo';
$form[ 'ncbi_cache_files' ] = array(
'#type' => 'textfield',
'#title' => t('Output Directory'),
'#description' => t('Where you want the static files to be dumped.
This should be a directory that www has write access to, and
should be accessible from the foo server'),
'#default_value' => t( $DEFAULT_OUT ),
'#size' => strlen( $DEFAULT_OUT ) + 5,
);
$form['dump'] = array(
'#type' => 'submit',
'#value' => 'Dump',
'#submit' => array( 'ncbi_cache_files_dump'),
);
return system_settings_form( $form );
}
Then the functionality is in the callback:
function ncbi_cache_files_dump( $p, $q) {
//dpm( get_defined_vars() );
$outdir = $p['ncbi_cache_files']['#post']['ncbi_cache_files'];
drupal_set_message('outdir: ' . $outdir );
}
The question: Is this a decent way of processing an arbitrary form in Drupal? I not really need to listen for any drupal hooks, because I am basically just doing some URL and file processing.
What are those arguments that I'm getting in the callback ($q)? That's the form array I guess, with the post values? Is this the best way to get the form parameters to work on?
Thanks for any advice.
You can process forms in two stages, validate and submit.
Validate is for when you want to validate some user provided and raise form errors if some user input was invalid (like an invalid url or email address)
Submit which is the one you use is called if a form passes all of its validations, so at that point if you made a proper validation you will know that the data supplied by the user is okay.
Your submit function should look like this:
function ncbi_cache_files_dump(&$form, &$form_state) {
// $form: an array containing the form data
// $form_state: data about the form, like the data inputted in the form etc.
// code...
}
I think you need 2 separate forms here:
for setting the directory (the one you have now);
for making a dump (another form that would use the configured path).
Also it seems logical to publish the previously saved path as the default value in the settings form (instead of a hard-coded path).
And in general you should check the form input data from the second parameter of the submit callback:
function ncbi_cache_files_dump(&$form, &$form_state) {
$outdir = $form_state['values']['ncbi_cache_files'];
// ...
}