How to create export function so member can download a CSV file

AndyB

Well-known member
Hello,

I'm creating an add-on which I would like to add an 'export' function. The way I would like to make it work is to add the code to the ControllerPublic/NewsletterSubscribe.php file.

For example the admin will invoke the export function by adding the the word export after the route.

Example:

http://www.domain.com/newslettersubscribe/export

With this URL I would like my add-on to prepare the $results and have a download dialog box appear on the admins display where he can download the $results to his computer.

Currently i have the following:

library/Andy/NewsLetterSubscribe/ControllerPublic/NewletterSubscribe

PHP:
	public function actionExport()
	{
		// get permission
		if (!XenForo_Visitor::getInstance()->hasPermission('newsletterSubscribeGID', 'newsletterSubscribeID'))
		{
			throw $this->getNoPermissionResponseException();
		}
		
		// get database
		$db = XenForo_Application::get('db');		
		
		// get data
		$results = $db->fetchCol("
			SELECT email
			FROM xf_newsletter_subscribe
			ORDER BY email ASC
		");
	}

What code do I need to add?

Thank you.
 
Hi Jake B.,

Thank you for your post. Yes I understand that part, sorry I wasn't more clear. The part I'm having trouble with is to get a prompt to save the file. I understand I will need to add some header code or call a function for this.
 
Everything you'll need is in
XenForo_ControllerPublic_Attachment::actionIndex
and
XenForo_ViewPublic_Attachmentview::renderRaw
 
My question is how to get a download dialog by adding code to the ControllerPublic, I'm not sure if it's possible. I was not able to use the code in other controllers, hence my call for help.
 
My question is how to get a download dialog by adding code to the ControllerPublic, I'm not sure if it's possible. I was not able to use the code in other controllers, hence my call for help.

Yes, you'll need to use a view. Take a look at XenForo_ViewPublic_Attachmentview::renderRaw
 
Yes, you'll need to use a view. Take a look at XenForo_ViewPublic_Attachmentview::renderRaw

Thank you for your suggestion.

What code would I use in the library/Andy/NewsLetterSubscribe/ControllerPublic/NewletterSubscribe.php file to call the XenForo_ViewPublic_Attachment_View class?
 
Thank you for your suggestion.

What code would I use in the library/Andy/NewsLetterSubscribe/ControllerPublic/NewletterSubscribe.php file to call the XenForo_ViewPublic_Attachment_View class?

You wouldn't call XenForo_ViewPublic_Attachment_View, you'd create your own view and use parts of the code from that.
 
I almost got this working perfect with this code:

PHP:
	public function actionExport()
	{
		// get permission
		if (!XenForo_Visitor::getInstance()->hasPermission('newsletterSubscribeGID', 'newsletterSubscribeID'))
		{
			throw $this->getNoPermissionResponseException();
		}

		// get database
		$db = XenForo_Application::get('db');		
		
		// get data
		$results = $db->fetchCol("
			SELECT email
			FROM xf_newsletter_subscribe
			ORDER BY email ASC
		");	
		
		// define variable
		$csv = '';
		
		foreach ($results as $result)
		{
			$csv = $csv . $result . ',';
		}

		// write to temporary file
		$file = tempnam(XenForo_Helper_File::getTempDir(), 'xf');
		
		$handle = fopen($file, "w");
		fwrite($handle, $csv);
		fclose($handle);
		
		// rename file
		XenForo_Helper_File::safeRename($file, '/tmp/export.csv');	
		
		// decalre variable
		$file = '/tmp/export.csv';
		
		if (file_exists($file)) {
			header('Content-Description: File Transfer');
			header('Content-Type: application/octet-stream');
			header('Content-Disposition: attachment; filename="'.basename($file).'"');
			header('Expires: 0');
			header('Cache-Control: must-revalidate');
			header('Pragma: public');
			header('Content-Length: ' . filesize($file));
			readfile($file);
			exit();
		}		
	}

The one problem is the code uses the exit() command, so after you save the export.csv file to your local disk, the URL changes internally to the web root:

http://www.domain.com/

So this is a little confusing and I'm not sure why this happens.
 
Got it.

What I needed to do was to have the following link:

http://www.domain.com/newslettersubscribe/export

call a template and in the template have a link to the actual export.

PHP:
	public function actionExport()
	{
		// send to template
		return $this->responseView('Andy_Trader_ViewPublic_Trader','andy_newslettersubscribe_export');
	}
	
	public function actionExportLink()
	{
		// get permission
		if (!XenForo_Visitor::getInstance()->hasPermission('newsletterSubscribeGID', 'newsletterSubscribeID'))
		{
			throw $this->getNoPermissionResponseException();
		}		

		// get database
		$db = XenForo_Application::get('db');		
		
		// get data
		$results = $db->fetchCol("
			SELECT email
			FROM xf_newsletter_subscribe
			ORDER BY email ASC
		");	
		
		if (!empty($results))
		{
			// comma separate results
			$csv = implode($results, ',');
			
			// remove trailing comma
			$csv = rtrim($csv, ',');		
	
			// write to temporary file
			$file = tempnam(XenForo_Helper_File::getTempDir(), 'xf');
			
			$handle = fopen($file, "w");
			fwrite($handle, $csv);
			fclose($handle);
			
			// rename file
			XenForo_Helper_File::safeRename($file, '/tmp/export.csv');	
			
			// decalre variable
			$file = '/tmp/export.csv';
			
			if (file_exists($file)) {
				header('Content-Description: File Transfer');
				header('Content-Type: application/octet-stream');
				header('Content-Disposition: attachment; filename="'.basename($file).'"');
				header('Expires: 0');
				header('Cache-Control: must-revalidate');
				header('Pragma: public');
				header('Content-Length: ' . filesize($file));
				readfile($file);		
				exit();
			}
		}
	}
 
Last edited:
Also consider using the built-in php function fputcsv to ensure the output is formatted correctly. Especially as email addresses can have all sorts of stuff encoded into them.
 
Top Bottom