I have 4 different types of file to download image/doc/pdf/xls. I want to download the file once I clicked on file link.
//controller
public function sendFile($id) {
$file = $this->Attachment->getFile($id);
$this->response->file($file['path']);
// Return response object to prevent controller from trying to render
// a view
return $this->response;
}
//view
<?php echo $file->name; ?>
No need to return the response - see this.
Simply use
public function send_file($id = null) {
...
$this->autoRender = false;
$this->response->file($file['path']);
}
In your view you need to link to this controller action, though (if you think about it):
$this->Html->link('Download', array(
'controller' => 'controller_name', 'action' => 'send_file', $id
));
Also note the conventions I corrected for you - the docs tell you that as well.
just to complete and add some notes on Mark's answer
Simply use
public function send_file($file_name = null) {
//local machine to the file
//e.g. c:\wamp\www\project\...
$path_local = 'path to the file'.$file_name;
//live path to the file
//e.g. \var\...
$path_live = 'path to the file'.$file_name;
$this->autoRender = false;
//if you trying to test it from localhost set the localhost path
//other wise live path
$this->response->file($path_live or $path_local, array('download' => true));
}
In your view you need to link to this controller action, though (if you think about it):
$this->Html->link('Download', array(
'controller' => 'controller_name', 'action' => 'send_file', $file_name
));
Also note the conventions I corrected for you - the docs tell you that as well.
Related
I am looking for input/help on how to do this. Might be some PHP/cake developers could provide some good solutions here. Cakephp 2.3 something :)
Problem; How to put shortcodes in wysiwyg editor (example: [slideshow=1]slideshow here[/slideshow]) and render an element (in this case, loading and displaying the slideshow with ID=1).
ShortcodeHelper.php
App::import('Helper', 'Html', 'Router');
class ShortcodeHelper extends AppHelper {
public $shortcodes = array(
'slideshow' => '/(\[slideshow=)(.+?)(\])(.+?)(\[\/slideshow\])/'
);
public $returncodes = array(
//'slideshow' => $this->render('/elements/slideshow', array('id'=>'\\2'))
'slideshow' => '<strong rel="\\2">\\4</strong>'
);
public function render($content, $render=null) {
$shortcodes = $this->shortcodes;
$returncodes = $this->returncodes;
if(isset($render)) {
$temp_shortcodes = array();
$temp_returncodes = array();
foreach ($render as $key => $value) {
$temp_shortcodes[$key] = $shortcodes[$value];
$temp_returncodes[$key] = $returncodes[$value];
}
$returncodes = $temp_returncodes;
$shortcodes = $temp_shortcodes;
}
$return = preg_replace($shortcodes, $returncodes, $content);
return $this->output($return);
}
}
view.ctp (call render function from helper, and run the page-content trough it):
<?php echo $this->Shortcode->render($page['Page']['body']); ?>
Thanks. You are awesome!! :)
-Tom
You need to turn the short code string into a method call, parse it.
Your helper will need to be able to detect them and then break them up. Your code needs to be mapped somehow to a callback.
// [slideshow=1]slideshow here[/slideshow]
$this->requestAction(array('controller' => 'slideshows', 'action' => 'view', $id);
For example.
I think the best way here would be to just always map the first arg, the "function call" to an element instead and pass all other args to the element. This way you can do there whatever you want and request the data or just simply display HTML only.
I would put the mapping of short codes into something like Configure::write('ShortCodes', $shortCodeArray); this way plugins could even register their callback mapping by simply adding them to that array.
array(
'slideshow' => array('controller' => 'slideshows', 'action' => 'view')
);
You'll have to merge that with args from the parsed short code.
Why requestAction()? You should not violate the MVC pattern, for this reason you'll have to request the data via requestAction().
i am new to cake and using version 2.
i have models
hgcylinder.php
class HgCylinder extends AppModel {
//put your code here
var $name= "HgCylinder";
var $belongsTo = array('HgKeyGase');
}
hgkeygase.php
class HgKeyGase extends AppModel {
//put your code here
var $name= "HgKeyGase";
public $belongsTo = array(
'HgKeyColor' => array(
'className' => 'HgKeyColor',
'foreignKey' => 'hg_key_color_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
Controller HgCylindersController.php
<?php
class HgCylindersController extends AppController
{
var $name = "HgCylinders";
// var $scaffold;
function index()
{
$this->set('hey',$this->HgCylinder->find('all'));
}
public function edit($id = null) {
$this->HgCylinder->id = $id;
if ($this->request->is('post')) {
var_dump($this->request->data);
exit;
if ($this->HgCylinder->save($this->request->data)) {
$this->Session->setFlash(__('Cylinder has been updated successfull'));
$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The cylinder could not be saved. Please, try again.'));
}
} else {
$this->request->data = $this->HgCylinder->read(null, $id);
}
$hg_key_gase_id = $this->HgCylinder->HgKeyGase->find('list');
$this->set(compact('hg_key_gase_id'));
}
}
?>
View : edit.ctp
<?php
echo $this->form->create('HgCylinder',array('action'=>'edit'));
echo $this->form->input('hg_key_gase_id',);
echo $this->form->input("capacityM3");
echo $this->form->input("weightEmpty");
echo $this->form->input("weightFilled");
echo $this->form->input("isFilled");
echo $this->form->end('Add');
?>
my problem is hg_key_gase_id is become select list with no options. if i changed the name to "hgKeyGas" in view and controller it shows the options from the hg_key_gases table. but on saving does not saving the value of hg_key_gase_id field in hg_cylinders table instead it stores null in this field.
second i want to know that it is necessary to have variable name passing to view from controller exactly same for as field in table.
try to stick to conventions.
so its
$hgKeyGases = $this->HgCylinder->HgKeyGase->find('list');
$this->set(compact('hgKeyGases'));
the pluralized form will be able to populate your select box (as documented in the cook book)
also use $this->Form->input() (note the capital F). $name is not necessary.
dont use read(), use find(first) instead. dont set the action (the form will post to itself by default).
and most importantly. ALWAYS respect the casing of files in your filesystem. especially if you plan on deploying on NIX systems (which are case sensitive).
so it would be HgCylinder.php and HgKeyGase.php as Model class files.
last tip: use baking (cake bake via shell console) to bake your crud files. this way you learn how its done the right way. it would have also answered your question itself by the way.
the documentation can be found here: http://book.cakephp.org/2.0/en/index.html
maybe you found an old outdated version, the 2.x one is the one you should have used.
I'm trying to implement something like Mark Story's "Down for Maintenance" page using CakePHP 2.1.0. I'm pretty close to achieving this, but I'm running into two issues that I could use some help with. First of all, here is all of the relevant code (six files):
1) app/Config/bootstrap.php:
Configure::write('App.maintenance', true);
2) app/Config/core.php:
Configure::write('debug', 1);
...
Configure::write('Exception', array(
'handler' => 'ErrorHandler::handleException',
'renderer' => 'AppExceptionRenderer',
'log' => true
));
3) app/Controller/AppController.php:
if (Configure::read('App.maintenance') == true) {
App::uses('DownForMaintenanceException', 'Error/Exception');
throw new DownForMaintenanceException(null);
}
4) app/Lib/Error/Exception/DownForMaintenanceException.php:
<?php
class DownForMaintenanceException extends CakeException {}
5) app/Lib/Error/AppExceptionRenderer.php:
<?php
App::uses('ExceptionRenderer', 'Error');
class AppExceptionRenderer extends ExceptionRenderer {
function _outputMessage($template) {
// Call the "beforeFilter" method so that the "Page Not Found" page will
// know if the user is logged in or not and, therefore, show the links that
// it is supposed to show.
if (Configure::read('App.maintenance') == false)
{
$this->controller->beforeFilter();
}
parent::_outputMessage($template);
}
public function downForMaintenance() {
$url = $this->controller->request->here();
$code = 403;
$this->controller->response->statusCode($code);
$this->controller->set(array(
'code' => $code,
'url' => h($url),
'isMobile' => $this->controller->RequestHandler->isMobile(),
'logged_in' => false,
'title_for_layout' => 'Down for Maintenance'
));
$this->_outputMessage($this->template);
}
}
6) app/View/Errors/down_for_maintenance.ctp:
<p>Down for Maintenance</p>
Now, for the two issues I'm experiencing. First, this code only works when debug is set higher than 1. Is there anything I can do about that? Does that indicate that I'm going about this the wrong way? The second issue is that, although I'm setting the "isMobile" and "logged_in" view variables to boolean values in the "downForMaintenance" method, the "app/View/Layouts/default.ctp" file is seeing them as strings. What can I do about that?
Thanks!
here is a quick and dirty maintenance page for cakephp
in public index.php
define('MAINTENANCE', 0);
if(MAINTENANCE > 0 && $_SERVER['REMOTE_ADDR'] !='188.YOUR.IP.HERE')
{
require('maintenance.php'); die();
}
Then just change MAINTENANCE = 1 when you want to take your site down and it will still be viewable from your home/office.
BONUS: Works with all versions of cake!
A more elegant way would be to add a route overriding any other one at the very top of routes.php:
//Uncomment to set the site to "under construction"
Router::connect('/*', array('controller' => 'pages', 'action' => 'underConstruction'));
//any other route should be underneath
If you want to add any condition you can also do it here:
define('MAINTENANCE', 0);
if(MAINTENANCE > 0 && $_SERVER['REMOTE_ADDR'] !='188.YOUR.IP.HERE')
Router::connect('/*', array('controller' => 'pages', 'action' => 'underConstruction'));
}
We'll need to create a custom Dispatch Filter,CakePHP has you covered.
check below link
http://josediazgonzalez.com/2013/12/13/simple-application-maintenance-mode/
In a download page for a blob from a database, how would I make it so that no other output is sent? Right now it's sending the header, debug info, and a footer. How do I make it so that none of that is sent, just for that view?
you can create an clear layout (e.g. empty.ctp ) in you layouts folder, only with
<?php echo $content_for_layout ?>
and then in you action where you're getting your blob data use that layout
$this->layout = 'empty.ctp';
and also to disable debugging, in your controllers use
Configure::write('debug',0);
if you're unable to create new layout you could try this.
$this->layout = null;
$this->render("view_name");
If you're using this to download files, you should use the Media view in cakePHP
http://book.cakephp.org/view/1094/Media-Views
$this->view = 'Media';
$params = array(
'id' => 'example.zip',
'name' => 'example',
'download' => true,
'extension' => 'zip', // must be lower case
'path' => APP . 'files' . DS // don't forget terminal 'DS'
);
CakePhp 2.3 users :
use Sending files from the Book
CakePhp 2.x users :
use '$this->viewClass' instead of '$this->view'
copy-paste ready full solution, right in any controller file:
<?php
public function download($file) {
$fsTarget = APP.WEBROOT_DIR.DS.'files'.DS.$file; // files located in 'files' folder under webroot
if (false == file_exists($fsTarget)){
throw new NotFoundException(__('Invalid file'));
}
$pathinfo = pathinfo($fsTarget);
$this->viewClass = 'Media';
$params = array(
'id' => $file,
'name' => $pathinfo['filename'], // without extension
'download' => true,
'extension' => $pathinfo['extension'], // must be lower case
'path' => dirname($fsTarget) . DS // don't forget terminal 'DS'
);
$this->set($params);
}
Hope this helps!
I have set up the RSS Helper (with CakePHP 1.3.4) and all is working - I can access my feeds by /news/feed.rss - as exampled at http://book.cakephp.org/view/1461/Creating-an-RSS-feed-with-the-RssHelper
But I want to be able to do this conditionally, in sudo, something like:
if (!empty($var)) {
switch ($var) {
case one :
$xml = $this->method->find('$var conditions...');
... use RSS Helper to serve results as XML.
case two :
$xml = $this->method->find('other $var conditions...');
... use RSS Helper to serve results as XML.
}
}
Can I use the RSS Helper in this circumstance? What calls/syntax do I use?
There are 2 ways to do this.
Basically you can pass a variable like this:
http://yourserver.com/news/feed.rss?recent=20
and then in the controller you can access this variable with
$this->params['url']['recent']; //20
Or you can add a line in your Router file like this:
Router::connect('/feed-:recent/*', array('plugin'=>false, 'controller' => 'news', 'action' => 'feed'), array('recent'=>'[0-9]+'));
This way your url will look like:
http://yourserver.com/news/feed-20.rss
and finally I believe that url like this will work as well:
http://yourserver.com/news/feed.rss/recent:20
You can pass params to the function the same as any other controller call.
IE
public function rss( $limit = 20, $topic = null ){
if( !$topic ){
$xml = $this->Article->find( 'all', array( 'limit' => $limit ));
...
} else {
$xml = $this->Article->find( 'all', array( 'limit' => $limit, 'conditions' => array( 'Article.topic' => $topic )));
}
...
}
}
?>
Then you can access the rss feed with: http://domain/articles/rss.rss
If you need to pass parameters: http://domain/articles/rss/100.rss (fetch all with 100 results)
Or like this: http://domain/articles/rss/10/obama.rss (fetch all with the topic "obama" and return 10 items.
The RequestHandler allows you to use a url like /posts/index.rss and it automatically loads the RSS Helper and sends the output to /views/posts/rss/index.ctp, and uses layout at /views/layouts/rss/default.ctp.
But if you want to use the RSS Helper on it's own you need to:
1) include it the controller
var $helpers = array('Rss');
2) in your controller action you need to specify where the output goes, so
$this->render('/posts/rss/index','/rss/default');
In this case I also specified which layout to use in the 2nd argument. The first arg - the index.ctp file location is relative to your views/ directory. The second arg - the layout is relative to your views/layouts/ directory.
So I have a method in my posts_controller that I use to identify which feed is wanted (through a passed var) and subsequently finds the posts and sends them to the correct views and layouts:
function rss(){
if (!empty($this->params['pass'])){
$ops=array(
'conditions'=>'where feedname_id=' . $this->params['pass'][0],
'order' => 'Post.created DESC',
'limit' => 10
);
$this->set('posts', $this->Post->find('all',$ops));
$this->render('/posts/rss/index','/rss/default');
} else {
$this->redirect(array('controller'=>'feednames','action'=>'index'));
}
}
There might be better ways to code this - if so please let me know.