Yii – Rewrite file or image urls for download or display

I recently had a need to track files and images which were uploaded and for their urls to be secret. This meant that they couldn’t sit in a folder waiting to be found and had to be saved below the site root.

To get access to them I wanted to the check the user permissions followed by forcing download of all files (including images). Here are the steps I took to accomplish this…

For the sake of this page, my (fake) file urls are something like:

example.com/user/123/file/456.pdf

Update the urlmanager to rewrite files to my filemanager/view:


	'user//file/.[a-z0-9]+' => 'filemanager/view',

Update the filemanager class’ actionView function:


	public function actionView($id)
	{
		$file = $this->loadModel($id);

		header("Pragma: public"); // required
		header("Expires: 0");
		header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
		header("Cache-Control: private",false); // required for certain browsers 
		//header("Content-Type: " . $file->mime_type);
		header("Content-Disposition: attachment; filename=\"" . str_replace(" ", "-", preg_replace("@[^a-z0-9 ]@", "", strtolower($file->file_name))) . '.' . $file->file_extension . "\";" );
		header("Content-Transfer-Encoding: binary");
		header("Content-Length: ".filesize(Yii::app()->basePath . '/../../uploads/user_files/' . $file->file_id . '.' . $file->file_extension));
		readfile(Yii::app()->basePath . '/../../uploads/user_files/' . $file->file_id . '.' . $file->file_extension);
		exit();

	}

I also of course required filemanager access to be strict:


	public function accessRules()
	{
		return array(
			array('allow', 
				'actions'=>array('view', 'create','delete'),
				'users'=>array(),
				'expression' => 'User::model()->hasRightsToFile()',
			),
			array('deny',  // deny all users
				'users'=>array('*'),
			),
		);
	}

And that’s about it. Controlled and forced file download with rewritten file urls.

[carousel keywords=”yii” tag=”fetchit-21″]

Yii – Only fill filter dropdown with used options

Sometimes you want to show a filter dropdown but by default it will show all value options even if not assigned to anything. You can use findAll with criteria to only bring back items which are in use…


$criteria = new CDbCriteria;
$criteria->join = 'INNER JOIN organisation ON organisation.category_id = t.category_id';

if($_GET['school_id'])
{
	$criteria->condition = 'organisation.school_id = ' . $_GET['school_id'];
}
else
{

}

$categories = OrganisationCategory::model()->findAll($criteria);
$categories_array = array();
foreach($categories as $category)
{
	$categories_array[$category->category_id] = $category->name;
}


$columns = array(
	'name',
	array(
		'name'=>'category_id',
		'value' => 'OrganisationCategory::model()->findByPk($data->category_id)->name',
		'filter' => $categories_array
[...]

[carousel keywords=”yii” tag=”fetchit-21″]

Yii CGridView default Order

If you want to set a default sort order for a CGridView, often used in admin views you can do so in the controller as part of the search() function:

$dataProvider = new CActiveDataProvider(get_class($this), array(
	'criteria'=>$criteria,
	'sort' => array('defaultOrder' => 'name')
));

[carousel keywords=”yii” tag=”fetchit-21″]

Using PHP_SELF Safely and submitting forms to the same page

I’ve lost count of the number of times i’ve seen this bit of HTML / PHP:

">

Looks pretty harmless doesn’t it, but it is a pretty dangerous shortcut to use. Imagin I get a user to visit the page the form is on by following this link, maybe hiding it in a short url:

http://example.com/formpage.php?"> 

where I’ve added some html into the url which contains a script tag.

I could use this method to grab all your cookies and log in as you, or send ajax requests back to the site on your behalf. All very frightening. The quick solution is to turn html characters into their harmless entities using the php function htmlspecialchars. So the code would be


But wait! The best way to submit to the same page with a form is to use and empty action attribute. It’s valid and it works.


Don’t believe me? Go tell Jesse. He also wrote about empty action attributes.

[carousel keywords=”php” tag=”fetchit-21″]

Writing a PHP Coda Plugin

Sound like a right pain in the arse? It’s surprisingly simple actually….

The steps to creating a simple locally run php Coda plugin:

You must have php installed and running locally.
Start the plugin file with theses lines:

#!/usr/bin/php -q

(no space between < and ?php) Where /use/bin/php is the path to you local php install Continue reading