<?php

/**
 * 	CLASS: Share Your Cart Base
 * 	AUTHOR: Barandi Solutions
 * 	COUNTRY: Romania
 * 	EMAIL: catalin.paun@barandisolutions.ro
 * 	VERSION : 1.0
 * 	DESCRIPTION: This class is used as a base class for every PHP plugin we create.
 * *    Copyright (C) 2012 Barandi Solutions
 */
require_once(dirname(__FILE__) ."/class.shareyourcart-api.php");

if(!class_exists('ShareYourCartBase',false)){

abstract class ShareYourCartBase extends ShareYourCartAPI {

	//this array is used to hold function calls between different instances of this class
	private static $_SINGLE_FUNCTIONS_CALLS = array();
	private static $_SDK_VERSION = '1.6';  //the first one is the SDK main version, while the second one is it's revision
	protected static $_DB_VERSION = '1.1';
	
	/**
    * Constructor
    * @param null
    */
    function __construct() {
	
		//set exception handler
		if(set_exception_handler(array(&$this,'UncaughtExceptionHandler')) !== null)
			restore_exception_handler(); //if there allready was an exception handler, revert back to it
	
		parent::__construct();
		
		//now, add the api version to the button_js, in order to force users to download the latest
		//JS file
		$this->SHAREYOURCART_BUTTON_JS .= '?v='. $this->getConfigValue('api_version');
		$this->SHAREYOURCART_BUTTON_URL .= '?client_id='. $this->getClientId();
		
		//set the language
		SyC::setLanguage($this->getConfigValue('lang'));
	}
	
	/**
	 * Execute NonQuery SQL
	 * @param string action
	 * @param string extra
	 * @return boolean
	 */
	protected abstract function executeNonQuery($sql);

	/**
	 *
	 * Get the row returned from the SQL
	 *
	 * @return an associative array containing the data of the row OR NULL
	 *         if there is none
	 */
	protected abstract function getRow($sql);

	/**
	 * Abstract getTableName
	 * @param string key
	 *
	 */
	protected abstract function getTableName($key);

	/**
	 * Abstract setConfigValue
	 * @param string option
	 * @param string value
	 * @return boolean
	 */
	protected abstract function setConfigValue($field, $value);

	/**
	 * Abstract setConfigValue
	 * @param string option
	 * @param string value
	 * @return string
	 */
	protected abstract function getConfigValue($field);

	/**
	 * 	Description: This returns an array with the details of the product
	 *               displayed on this page. If it is not a product, it
	 *               will return FALSE
	 * @remarks  this will be mostly used by LITE integrations, so no need to make it mandatory
	 * @param null
	 * @return boolean / array
	 */
	protected function getCurrentProductDetails(){
		
		return FALSE;
	}
	
	/**
	*
	* Return TRUE if this page describes a single product, otherwise FALSE
	*
	*/
	protected abstract function isSingleProduct();

	/**
	 *
	 * Return the URL to be called when the button is pressed
	 *
	 */
	protected abstract function getButtonCallbackURL();

	/*
	 *
	 * Create url for the specified file. The file must be specified in relative path
	 * to the base of the plugin
	 */
	protected abstract function createUrl($file);

	/**
	 *
	 * Called after the session has been reloaded
	 *
	 */
	protected abstract function loadSessionData();

	/**
	 *
	 * Returns the plugin's secret key. This should be overwritten only by the actual plugin
	 *
	 */
	protected abstract function getSecretKey();

	/**
	 *
	 * Insert a row in the table
	 * @param string tableName
	 * @param array data
	 *
	 */
	protected abstract function insertRow($tableName, $data);

	/**
	 *
	 * Apply the coupon code
	 *
	 */
	protected abstract function applyCoupon($coupon_code);
	
	/**
	*
	* Get the plugin version.
	* @return an integer
	*
	*/
	protected abstract function getPluginVersion();

	/**
	*
	* Get the entire Version, including the SDK one
	*
	*/
	public function getVersion()
	{
		//make sure a developer enters only a number as a version
		if(!is_int($minor_version = $this->getPluginVersion()))
			throw new Exception(SyC::t('sdk','The Plugin Version must be an integer'));
		
		return self::$_SDK_VERSION.'.'.$minor_version;
	}
	
	/**
	*
	* Return TRUE if there is a newer version of the plugin
	*
	*/
	protected function hasNewerVersion()
	{
		return version_compare($this->getVersion(),$this->getConfigValue('latest_version'),'<');
	}
	
	/**
	*
	* Check if this instance can load, or not!
	* This is ment so that at ALL times, only the latest plugin version will work
	*
	*/
	protected function canLoad()
	{
		$v = version_compare($this->getVersion(),$this->getConfigValue('plugin_current_version')); 
		//first, check if this instance is the latest one, or not
		if($v < 0) return false;
			 
		//save the latest version, for later reference
		if($v > 0 )
			$this->setConfigValue('plugin_current_version',$this->getVersion());
			
		return true;
	}

	/**
	 * install the plugin
	 * @param null
	 * @return boolean
	 */
	public function install(&$message = null) {

		//this is a single call function
		if (!$this->isFirstCall(__FUNCTION__))
		return;

		//create the tokens table
		$this->createTable($this->getTableName('shareyourcart_tokens'), array(
            'id' => 'int(11)',
            'token' => 'varchar(255)',
            'session_id' => 'varchar(255)',
		), 'id');

		//create the coupon log table
		$this->createTable($this->getTableName('shareyourcart_coupons'), array(
            'id' => 'int(11)',
            'token' => 'varchar(255)',
            'coupon_id' => 'varchar(255)',
		), 'id');

		//save the DB version, for later use
		$this->setConfigValue('db_version', self::$_DB_VERSION);

		//if we have credentials in the DB, try to activate the plugin
		//with them
		$activated = false;
		$appKey = $this->getAppKey();
		$clientId = $this->getClientId();
		if(!empty($appKey) && !empty($clientId)){
		
			$activated = $this->activate($message);
		}

		return true;
	}

	/**
	 * uninstall the plugin
	 * @param null
	 * @return boolean
	 */
	public function uninstall(&$message = null) {

		//this is a single call function
		if (!$this->isFirstCall(__FUNCTION__))
		return;

		//first, make sure we deactivate the plugin
		$this->deactivate($message);

		//remove the tables
		$this->dropTable($this->getTableName('shareyourcart_tokens'));
		$this->dropTable($this->getTableName('shareyourcart_coupons'));

		//remove the db version
		$this->setConfigValue('db_version', null);

		return true;
	}

	/**
	 * activate the plugin
	 * @param null
	 * @return boolean
	 */
	public function activate(&$message = null) {

		//this is a single call function
		if (!$this->isFirstCall(__FUNCTION__))
		return;

		//active the API
		if ($this->setAccountStatus($this->getSecretKey(), $this->getClientID(), $this->getAppKey(), true, $message) === TRUE) {

			$this->setConfigValue("account_status", "active");
			return true;
		} else {

			$this->setConfigValue("account_status", "inactive");
			return false;
		}
	}

	/**
	 * deactivate the plugin
	 * @param null
	 * @return boolean
	 */
	public function deactivate(&$message = null) {

		//send the notification to the API
		$this->setAccountStatus($this->getSecretKey(), $this->getClientID(), $this->getAppKey(), false, $message);

		//no matter what the API says, disable this plugin
		$this->setConfigValue("account_status", "inactive");

		return true;
	}

	/**
	 * getAppKey
	 * @param null
	 * @return appKey
	 */
	public function getAppKey() {

		return $this->getConfigValue('appKey');
	}

	/**
	 * getClientID
	 * @param null
	 * @return clientID
	 */
	public function getClientId() {
		return $this->getConfigValue('clientId');
	}

	/**
	 * Check if the plugin is active, or not
	 * @param null
	 * @return boolean
	 */
	public function isActive() {

		return ($this->getConfigValue('account_status') == "active");
	}

	/**
	 * startSession
	 * @param string $params
	 * @return boolean
	 */
	public function startSession($params) {

		//make sure the params contain the required entries
		if (!isset($params['app_key']))
		$params['app_key'] = $this->getAppKey();

		if (!isset($params['client_id']))
		$params['client_id'] = $this->getClientId();

		//create a new session
		$data = parent::startSession($params);

		//save session details
		$this->insertRow($this->getTableName('shareyourcart_tokens'), $data);
		
		//we can't rely on the fact that the row has been inserted, so check!
		if($this->getSessionId($data['token']) === null)
			throw new Exception(SyC::t('sdk','Token cannot be saved. Check your "{table_name}" table permissions.', array('{table_name}' => $this->getTableName('shareyourcart_tokens'))));

		return true;
	}

	/**
	 * ensureCouponIsValid
	 * @param string $params
	 * @return boolean
	 */
	public function assertCouponIsValid($token, $coupon_code, $coupon_value, $coupon_type) {

		//first call the parent function
		parent::assertCouponIsValid($token, $coupon_code, $coupon_value, $coupon_type);

		//get the session_id associated with the token
		$session_id = $this->getSessionId($token);

		//make sure the session is valid
		if ($session_id === null) {
			throw new Exception(SyC::t('sdk','Token not found'));
		}

		//resume the session
		session_destroy();
		session_id($session_id);
		session_start();

		$this->loadSessionData();
	}

	/**
	 * simply show the button
	 * @param null
	 * @return boolean
	 */
	public function showButton() {

		echo $this->getButton();
	}

	/**
	 *
	 * get the button code
	 *
	 */
	public function getButton() {

		//make sure the API is active
		if(!$this->isActive()) return;

		return $this->renderButton($this->getButtonCallbackURL());
	}

	/**
	 * renderButton
	 * @param null
	 * @return boolean
	 */
	protected function renderButton($callback_url) {
		ob_start();

		$current_button_type = $this->getConfigValue("button_type");
		$button_html = $this->getConfigValue("button_html");
		
		$button_img = $this->getConfigValue("btn-img");
		$button_img_width = $this->getConfigValue("btn-img-width");
		$button_img_height = $this->getConfigValue("btn-img-height");
		
		$button_img_hover = $this->getConfigValue("btn-img-h");
		$button_img_hover_width = $this->getConfigValue("btn-img-h-width");
		$button_img_hover_height = $this->getConfigValue("btn-img-h-height");

		switch ($current_button_type)
		{
			case '1':
				include(dirname(__FILE__) . '/views/button.php');
				break;
			case '2':
				include(dirname(__FILE__) . '/views/button-img.php');
				break;
			case '3':
				include(dirname(__FILE__) . '/views/button-custom.php');
				break;
			default:
				include(dirname(__FILE__) . '/views/button.php');
				break;
		}


		return ob_get_clean();
	}

	/**
	 *
	 * show the button on a product page
	 *
	 */
	public function showProductButton() {

		echo $this->getProductButton();
	}

	/**
	 *
	 * get the button for a product page
	 *
	 */
	public function getProductButton() {

		if($this->isSingleProduct() && !$this->getConfigValue('hide_on_product')){

			return	$this->getButton();
		}

		//else return nothing
		return null;
	}

	/**
	 *
	 * show the button on a cart page
	 *
	 */
	public function showCartButton() {
		
		echo $this->getCartButton();
	}

	/**
	 *
	 * get the button for the cart page
	 *
	 */
	public function getCartButton() {

		if(!$this->getConfigValue('hide_on_checkout')){

			return $this->getButton();
		}

		//return nothing
		return null;
	}

	/**
	 *
	 * Simply show the page header
	 *
	 */
	public function showPageHeader() {

		echo $this->getPageHeader();
	}

	/**
	 * Get the page header code
	 * @param null
	 * @return boolean
	 */
	public function getPageHeader() {

		//this is a single call function
		if (!$this->isFirstCall(__FUNCTION__))
		return;

		$data = $this->getCurrentProductDetails();

		ob_start();
		include(dirname(__FILE__) . '/views/page-header.php');
		return ob_get_clean();
	}

	/**
	 * Show the admin header code
	 * @param null
	 * @return boolean
	 */
	public function showAdminHeader() {

		echo $this->getAdminHeader();
	}

	/**
	 * Get the admin header code
	 * @param null
	 * @return boolean
	 */
	public function getAdminHeader() {
			
		//this is a single call function
		if (!$this->isFirstCall(__FUNCTION__))
		return;
		
		//check the SDK status
		$this->checkSDKStatus(true); //for a check, as the admin might have changed the language in the configure page, so we need to sync with it

		ob_start();
		include(dirname(__FILE__) . '/views/admin-header.php');
		return ob_get_clean();
	}

	/**
	 * show the admin page
	 * @param null
	 * @return boolean
	 */
	public function showAdminPage($html='',$show_header=TRUE,$show_footer=TRUE) {

		echo $this->getAdminPage($html,$show_header,$show_footer);
	}

	/**
	 *
	 * get the admin page
	 *
	 */
	public function getAdminPage($html='',$show_header=TRUE,$show_footer=TRUE) {

		//this is a single call function
		if (!$this->isFirstCall(__FUNCTION__))
		return;

		$status_message = '';
		$refresh = false;
		
		//check if this is a post for this particular page
		if ($_SERVER['REQUEST_METHOD'] == 'POST' &&
		!empty($_POST['syc-account-form'])) {

			$this->setConfigValue('appKey', $_POST['app_key']);
			$this->setConfigValue('clientId', $_POST['client_id']);

			//it is vital that we call the activation API here, to make sure, that the account is ACTIVE
			//call the account status function
			$message = '';
			if ($this->activate($message) == true) {

				$status_message = SyC::t('sdk','Account settings successfully saved');
			} else {

				//the account did not activate, so show the error
				$status_message = $message;
			}
			
			//since we might have changed the status, REFRESH
			$refresh = true;
		}
		//the user decided to disable the API
		else if ($_SERVER['REQUEST_METHOD'] == 'POST' &&
		!empty($_POST['disable-API'])){
			
			$this->deactivate($status_message);
			
			//since we might have changed the status, REFRESH
			$refresh = true;
		}
		//the user decided to activate the API
		else if ($_SERVER['REQUEST_METHOD'] == 'POST' &&
		!empty($_POST['enable-API'])){
			
			$this->activate($status_message);
			
			//since we might have changed the status, REFRESH
			$refresh = true;
		}  
		//check if the user want's to recover his account
		else if (@$_REQUEST['syc-account'] === 'recover'){

			//by default, show the form if we are here
			$show_form = true;
			if($_SERVER['REQUEST_METHOD'] == 'POST' &&
			!empty($_POST['syc-recover-account']))
			{
				//try to recover if the user posted the form
				$show_form = !$this->recover($this->getSecretKey(), @$_REQUEST['domain'], @$_REQUEST['email'], $status_message);
			}
			
			//if we need to show the form
			if($show_form)
			{
				//if there is a message, put the form on a new line
				if(!empty($status_message))
					$status_message .= "<br /><br />";
				
				ob_start();
				include(dirname(__FILE__) . '/views/account-recover-partial.php');
				$status_message .= ob_get_clean();
			} 
			else 
			{
				//Refresh in order to get rid of the GET parameter
				$refresh = true;
			}
		}
		else if (@$_REQUEST['syc-account'] === 'create'){
			
			//by default, show the form if we are here
			$show_form = true;
			if($_SERVER['REQUEST_METHOD'] == 'POST' &&
			!empty($_POST['syc-create-account']))
			{
				//first, check if the user has agreed to the terms and conditions
				if(isset($_POST['syc-terms-agreement']))
				{
					//try to create the account if the user posted the form
					if (!(($register = $this->register($this->getSecretKey(), @$_REQUEST['domain'], @$_REQUEST['email'], $status_message)) === false)) 
					{
						$this->setConfigValue('appKey', @$register['app_key']);
						$this->setConfigValue('clientId', @$register['client_id']);
                        
						$this->setConfigValue("account_status", "active");
						$show_form = false; //no need to show the register form anymore
						
						//since we might have changed the status, REFRESH
						$refresh = true;
					}
				}
				else
				{
					$status_message = SyC::t('sdk',"Error. You must agree with the terms and conditions bellow");
				}
			}
			
			//if we need to show the form
			if($show_form)
			{
				//if there is a message, put the form on a new line
				if(!empty($status_message))
					$status_message .= "<br /><br />";
				
				ob_start();
				include(dirname(__FILE__) . '/views/account-create-partial.php');
				$status_message .= ob_get_clean();
			}
		}
		
		//make sure there is a session variable setup
		session_start();
		
		//since switching the API status has a great impact on how the UI looks, refresh the page
		//just to make sure the UI is using the latest value
		if($refresh)
		{
			//first, save the status message
			$_SESSION['_syc_status_message'] = $status_message;
			
			//recreate the url ( but before that make sure there is no syc-account parameter in it )
			unset($_GET['syc-account']);
			$url = '?'.http_build_query($_GET,'','&');
			
			@header("HTTP/1.1 302 Found");
			@header("Location: $url");
			echo "<meta http-equiv=\"refresh\" content=\"0; url=$url\">"; //it can happen that the headers have allready been sent, so use the html version as well
			exit;
		}
		
		//if there is a status message
		if(!empty($_SESSION['_syc_status_message']))
		{
			$status_message = $_SESSION['_syc_status_message'];
			unset($_SESSION['_syc_status_message']);
		}

		// Display the view
		ob_start();
		include(dirname(__FILE__) . '/views/admin-page.php');
		return ob_get_clean();
	}

	/**
	 *
	 * show page to customize the button
	 *
	 */
	public function showButtonCustomizationPage($html='',$show_header=TRUE,$show_footer=TRUE) {

		echo $this->getButtonCustomizationPage($html,$show_header,$show_footer);
	}

	/**
	 *
	 * get the page to customize the button
	 *
	 */
	public function getButtonCustomizationPage($html='',$show_header=TRUE,$show_footer=TRUE){
		//if visual settings are submitted
		if ($_SERVER['REQUEST_METHOD'] == 'POST' &&
		!empty($_POST['syc-visual-form'])) {

			//set the button declaration
			$this->setConfigValue("button_type", $_POST['button_type']);

			//set the button skin
			$this->setConfigValue("button_skin", $_POST['button_skin']);

			//set the button position
			$this->setConfigValue("button_position", $_POST['button_position']);
			
			//set the button height
			$this->setConfigValue("dont_set_height", empty($_POST['show_on_single_row']));

			//set the button html
			$this->setConfigValue("button_html", rawurldecode($_POST['button_html']));

			//set the show
			$this->setConfigValue("hide_on_product", empty($_POST['show_on_product']));

			//set the show'
			$this->setConfigValue("hide_on_checkout", empty($_POST['show_on_checkout']));

			if($_FILES["button-img"]["name"]!='') {

				$target_path = dirname(__FILE__). "/img/";

				$target_path = $target_path . 'button-img.png';

				if(file_exists($target_path)) unlink($target_path);
				
				list($width, $height, $type, $attr) = getimagesize($_FILES['button-img']['tmp_name']);
				
				if (move_uploaded_file($_FILES['button-img']['tmp_name'], $target_path))
				{
					//set the button img
					$this->setConfigValue("btn-img", $this->createUrl($target_path));
					$this->setConfigValue("btn-img-width", $width);
					$this->setConfigValue("btn-img-height", $height);
				}
			}

			if($_FILES["button-img-hover"]["name"]!='') {
				$target_path = dirname(__FILE__). "/img/";

				$target_path = $target_path . 'btn-img-hover.png';

				if(file_exists($target_path)) unlink($target_path);
				
				list($width, $height, $type, $attr) = getimagesize($_FILES['button-img-hover']['tmp_name']);

				if(move_uploaded_file($_FILES['button-img-hover']['tmp_name'], $target_path))
				{
					//set the show'
					$this->setConfigValue("btn-img-h", $this->createUrl($target_path));
					$this->setConfigValue("btn-img-h-width", $width);
					$this->setConfigValue("btn-img-h-height", $height);
				}
			}

			$status_message = SyC::t('sdk','Button settings successfully updated.');
		}

		$current_button_type = $this->getConfigValue("button_type");
		$current_skin = $this->getConfigValue("button_skin");
		$current_position = $this->getConfigValue("button_position");
		$show_on_checkout = !$this->getConfigValue("hide_on_checkout");
		$show_on_product = !$this->getConfigValue("hide_on_product");
		$show_on_single_row = !$this->getConfigValue("dont_set_height");

		$button_html = $this->getConfigValue("button_html");
		$button_img = $this->getConfigValue("btn-img");
		$button_img_hover = $this->getConfigValue("btn-img-h");

		//render the view
		ob_start();
		include(dirname(__FILE__) . '/views/button-settings-page.php');
		return ob_get_clean();
	}

	/**
	 * showDocumentation
	 * @param null
	 * @return boolean
	 */
	public function showDocumentationPage($html='',$show_header=TRUE,$show_footer=TRUE) {

		echo $this->getDocumentationPage($html,$show_header,$show_footer);
	}

	/**
	 * get the documentation page
	 * @param null
	 * @return boolean
	 */
	public function getDocumentationPage($html='',$show_header=TRUE,$show_footer=TRUE) {

		//this is a single call function
		if (!$this->isFirstCall(__FUNCTION__))
		return;
			
		$action_url = $this->getButtonCallbackURL();

		//render the view
		ob_start();
		include(dirname(__FILE__) . '/views/documentation.php');
		return ob_get_clean();
	}
	
	/**
	 * show the update notification
	 * @param null
	 */
	public function showUpdateNotification(){
		echo $this->getUpdateNotification();
	}
	
	/**
	 * get the update notification
	 * @param null
	 */
	public function getUpdateNotification(){
		//render the view
		ob_start();
		include(dirname(__FILE__) . '/views/update-notification-partial.php');
		return ob_get_clean();
	}

	/*
	 *
	 * Called when a new coupon is generated
	 *
	 */

	public function couponCallback() {

		try {
			/*             * ********* Check input parameters ******************************* */
			if (!isset($_POST['token'], $_POST['coupon_code'], $_POST['coupon_value'], $_POST['coupon_type'])) {
				throw new Exception(SyC::t('sdk','At least one of the parameters is missing. Received: {data}', array('{data}' => print_r($_POST, true))));
			}

			//make sure the coupon is valid
			$this->assertCouponIsValid($_POST['token'], $_POST['coupon_code'], $_POST['coupon_value'], $_POST['coupon_type']);

			//save the coupon
			$this->saveCoupon($_POST['token'], $_POST['coupon_code'], $_POST['coupon_value'], $_POST['coupon_type']);

			//check if the coupon is intended to be applied to the current cart
			if (empty($_POST['save_only'])) {
				$this->applyCoupon($_POST['coupon_code']);
			}
		} catch (Exception $e) {

			header("HTTP/1.0 403");
			echo $e->getMessage();
		}
	}

	/**
	 * Save the coupon
	 * @param null
	 * @return boolean
	 */
	protected function saveCoupon($token, $coupon_code, $coupon_value, $coupon_type) {

		//add the coupon id in shareyourcart coupons table
		$data = array(
            'token' => $token,
            'coupon_id' => $coupon_code,
		);

		$this->insertRow($this->getTableName('shareyourcart_coupons'), $data);
	}

	/**
	 *
	 * Based on the token, get the session_id
	 *
	 */
	protected function getSessionId($token) {

		$result = $this->getRow("SELECT session_id FROM " . $this->getTableName('shareyourcart_tokens') . " WHERE token='$token'");

		return isset($result) ? $result['session_id'] : null;
	}

	/**
	 *
	 * createTable
	 * @param table
	 * @param array columns
	 * @param string @option
	 */
	protected function createTable($tableName, $columns, $primaryKey, $options=NULL) {

		$sql = "CREATE TABLE IF NOT EXISTS $tableName (\n ";

		foreach ($columns as $name => $type) {

			$sql .= "$name $type";

			if ($name == $primaryKey) {
				$sql .= " NOT NULL AUTO_INCREMENT";
			}

			$sql .= ",\n ";
		}

		$sql .= "PRIMARY KEY ($primaryKey));";

		$this->executeNonQuery($sql);
		
		//we can't relly on the fact that the table has been properly created, so check it!
		if(!$this->existsTable($tableName))
			throw new Exception(SyC::t('sdk','Cannot create table "{table_name}". Check your database permissions.', array('{table_name}' => $tableName)));
	}
	
	/**
	*
	* existsTable
	* @return TRUE if the table exists, otherwise false
	*/
	protected function existsTable($tableName){
	
		$table_details = $this->getRow("show tables like '$tableName'");
		
		//if there are table details, it means the table exists
		return !empty($table_details);
	}

	/**
	 * Drop the specified table
	 *
	 * @param string tableName
	 */
	protected function dropTable($tableName) {

		$this->executeNonQuery("DROP TABLE $tableName");
		
		//we can't relly on the fact that the table has been properly droped, so check it!
		if(!$this->existsTable($tableName))
			throw new Exception(SyC::t('sdk','Cannot drop table "{table_name}". Check your database permissions.', array('{table_name}' => $tableName)));
	}

	/**
	*
	* Check the SDK status
	* @param $force. TRUE to check, no matter how soon the previous check was. This can have a great impact on the experience of the admin
	*
	*/
	protected function checkSDKStatus($force = false) {
		
		//call the API at most only once every 5 minutes, not sooner
		if(!$force && (time()-$this->getConfigValue('api_last_check')) < 5*60) 
			return;
		
		//set the latest check time
		$this->setConfigValue('api_last_check',time());
		
		//get an update from the API
		$message = '';
		if(is_array($result = $this->getSDKStatus($this->getSecretKey(), $this->getClientId(), $this->getAppKey(), $message)))
		{
			//save the data
			$this->setConfigValue('api_version', @$result['api_version']);
			$this->setConfigValue('latest_version', @$result['plugin_latest_version']);
			$this->setConfigValue('download_url', @$result['plugin_download_url']);
			
			//set the current language the SDK should be displayed in
			if(isset($result['lang'])){
				$this->setConfigValue('lang', $result['lang']);
				SyC::setLanguage($result['lang']);
			}
		}else{
			//simply log the error, for now!
			error_log(print_r($message,true));
		}	
	}
	
	/*
	 *
	 * Call to see if the function was called once, or not
	 *
	 */
	protected static function isFirstCall($functionName) {

		if (array_key_exists($functionName, self::$_SINGLE_FUNCTIONS_CALLS))
		return false;

		self::$_SINGLE_FUNCTIONS_CALLS[$functionName] = true;
		return true;
	}
	
	/**
	*
	* User to catch any unhandled exceptions and print them nicelly
	*
	*/
	public function UncaughtExceptionHandler(Exception $e) {
		//@header("HTTP/1.0 403");
		echo $e->getMessage();
	}
}

} //END IF
?>