Select Git revision
      
  downloads_for_sale.php
              Reinhold Kainhofer authored 
 -) Send file size in http header -) Check if the file exists and is readable on disk -) Don't flush the queue before trying to change the header (will break if the header was already set before)
  downloads_for_sale.php  22.40 KiB 
<?php
defined('_JEXEC') or 	die( 'Direct Access to ' . basename( __FILE__ ) . ' is not allowed.' ) ;
/**
 * A custom field plugin for downloadable files
 * @author Reinhold Kainhofer
 * @package VirtueMart
 * @subpackage vmcustom
 * @copyright Copyright (C) 2013 Reinhold Kainhofer - All rights reserved.
 * @license http://www.gnu.org/copyleft/gpl.html GNU/GPL, see LICENSE.php
 * VirtueMart is free software. This version may have been modified pursuant
 * to the GNU General Public License, and as distributed it includes or
 * is derivative of works licensed under the GNU General Public License or
 * other free or open source software licenses.
 * See /administrator/components/com_virtuemart/COPYRIGHT.php for copyright notices and details.
 *
 * http://kainhofer.com
 */
if (!class_exists('vmCustomPlugin')) require(JPATH_VM_PLUGINS . DS . 'vmcustomplugin.php');
if(!class_exists('VmTable'))require(JPATH_VM_ADMINISTRATOR.DS.'helpers'.DS.'vmtable.php');
if (JVM_VERSION === 2) {
    if (!defined('VMDLSALE_PLUGINPATH')) define('VMDLSALE_PLUGINPATH', JPATH_SITE.DS.'plugins'.DS.'vmcustom'.DS.'downloads_for_sale');
    if (!defined('VMDLSALE_PLUGINWEBROOT')) define('VMDLSALE_PLUGINWEBROOT', 'plugins/vmcustom/downloads_for_sale');
} else {
    if (!defined('VMDLSALE_PLUGINPATH')) define('VMDLSALE_PLUGINPATH', JPATH_SITE.DS.'plugins'.DS.'vmcustom');
    if (!defined('VMDLSALE_PLUGINWEBROOT')) define('VMDLSALE_PLUGINWEBROOT', 'plugins/vmcustom');
}
class plgVmCustomDownloads_for_Sale extends vmCustomPlugin {
	function __construct(& $subject, $config) {
		parent::__construct($subject, $config);
		$this->_tablepkey = 'id';
		$this->tableFields = array_keys($this->getTableSQLFields());
		$varsToPush = array(
			'media_id'=>array(0,'char'),
			'invoice_link_type'=>array('text', 'char'),
			'product_link_type'=>array('image','char'),
			'download_type'=>array('free_download','char'),
			'paid_status'=>array(array('S', 'C'), 'array'),
			'downloaded_status'=>array('', 'char'),
		);
		$this->setConfigParameterable('custom_params',$varsToPush);
		$this->onStoreInstallPluginTable($this->_psType);
	}
	function getDownloadFiles () {
		$db = JFactory::getDBO();
		$db->setQuery('SELECT * FROM `#__virtuemart_medias` WHERE `file_is_downloadable`=1 OR `file_is_forSale`=1 ');
		return $db->loadObjectList();
	}
	function getDownloadFile ($media_id) {
		$db = JFactory::getDBO();
		$db->setQuery('SELECT * FROM `#__virtuemart_medias` WHERE `virtuemart_media_id` = '.(int)$media_id);
		return $db->loadObject();
	}
	
	function getCustomfieldOrderitems ($field, $user, $show_warnings) {
		$orderModel = VmModel::getModel('orders');
		$order_number = JRequest::getString('order_number', null);
		$order_pass = JRequest::getString('order_pass', null);
		$orders = array();
		if ($order_number && $order_pass) {
			$order_id = $orderModel->getOrderIdByOrderPass ($order_number, $order_pass);
			if ($order_id) {
				$orders[] = $orderModel->getOrder($order_id);
			} 
			if (empty($orders) && $show_warnings) {
				JFactory::getApplication()->enqueueMessage(JText::_('VMCUSTOM_DLSALE_ERROR_WRONG_PASSWD'), 'error');
			}		} elseif ($order_number) {
			// Check if user has access
			$order_id = $orderModel->getOrderIdByOrderNumber ($order_number);
			if ($order_id) {
				$order = $orderModel->getOrder($order_id);
				if ($order['details']['BT']->virtuemart_user_id == $user) {
					$orders[] = $order;
				} elseif ($show_warnings) {
					JFactory::getApplication()->enqueueMessage(JText::_('VMCUSTOM_DLSALE_ERROR_NOT_AUTHORIZED'), 'error');
				}
			} elseif ($show_warnings) {
				JFactory::getApplication()->enqueueMessage(JText::_('VMCUSTOM_DLSALE_ERROR_ORDER_NOT_FOUND'), 'error');
			}
		} else {
			if ($user>0) {
				$os = $orderModel->getOrdersList($user, true);
				foreach ($os as $o) {
					$orders[] = $orderModel->getOrder($o->virtuemart_order_id);
				}
			}
		}
		if (isset($field->virtuemart_product_id) && $field->virtuemart_product_id) {
			$prodid = $field->virtuemart_product_id;
		} else {
			JFactory::getApplication()->enqueueMessage(JText::sprintf("WARNING: no product ID given in custom field. Please contact the plugin developer and copy this message: <pre>%s</pre>", print_r($field,1)), 'error');
			return array();
		}
		$orderitems = array();
		foreach ($orders as $order) {
			if ($order && in_array($order['details']['BT']->order_status, $field->paid_status)) {
				foreach ($order['items'] as $i) {
					if ($i->virtuemart_product_id == $prodid) {
						if (in_array($i->order_status, $field->paid_status)) {
							$orderitems[] = $i;
						} elseif ($show_warnings) {
							JFactory::getApplication()->enqueueMessage(JText::_('VMCUSTOM_DLSALE_ERROR_STATUS_NOT_AUTHORIZED'), 'error');
						}
					}
				}
			}
		}
		return $orderitems;
	}
	
	function checkDownloadable ($field, $orderitem=null, $show_warnings=false) {
		// Free downloads are always allowed
		if ($field->download_type == 'free_download') {
			return true;
		}
		// For registered downloads it suffices to be logged in (that's not neccessary, giving
		// the order nr/pwd should also work, so don't return false here!)
		$userId = (int) JFactory::getUser()->get('id');
		if (($field->download_type == 'registered_download') && ($userId>0)) {
			return ($userId>0);
		}
		// In all other cases, check that the orderitem has the correct state
		if ($orderitem) {
			if (in_array($orderitem->order_status, $field->paid_status)) {
				return true;
			} elseif ($show_warnings) {
				JFactory::getApplication()->enqueueMessage(JText::sprintf("Order status %s does not allow download (allowed states are %s)", $orderitem->order_status, join($field->paid_status, ", ")), 'error');
			}
		}
		return false;
	}
	function loadCustomfieldData ($customfield_id) {
		$db = JFactory::getDbo();	
		$q = 'SELECT * FROM `#__virtuemart_customs` LEFT JOIN `#__virtuemart_product_customfields` USING (`virtuemart_custom_id`, `custom_value`) WHERE `virtuemart_customfield_id` = '.(int)$customfield_id . ' AND `custom_value`=\''.$db->getEscaped($this->_name).'\'';
		$db->setQuery($q);
		$field = $db->loadObject();
		if(!$field) return false;
		$err = $db->getErrorMsg();
		if (!empty($err)) {
			JError::raiseWarning(500, 'Downloads for sale plugin, loadCustomfieldData error '.$err);
			return false;
		}
		$field->custom_element = $this->_name;
		$this->parseCustomParams($field);
		return $field;
	}
	
	// This readfile replacement comes from the php.net documentation of readfile (http://www.php.net/readfile), in particular flowbee@gmail.com and chrisputnam@gmail.com summarizing discussions @php-dev:
	function readfile_chunked($filename, $retbytes=true) {
		$chunksize = 1*(1024*1024); // how many bytes per chunk
		$buffer = '';
		$cnt =0;
		$handle = fopen($filename, 'rb');
		if ($handle === false) {
			return false;
		}
		while (!feof($handle)) {
			$buffer = fread($handle, $chunksize);
			echo $buffer;
			ob_flush();
			flush();
			if ($retbytes) {
				$cnt += strlen($buffer);
			}
		}
		$status = fclose($handle);
		if ($retbytes && $status) {
			return $cnt; // return num. bytes delivered like readfile() does.
		}
		return $status;
	}
	
	function downloadFile($media_id, &$output){
		if ($media_id) {
			jimport('joomla.filesystem.file');
			$media = $this->getDownloadFile($media_id);
			if (!$media) {
				JFactory::getApplication()->enqueueMessage(JText::sprintf('VMCUSTOM_DLSALE_ERROR_MEDIA_NOT_CONFIGURED', $media_id), 'error');
				return false;
			}
			if (!is_file($media->file_url) || !is_readable($media->file_url)) {
				JFactory::getApplication()->enqueueMessage(JText::sprintf('VMCUSTOM_DLSALE_ERROR_NO_FILE_SET', $media_id, $media->file_url), 'error');
				return false;
			}
			header("Content-Type: " . $media->file_mimetype);
			header("Content-Disposition: attachment; filename=\"".JFile::getName($media->file_url)."\"");
			header("Content-Length: ".filesize($media->file_url));
			if (@$this->readfile_chunked($media->file_url)) {
				return true;
			} else { 
				return false;
			}
		} else {
			$output = JText::_('VMCUSTOM_DLSALE_NO_FILE_FOUND');
		}
		return false;
	}
	function updateDownloadCounterSQL ($field, $orderitem) {
		$db = JFactory::getDBO();
		$sql = "SELECT `id` FROM `" . $this->_tablename . "` WHERE `virtuemart_customfield_id`=" . (int)$field->virtuemart_customfield_id .
		       " AND `virtuemart_order_item_id`=" . (int)($orderitem?$orderitem->virtuemart_order_item_id:0);
		$user = (int)JFactory::getUser()->get('id');		$date = JFactory::getDate()->toMySQL();
		
		$db->setQuery($sql);
		$id = $db->loadResult();
		if ($id) {
			// Entry found, update downloaded 
			$sql = "UPDATE `" . $this->_tablename . "` SET `downloaded`=(`downloaded`+1), `modified_on` = '" . $date . "', `modified_by`=" . (int)$user . " WHERE `id`=" . (int)$id;
			$db->setQuery($sql);
			$db->query();
		} else {
			// New entry, insert with value 1
			$sql = "INSERT INTO `" . $this->_tablename . 
			      "` (`virtuemart_customfield_id`, `virtuemart_order_item_id`, `downloaded`, `created_on`, `created_by`) " .
			      "VALUES (" . (int)$field->virtuemart_customfield_id . ", " . (int)($orderitem?($orderitem->virtuemart_order_item_id):0) . 
			      ", 1, '" . $date . "', " . (int)$user . ")";
			$db->setQuery($sql);
			$db->query();
		}
	}
	
	function updateOrderStatus ($field, $orderitem) {
		if (!$orderitem || empty($field->downloaded_status)) return;
		$orderModel = VmModel::getModel('orders');
		
		# Update the whole order:
		$order = $this->orderDataFromItem ($orderitem->virtuemart_order_item_id);
		$orderdata['order_status'] = $field->downloaded_status;
		$orderdata['customer_notified'] = 0;
		$orderdata['comments'] = '';
		if ($order->order_status != $orderdata['order_status']) {
			$orderModel->updateStatusForOneOrder($order->virtuemart_order_id, $orderdata, /*triggers=*/false);
		}
	}
	function plgVmOnSelfCallFE($type,$name,&$render) {
		if ($name != $this->_name || $type != 'vmcustom') return false;
		$handled = false;
		$field_id = JRequest::getInt('customfield_id',0);
		$customModel = VmModel::getModel('customfields');
		if ($field_id) {
			$field = $this->loadCustomfieldData ($field_id);
			if ($field) {
				$this->parseCustomParams($field);
				$orderitems = $this->getCustomfieldOrderitems ($field, JFactory::getUser()->get('id'), true);
				$orderitem = empty($orderitems)?null:$orderitems[0];
				if ($this->checkDownloadable ($field, $orderitem, true)) {
					$handled = true;
					if ($this->downloadFile ($field->media_id, $render)) {
						// Successful download, so set order status to shipped, then exit to prevent the normal joomla page formatting (we sent a potentially binary file already!)
						$this->updateDownloadCounterSQL ($field, $orderitem);
						$this->updateOrderStatus ($field, $orderitem);
						JExit();
					} else {
						return true;
					}
				} else {
					JFactory::getApplication()->enqueueMessage(JText::_('VMCUSTOM_DLSALE_ERROR_NOT_AUTHORIZED_UNKNOWN'), 'error');
				}
			} else {
				JFactory::getApplication()->enqueueMessage(JText::_('VMCUSTOM_DLSALE_ERROR_LOAD_FAILURE'), 'error');
			}
		}
		if (!$handled) {
			$user = JFactory::getUser()->get('id');
			// If we have an order number and password, display all downloads of that order, even if the user is not logged in:
			$field_id = JRequest::getInt('customfield_id',0);
			$order_number = JRequest::getString('order_number', null);			$order_pass = JRequest::getString('order_pass', null);
			$orders = array();
			if ($order_number && $order_pass) {
				$orderModel = VmModel::getModel('orders');
				$order_id = $orderModel->getOrderIdByOrderPass ($order_number, $order_pass);
				if ($order_id) {
					$orders[] = $orderModel->getOrder($order_id);
				}
				if (empty($orders)) {
					JFactory::getApplication()->enqueueMessage(JText::_('VMCUSTOM_DLSALE_ERROR_WRONG_PASSWD'), 'error');
				}
			} elseif ($order_number) {
				$orderModel = VmModel::getModel('orders');
				$order_id = $orderModel->getOrderIdByOrderNumber ($order_number);
				if ($order_id) {
					$order = $orderModel->getOrder($order_id);
					if ($order['details']['BT']->virtuemart_user_id == $user) {
						$orders[] = $order;
					}
				}
				if (empty($orders)) {
					JFactory::getApplication()->enqueueMessage(JText::_('VMCUSTOM_DLSALE_ERROR_NOT_AUTHORIZED'), 'error');
				}
			} else {
				if ($user>0) {
					$orderModel = VmModel::getModel('orders');
					$os = $orderModel->getOrdersList($user, true);
					foreach ($os as $o) {
						$orders[] = $orderModel->getOrder($o->virtuemart_order_id);
					}
				}
			
			}
			
			$orderdownloads = array();
			foreach ($orders as $order) {
				$orderinfo = $order;
				$orderinfo['download_products']=array();
				foreach ($order['items'] as $i) {
					$productinfo=$i;
					$productinfo->downloads = array();
					$customs = (array)$customModel->getproductCustomslist ($i->virtuemart_product_id);
					foreach ($customs as $field) {
						$downloadable = ($field->custom_element == $this->_name);
						if ($field->download_type == "paid_download") {
							$downloadable = $downloadable && in_array($order['details']['BT']->order_status, $field->paid_status);
						}
						$downloadable = $downloadable && $this->checkDownloadable ($field, $i, false);
						// Order needs to be downloadable and the individiaul field, too:
						if ($downloadable) {
							$productinfo->downloads[] = $this->createDownloadLink ($field, 'order', $field->invoice_link_type, $i);
						}
					}
					if (!empty($productinfo->downloads)) {
						$orderinfo['download_products'][] = $productinfo;
					}
				}
				if (!empty($orderinfo['download_products'])) {
					$orderdownloads[] = $orderinfo;
				}
			}
			
			print $this->renderByLayout('downloads', array($orderdownloads, $order_number));
			return true;
		}
	}
	/**
	 * plgVmOnSelfCallBE ... Called to execute some plugin action in the backend (e.g. set/reset dl counter, show statistics etc.)
	 *//*	function plgVmOnSelfCallBE($type, $name, &$output) {
		if ($name != $this->_name || $type != 'vmcustom') return false;
		vmDebug('plgVmOnSelfCallBE');
		
		$db = JFactory::getDBO();
		$nullDate = $db->getNullDate();
	}*/
	/**
	 * @see Form displayed in the product edit page in the BE, configure the download file
	 * @author Reinhold Kainhofer
	 */
	function plgVmOnProductEdit($field, $product_id, &$row,&$retValue) {
		if ($field->custom_element != $this->_name) return '';
		$this->parseCustomParams($field);
		$html = '';
		$html .='<fieldset>
			<legend>'. JText::_('VMCUSTOM_DLSALE') .'</legend>
			<table class="admintable">
			';
		
		$download_files = $this->getDownloadFiles();
		if (!$download_files) {
			$html .= '<tr><td>'.JText::_('VMCUSTOM_DLSALE_NO_FILES').'</td></tr>';
			$html .= '</table></fieldset>';
			$retValue .= $html;
			return true;
		}
		
		$link_types = array(
			array('link-id'=>'none', 'link-name'=>'VMCUSTOM_DLSALE_LINK_NONE'), 
			array('link-id'=>'compact', 'link-name'=>'VMCUSTOM_DLSALE_LINK_COMPACT'), 
			array('link-id'=>'long', 'link-name'=>'VMCUSTOM_DLSALE_LINK_LONG'),
		);
		$download_types = array (
			array('id'=>'free_download', 'name'=>'VMCUSTOM_DLSALE_TYPE_FREE'),
			array('id'=>'registered_download', 'name'=>'VMCUSTOM_DLSALE_TYPE_REGISTERED'),
			array('id'=>'paid_download', 'name'=>'VMCUSTOM_DLSALE_TYPE_PAID'),
		);
		$html .= VmHTML::row('select', 'VMCUSTOM_DLSALE_DOWNLOAD_FILE', 'custom_param['.$row.'][media_id]', $download_files, $field->media_id,'','virtuemart_media_id', 'file_title', false);
		if ($field->is_cart_attribute) {
			$html .= VmHTML::row ('select','VMCUSTOM_DLSALE_INVOICE_LINK', 'custom_param['.$row.'][invoice_link_type]',$link_types,$field->invoice_link_type, '', 'link-id', 'link-name', false);
		}
		$html .= VmHTML::row ('select','VMCUSTOM_DLSALE_PRODUCT_LINK', 'custom_param['.$row.'][product_link_type]',$link_types,$field->product_link_type, '', 'link-id', 'link-name', false);
		$html .= VmHTML::row('select', 'VMCUSTOM_DLSALE_TYPE', 'custom_param['.$row.'][download_type]', $download_types,$field->download_type,'','id', 'name', false);
		$html .= '</table></fieldset>';
		$retValue .= $html;
		$row++;
		return true ;
	}
	
	
	function createDownloadLink ($field, $view='order', $type='none', $orderitem=null) {
		$url = JURI::root().'index.php?option=com_virtuemart&view=plugin&name=downloads_for_sale&customfield_id='.(int)$field->virtuemart_customfield_id;
		$_currentUser = JFactory::getUser();
		$cuid = $_currentUser->get('id');
		$this->parseCustomParams($field);
		// TODO: Extract download statistics
		switch ($field->download_type) {
			case 'free_download':  break;
			case 'registered_download': if ($cuid<=0) return false; break; 
			case 'paid_download': 
				if ($orderitem) {
					$order = $this->orderDataFromItem ($orderitem->virtuemart_order_item_id);
				} elseif ($cuid>0) {
					$orderModel = VmModel::getModel('orders');
					foreach ($orderModel->getOrdersList($cuid, true) as $ol) {
						$o = $orderModel->getOrder($ol->virtuemart_order_id);						foreach ($o['items'] as $i) {
							if ($i->virtuemart_product_id == $field->virtuemart_product_id && in_array($i->order_status, $field->paid_status)) {
								$order = $o['details']['BT'];
							}
						}
					}
				}
				if (empty($order)) {
					return false;
				}
				$url .= '&order_number='.urlencode($order->order_number).'&order_pass='.urlencode($order->order_pass); 
				break;
		}
		$media = $this->getDownloadFile ($field->media_id);
		return $this->renderByLayout($view.'_'.$type, array($url, $field, $media));
	}
	function createProductDownloadLinks ($field, $type) {
		$return = '';
		if ($type != 'none') {
			$items = $this->getCustomfieldOrderitems ($field, JFactory::getUser()->get('id'), false);
			foreach ($items as $i) {
				if ($this->checkDownloadable ($field, $i, false)) {
					$dllink = $this->createDownloadLink($field, "product", $type, $i);
					if (!empty($dllink)) {
						$return .= $dllink;
					}
				}
			}
			// Handle downloads without a corresponding order
			if (empty($items) && $this->checkDownloadable ($field, null, false)) {
				$dllink = $this->createDownloadLink($field, "product", $type, null);
				if (!empty($dllink)) {
					$return .= $dllink;
				}
			}
		}
		return $return;	
	}
	
	/**
	 * plgVmOnDisplayProductVariantFE ... Called for product variant custom fields to display on the product details page
	 */
	function plgVmOnDisplayProductVariantFE($field,&$row,&$group) {
		// default return if it's not this plugin
		if ($field->custom_element != $this->_name) return '';
		$this->parseCustomParams($field);
		$output = $this->createProductDownloadLinks ($field, $field->product_link_type);
		if (!empty($output)) {
			$group->display .= $output;
			return true;
		} else {
			return false;
		}
	}
	/**
	 * plgVmOnDisplayProductFE ... Called for NON-product variant custom fields to display on the product details page
	 */
	function plgVmOnDisplayProductFE( $product, &$idx,&$field){
		// default return if it's not this plugin
		if ($field->custom_element != $this->_name) return '';
		$field->virtuemart_product_id = $product->virtuemart_product_id;
		$this->parseCustomParams($field);
		$output = $this->createProductDownloadLinks ($field, $field->product_link_type);
		if (!empty($output)) {
			$field->display .= $output;
			return true;
		} else {			return false;
		}
	}
	/** 
	 * plgVmDisplayInOrderBE ... Called to display product variants in the order list in the BE
	 */
	function plgVmDisplayInOrderBE($item, $row, &$html) {
		if (empty($item->productCustom->custom_element) or $item->productCustom->custom_element != $this->_name) return '';
		// MODIFY $html to display info in the backend order
	}
	
	function orderDataFromItem ($orderitem) {
		$db = JFactory::getDBO();
		$q = "SELECT * 
			FROM `#__virtuemart_order_items` JOIN `#__virtuemart_orders` USING (`virtuemart_order_id`) 
			WHERE `virtuemart_order_item_id` = ".(int)$orderitem;
		$db->setQuery($q);
		return $db->loadObject();
	}
	
	/**
	 * plgVmDisplayInOrderFE ... Called to display the dl link in the order in the frontend 
	 */
	function plgVmDisplayInOrderFE($item, $row, &$html) {
		if (empty($item->productCustom->custom_element) or $item->productCustom->custom_element != $this->_name) return '';
		$item->productCustom = $this->loadCustomfieldData ($item->productCustom->virtuemart_customfield_id);
		$this->parseCustomParams($item->productCustom);
		$type = $item->productCustom->invoice_link_type;
		$return = false;
		if ($type != 'none') {
			if ($this->checkDownloadable ($item->productCustom, $item, false)) {
				$dllink = $this->createDownloadLink($item->productCustom, "order", $type, $item);
				if (!empty($dllink)) {
					$html .= $dllink;
					$return = true;
				}
			}
		}
		return $return;
	}
	/**
	 * We must reimplement this triggers for joomla 1.7
	 * vmplugin triggers note by Max Milbers
	 */
	public function plgVmOnStoreInstallPluginTable($psType, $name) {
		return $this->onStoreInstallPluginTable($psType, $name);
	}
	function plgVmDeclarePluginParamsCustom($psType,$name,$id, &$data){
		return $this->declarePluginParams('custom', $name, $id, $data);
	}
	function plgVmSetOnTablePluginParamsCustom($name, $id, &$table){
		return $this->setOnTablePluginParams($name, $id, $table);
	}
	function plgVmOnDisplayEdit($virtuemart_custom_id,&$customPlugin){
		return $this->onDisplayEditBECustom($virtuemart_custom_id,$customPlugin);
	}
	/**
	 * This function is called, when the order is confirmed by the shopper.
	 *
	 * Here are the last checks done by payment plugins.
	 * The mails are created and send to vendor and shopper
	 * will show the orderdone page (thank you page)
	 *	 */
	function plgVmConfirmedOrder($cart, $order) {
		// TODO: Add record to the database to store # of downloads etc.
		// TODO
		
	}
	/**
	 * Create the database table for this plugin.
	 */
	public function getVmPluginCreateTableSQL() {
		return $this->createTableSQL('Downloads for Sale tracking');
	}
	function getTableSQLFields() {
		$SQLfields = array(
		  'id' => 'int(1) UNSIGNED NOT NULL AUTO_INCREMENT',
		  'virtuemart_customfield_id' => 'int(11) UNSIGNED NOT NULL DEFAULT 0',
		  'virtuemart_order_item_id' => 'int(11) UNSIGNED NULL DEFAULT 0',
		  'downloaded' => 'int(11) UNSIGNED NOT NULL DEFAULT 0',
		);
		return $SQLfields;
	}
}
// No closing tag