<?php

/***************************************************************************
*                            Dolphin Smart Community Builder
*                              -----------------
*     begin                : Mon Mar 23 2006
*     copyright            : (C) 2006 BoonEx Group
*     website              : http://www.boonex.com/
* This file is part of Dolphin - Smart Community Builder
*
* Dolphin is free software. This work is licensed under a Creative Commons Attribution 3.0 License. 
* http://creativecommons.org/licenses/by/3.0/
*
* Dolphin is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Creative Commons Attribution 3.0 License for more details. 
* You should have received a copy of the Creative Commons Attribution 3.0 License along with Dolphin, 
* see license.txt file; if not, write to marketing@boonex.com
***************************************************************************/


require_once( 'header.inc.php' );
require_once( BX_DIRECTORY_PATH_INC . 'db.inc.php' );
require_once( BX_DIRECTORY_PATH_INC . 'languages.inc.php' );
require_once( BX_DIRECTORY_PATH_INC . 'profiles.inc.php' );

//MESSAGE CONSTANTS PASSED TO _t_ext() FUNCTION BY checkAction()
//NOTE: checkAction() RETURNS LANGUAGE DEPENDANT MESSAGES

define('CHECK_ACTION_MESSAGE_NOT_ALLOWED',			"_ACTION_NOT_ALLOWED");
define('CHECK_ACTION_MESSAGE_NOT_ACTIVE',			"_ACTION_NOT_ACTIVE");
define('CHECK_ACTION_MESSAGE_LIMIT_REACHED',		"_ACTION_LIMIT_REACHED");
define('CHECK_ACTION_MESSAGE_MESSAGE_EVERY_PERIOD',	"_ACTION_EVERY_PERIOD");
define('CHECK_ACTION_MESSAGE_NOT_ALLOWED_BEFORE',	"_ACTION_NOT_ALLOWED_BEFORE");
define('CHECK_ACTION_MESSAGE_NOT_ALLOWED_AFTER',	"_ACTION_NOT_ALLOWED_AFTER");

//NODES OF THE $args ARRAY THAT IS PASSED TO THE _t_ext() FUNCTION BY checkAction()

define('CHECK_ACTION_LANG_FILE_ACTION',		1);
define('CHECK_ACTION_LANG_FILE_MEMBERSHIP',	2);
define('CHECK_ACTION_LANG_FILE_LIMIT',		3);
define('CHECK_ACTION_LANG_FILE_PERIOD',		4);
define('CHECK_ACTION_LANG_FILE_AFTER',		5);
define('CHECK_ACTION_LANG_FILE_BEFORE',		6);
define('CHECK_ACTION_LANG_FILE_SITE_EMAIL',	7);

//ACTION ID's

define('ACTION_ID_SEND_VKISS',		 1);
define('ACTION_ID_VIEW_PROFILES',	 2);
define('ACTION_ID_VOTE',			 3);
define('ACTION_ID_SEND_MESSAGE',	 4);
define('ACTION_ID_GET_EMAIL',		 5);
define('ACTION_ID_COMMENTS_POST', 6);
define('ACTION_ID_COMMENTS_VOTE', 7);
define('ACTION_ID_COMMENTS_EDIT_OWN', 8);
define('ACTION_ID_COMMENTS_REMOVE_OWN', 9);

//PREDEFINED MEMBERSHIP ID's

define('MEMBERSHIP_ID_NON_MEMBER', 1);
define('MEMBERSHIP_ID_STANDARD', 2);
define('MEMBERSHIP_ID_PROMOTION', 3);

//INDICES FOR checkAction() RESULT ARRAY

define('CHECK_ACTION_RESULT', 0);
define('CHECK_ACTION_MESSAGE', 1);
define('CHECK_ACTION_PARAMETER', 3);

//CHECK_ACTION_RESULT NODE VALUES

define('CHECK_ACTION_RESULT_ALLOWED',				0);
define('CHECK_ACTION_RESULT_NOT_ALLOWED',			1);
define('CHECK_ACTION_RESULT_NOT_ACTIVE',			2);
define('CHECK_ACTION_RESULT_LIMIT_REACHED',			3);
define('CHECK_ACTION_RESULT_NOT_ALLOWED_BEFORE',	4);
define('CHECK_ACTION_RESULT_NOT_ALLOWED_AFTER',		5);

/**
 * Returns number of members with a given membership at a given time
 *
 * @param int $iMembershipId		- members of what membership should be counted.
 * 								  if 0, then all members are counted ($except is not considered);
 * 								  if MEMBERSHIP_ID_NON_MEMBER is specified, function returns -1
 *
 * @param unix_timestamp $time	- date/time to use when counting members.
 * 								  if not specified, uses the present moment
 * @param boolean $except		- if true, counts all members that DON'T have specified membership
 *
 *
 */
/*function getMembersCount($iMembershipId = 0, $time = '', $except = false)  //'A' just old unused function
{
	$iMembershipId = (int)$iMembershipId;
	$time = ($time == '') ? time() : (int)$time;
	$except = $except ? true : false;

	if($iMembershipId == MEMBERSHIP_ID_NON_MEMBER || $iMembershipId < 0) return -1;

	$resProfiles = db_res("SELECT COUNT(*) FROM Profiles");

	$totalProfiles = mysql_fetch_row($resProfiles);
	$totalProfiles = (int)$totalProfiles[0];

	if($iMembershipId == 0) return $totalProfiles;

	$queryWhereMembership = '';

	if($iMembershipId != MEMBERSHIP_ID_STANDARD) $queryWhereMembership = "IDLevel = $iMembershipId AND";

	$query = "
		SELECT	COUNT(DISTINCT IDMember)
		FROM	`sys_acl_levels_members`
		WHERE	$queryWhereMembership
				(DateExpires IS NULL OR UNIX_TIMESTAMP(DateExpires) > $time) AND
				(DateStarts IS NULL OR UNIX_TIMESTAMP(DateStarts) <= $time)";

	$resProfileMemLevels = db_res($query);

	$membershipProfiles = mysql_fetch_row($resProfileMemLevels);
	$membershipProfiles = (int)$membershipProfiles[0];

	if($iMembershipId == MEMBERSHIP_ID_STANDARD) $membershipProfiles = $totalProfiles - $membershipProfiles;

	if($except) $membershipProfiles = $totalProfiles - $membershipProfiles;

	return $membershipProfiles;
}*/

/**
 * This is an internal function - do NOT use it outside of membership_levels.inc.php!
 */
function getMemberMembershipInfo_current($iMemberId, $time = '') {
	$iMemberId = (int)$iMemberId;
	$time = ($time == '') ? time() : (int)$time;

	/**
	 * Fetch the last purchased/assigned membership that is still active for the given member.
	 * NOTE. Don't use cache here, because it's causing an error, if a number of memberrship levels are purchased at the same time.
	 * fromMemory returns the same DateExpires because buyMembership function is called in cycle in the same session.
	 */
	$aMemLevel =& $GLOBALS['MySQL']->getRow("
        SELECT  `sys_acl_levels_members`.IDLevel as ID,
                `sys_acl_levels`.Name as Name,
                UNIX_TIMESTAMP(`sys_acl_levels_members`.DateStarts) as DateStarts,
                UNIX_TIMESTAMP(`sys_acl_levels_members`.DateExpires) as DateExpires,
                `sys_acl_levels_members`.`TransactionID` AS `TransactionID`
        FROM    `sys_acl_levels_members`
                RIGHT JOIN Profiles
                ON `sys_acl_levels_members`.IDMember = Profiles.ID
                    AND (`sys_acl_levels_members`.DateStarts IS NULL
                        OR `sys_acl_levels_members`.DateStarts <= FROM_UNIXTIME($time))
                    AND (`sys_acl_levels_members`.DateExpires IS NULL
                        OR `sys_acl_levels_members`.DateExpires > FROM_UNIXTIME($time))
                LEFT JOIN `sys_acl_levels`
                ON `sys_acl_levels_members`.IDLevel = `sys_acl_levels`.ID

        WHERE   Profiles.ID = $iMemberId

        ORDER BY `sys_acl_levels_members`.DateStarts DESC

        LIMIT 0, 1");

	/**
	 * no such member found
	 */
    if (!$aMemLevel || !count($aMemLevel)) {
		//fetch info about Non-member membership
        $aMemLevel =& $GLOBALS['MySQL']->fromCache('sys_acl_levels' . MEMBERSHIP_ID_NON_MEMBER, 'getRow', "SELECT ID, Name FROM `sys_acl_levels` WHERE ID = ".MEMBERSHIP_ID_NON_MEMBER);
        if(!$aMemLevel || !count($aMemLevel)) {
			//this should never happen, but just in case
			echo "<br /><b>getMemberMembershipInfo()</b> fatal error: <b>Non-Member</b> membership not found.";
			exit();
		}
		return $aMemLevel;
	}

	/**
	 * no purchased/assigned memberships for the member or all of them have expired -- the member is assumed to have Standard membership
	 */
	if(is_null($aMemLevel['ID'])) {
        $aMemLevel =& $GLOBALS['MySQL']->fromCache('sys_acl_levels' . MEMBERSHIP_ID_STANDARD, 'getRow', "SELECT ID, Name FROM `sys_acl_levels` WHERE ID = ".MEMBERSHIP_ID_STANDARD);
        if (!$aMemLevel || !count($aMemLevel)) {
			//again, this should never happen, but just in case
			echo "<br /><b>getMemberMembershipInfo()</b> fatal error: <b>Standard</b> membership not found.";
			exit();
		}
	}

	return $aMemLevel;
}

/**
 * This is an internal function - do NOT use it outside of membership_levels.inc.php!
 */
function getMemberMembershipInfo_latest($iMemberId, $iTime = '') {
    $iTime = $iTime == '' ? time() : (int)$iTime;

    $aMembershipCurrent = getMemberMembershipInfo_current($iMemberId, $iTime);
    if(in_array($aMembershipCurrent['ID'], array(MEMBERSHIP_ID_STANDARD, MEMBERSHIP_ID_NON_MEMBER)))
        return $aMembershipCurrent;

    $aMembership = $aMembershipCurrent;
    while($aMembership['ID'] != MEMBERSHIP_ID_STANDARD) {
        $aMembershipLast = $aMembership;
        if((int)$aMembership['DateExpires'] == 0)
            break;

        $aMembership = getMemberMembershipInfo_current($iMemberId, $aMembership['DateExpires']);
    }

    return $aMembershipLast;
}


/**
 * Retrieves information about membership for a given member at a given moment.
 *
 * If there are no memberships purchased/assigned to the
 * given member or all of them have expired at the given point,
 * the member is assumed to be a standard member, and the function
 * returns	information about the Standard membership. This will
 * also happen if a member wasnt actually registered in the database
 * at that point - the function will still return info about Standard
 * membership, not the Non-member one.
 *
 * If there is no profile with the given $iMemberId,
 * the function returns information about the Non-member
 * predefined membership.
 *
 * The Standard and Non-member memberships have their
 * DateStarts and DateExpires attributes set to NULL.
 *
 * @param int $iMemberId	- ID of a member to get info about
 * @param int $time		- specifies the time to use when determining membership;
 * 						  if not specified, the function takes the current time
 *
 * @return array(	'ID'			=> membership id,
 * 					'Name'			=> membership name,
 * 					'DateStarts'	=> (UNIX timestamp) date/time purchased,
 * 					'DateExpires'	=> (UNIX timestamp) date/time expires )
 *
 */
function getMemberMembershipInfo($iMemberId, $iTime = '') {
	$iTime = ($iTime == '') ? time() : (int)$iTime;

	$aMembershipCurrent = getMemberMembershipInfo_current($iMemberId, $iTime);
	if(in_array($aMembershipCurrent['ID'], array(MEMBERSHIP_ID_STANDARD, MEMBERSHIP_ID_NON_MEMBER)))
		return $aMembershipCurrent;

	$aMembership = $aMembershipCurrent;
	do {
		$iDateStarts = $aMembership['DateStarts'];
		$aMembership = getMemberMembershipInfo_current($iMemberId, ((int)$iDateStarts < 1 ? 0 : $iDateStarts - 1));
	}
	while($aMembership['ID'] == $aMembershipCurrent['ID'] && (int)$aMembership['DateStarts']);

	$aMembership = $aMembershipCurrent;
	do {
		$iDateExpires = $aMembership['DateExpires'];
		$aMembership = getMemberMembershipInfo_current($iMemberId, $iDateExpires);
	}
	while($aMembership['ID'] == $aMembershipCurrent['ID'] && (int)$aMembership['DateExpires']);

	$aMembershipCurrent['DateStarts'] = $iDateStarts;
	$aMembershipCurrent['DateExpires'] = $iDateExpires;

	return $aMembershipCurrent;
}

/**
 * Checks if a given action is allowed for a given member and updates action information if the
 * action is performed.
 *
 * @param int $iMemberId			- ID of a member that is going to perform an action
 * @param int $actionID			- ID of the action itself
 * @param boolean $performAction	- if true, then action information is updated, i.e. action
 * 								  is 'performed'
 *
 * @return array(	CHECK_ACTION_RESULT => CHECK_ACTION_RESULT_ constant,
 * 					CHECK_ACTION_MESSAGE => CHECK_ACTION_MESSAGE_ constant,
 * 					CHECK_ACTION_PARAMETER => additional action parameter (string) )
 *
 *
 * NOTES:
 *
 * $result[CHECK_ACTION_MESSAGE] contains a message with detailed information about the result,
 * already processed by the language file
 *
 * if $result[CHECK_ACTION_RESULT] == CHECK_ACTION_RESULT_ALLOWED then this node contains
 * an empty string
 *
 * The error messages themselves are stored in the language file. Additional variables are
 * passed to the languages.inc.php function _t_ext() as an array and can be used there in the form of
 * {0}, {1}, {2} ...
 *
 * Additional variables passed to the lang. file on errors (can be used in error messages):
 *
 * 	For all errors:
 *
 * 		$arg0[CHECK_ACTION_LANG_FILE_ACTION]	= name of the action
 * 		$arg0[CHECK_ACTION_LANG_FILE_MEMBERSHIP]= name of the current membership
 *
 * 	CHECK_ACTION_RESULT_LIMIT_REACHED:
 *
 * 		$arg0[CHECK_ACTION_LANG_FILE_LIMIT]		= limit on number of actions allowed for the member
 * 		$arg0[CHECK_ACTION_LANG_FILE_PERIOD]	= period that the limit is set for (in hours, 0 if unlimited)
 *
 * 	CHECK_ACTION_RESULT_NOT_ALLOWED_BEFORE:
 *
 * 		$arg0[CHECK_ACTION_LANG_FILE_BEFORE]	= date/time since when the action is allowed
 *
 * 	CHECK_ACTION_RESULT_NOT_ALLOWED_AFTER:
 *
 * 		$arg0[CHECK_ACTION_LANG_FILE_AFTER]		= date/time since when the action is not allowed
 *
 * $result[CHECK_ACTION_PARAMETER] contains an additional parameter that can be considered
 * when performing the action (like the number of profiles to show in search result)
*/
function checkAction($iMemberId, $actionID, $performAction = false, $iForcedProfID = 0, $isCheckMemberStatus = true)
{
	global $logged;
	global $site;

	//output array initialization

	$result = array();
	$arrLangFileParams = array();

	$dateFormat = "F j, Y, g:i a";	//used when displaying error messages

	//input validation

	$iMemberId = (int)$iMemberId;
	$actionID = (int)$actionID;
	$performAction = $performAction ? true : false;

	//get current member's membership information

	$arrMembership = getMemberMembershipInfo($iMemberId);

	$arrLangFileParams[CHECK_ACTION_LANG_FILE_MEMBERSHIP] = $arrMembership['Name'];
	$arrLangFileParams[CHECK_ACTION_LANG_FILE_SITE_EMAIL] = $site['email'];

	//profile active check

	if($arrMembership['ID'] != MEMBERSHIP_ID_NON_MEMBER || $logged['admin'] || $logged['moderator'])
	{
		$iDestID = $iMemberId;
        if ( (isAdmin() || isModerator()) && $iForcedProfID>0) {		
			$iDestID = $iForcedProfID;
			$performAction = false;
		}

        if ($isCheckMemberStatus) {
            $active = getProfileInfo( $iDestID );
            if ($active['Status'] != 'Active') {
                $result[CHECK_ACTION_RESULT] = CHECK_ACTION_RESULT_NOT_ACTIVE;
                $result[CHECK_ACTION_MESSAGE] = _t_ext(CHECK_ACTION_MESSAGE_NOT_ACTIVE, $arrLangFileParams);
                return $result;
            }
        }
	}

	//get permissions for the current action

	$resMembershipAction = db_res("
		SELECT	Name,
				IDAction,
				AllowedCount,
				AllowedPeriodLen,
				UNIX_TIMESTAMP(AllowedPeriodStart) as AllowedPeriodStart,
				UNIX_TIMESTAMP(AllowedPeriodEnd) as AllowedPeriodEnd,
				AdditionalParamValue
		FROM	`sys_acl_actions`
				LEFT JOIN `sys_acl_matrix`
				ON	`sys_acl_matrix`.IDAction = `sys_acl_actions`.ID
					AND `sys_acl_matrix`.IDLevel = {$arrMembership['ID']}
		WHERE	`sys_acl_actions`.ID = $actionID");

	//no such action

	if(mysql_num_rows($resMembershipAction) < 1)
	{
		echo "<br /><b>checkAction()</b> fatal error. Unknown action ID: $actionID<br />";
		exit();
	}

	$arrAction = mysql_fetch_assoc($resMembershipAction);

	$result[CHECK_ACTION_PARAMETER]	= $arrAction['AdditionalParamValue'];
	$arrLangFileParams[CHECK_ACTION_LANG_FILE_ACTION] = _t('_'.$arrAction['Name']);

	//action is not allowed for the current membership

	if(is_null($arrAction['IDAction']))
	{
		$result[CHECK_ACTION_RESULT] = CHECK_ACTION_RESULT_NOT_ALLOWED;
		$result[CHECK_ACTION_MESSAGE] = _t_ext(CHECK_ACTION_MESSAGE_NOT_ALLOWED, $arrLangFileParams);
		return $result;
	}

	//Check fixed period limitations if present (also for non-members)

	if($arrAction['AllowedPeriodStart'] && time() < $arrAction['AllowedPeriodStart'])
	{

		$arrLangFileParams[CHECK_ACTION_LANG_FILE_BEFORE] = date($dateFormat, $arrAction['AllowedPeriodStart']);

		$result[CHECK_ACTION_RESULT] = CHECK_ACTION_RESULT_NOT_ALLOWED_BEFORE;
		$result[CHECK_ACTION_MESSAGE] = _t_ext(CHECK_ACTION_MESSAGE_NOT_ALLOWED_BEFORE, $arrLangFileParams);

		return $result;
	}

	if($arrAction['AllowedPeriodEnd'] && time() > $arrAction['AllowedPeriodEnd'])
	{

		$arrLangFileParams[CHECK_ACTION_LANG_FILE_AFTER] = date($dateFormat, $arrAction['AllowedPeriodEnd']);

		$result[CHECK_ACTION_RESULT] = CHECK_ACTION_RESULT_NOT_ALLOWED_AFTER;
		$result[CHECK_ACTION_MESSAGE] = _t_ext(CHECK_ACTION_MESSAGE_NOT_ALLOWED_AFTER, $arrLangFileParams);

		return $result;
	}

	//if non-member, allow action without performing further checks

	if ($arrMembership['ID'] == MEMBERSHIP_ID_NON_MEMBER)
	{
		$result[CHECK_ACTION_RESULT] = CHECK_ACTION_RESULT_ALLOWED;
		return $result;
	}

	//check other limitations (for members only)

	$allowedCnt = (int)$arrAction['AllowedCount'];		//number of allowed actions
														//if not specified or 0, number of
														//actions is unlimited

	$periodLen = (int)$arrAction['AllowedPeriodLen'];	//period for AllowedCount in hours
														//if not specified, AllowedCount is
														//treated as total number of actions
														//permitted

	//number of actions is limited

	if($allowedCnt > 0)
	{
		//get current action info for the member

		$actionTrack = db_res("SELECT ActionsLeft,
									  UNIX_TIMESTAMP(ValidSince) as ValidSince
							   FROM `sys_acl_actions_track`
							   WHERE IDAction = $actionID AND IDMember = $iMemberId");

		$actionsLeft = $performAction ? $allowedCnt - 1 : $allowedCnt;
		$validSince = time();

		//member is requesting/performing this action for the first time,
		//and there is no corresponding record in sys_acl_actions_track table

		if(mysql_num_rows($actionTrack) <= 0)
		{
			//add action to sys_acl_actions_track table

			db_res("
				INSERT INTO `sys_acl_actions_track` (IDAction, IDMember, ActionsLeft, ValidSince)
				VALUES ($actionID, $iMemberId, $actionsLeft, FROM_UNIXTIME($validSince))");

			$result[CHECK_ACTION_RESULT] = CHECK_ACTION_RESULT_ALLOWED;
			return $result;
		}

		//action has been requested/performed at least once at this point
		//and there is a corresponding record in sys_acl_actions_track table

		$actionTrack = mysql_fetch_assoc($actionTrack);

		//action record in sys_acl_actions_track table is out of date

		$periodEnd = (int)$actionTrack['ValidSince'] + $periodLen * 3600; //ValidSince is in seconds, PeriodLen is in hours

		if($periodLen > 0 && $periodEnd < time())
		{
			db_res("
				UPDATE	`sys_acl_actions_track`
				SET		ActionsLeft = $actionsLeft, ValidSince = FROM_UNIXTIME($validSince)
				WHERE	IDAction = $actionID AND IDMember = $iMemberId");

			$result[CHECK_ACTION_RESULT] = CHECK_ACTION_RESULT_ALLOWED;
			return $result;
		}

		//action record is up to date

		$actionsLeft = (int)$actionTrack['ActionsLeft'];

		//action limit reached for now

		if($actionsLeft <= 0 )
		{
			$arrLangFileParams[CHECK_ACTION_LANG_FILE_LIMIT] = $allowedCnt;
			$arrLangFileParams[CHECK_ACTION_LANG_FILE_PERIOD] = $periodLen;

			$result[CHECK_ACTION_RESULT] = CHECK_ACTION_RESULT_LIMIT_REACHED;
			$result[CHECK_ACTION_MESSAGE] = '<div style="width: 80%">' .
				_t_ext(CHECK_ACTION_MESSAGE_LIMIT_REACHED, $arrLangFileParams) .
				($periodLen > 0 ? _t_ext(CHECK_ACTION_MESSAGE_MESSAGE_EVERY_PERIOD, $arrLangFileParams) : '') .
				'.</div>';

			return $result;
		}

		if($performAction)
		{
			$actionsLeft--;

			db_res("
				UPDATE `sys_acl_actions_track`
				SET ActionsLeft = $actionsLeft
				WHERE IDAction = $actionID AND IDMember = $iMemberId");
		}
	}

	$result[CHECK_ACTION_RESULT] = CHECK_ACTION_RESULT_ALLOWED;
	return $result;
}

/**
 * Buy a membership for a member
 *
 * @param int $iMemberId			- member that is going to get the membership
 * @param int $iMembershipId		- bought membership
 * @param int $sTransactionId	- internal key of the transaction (ID from Transactions table)
 * @param boolean $bStartsNow	- if true, the membership will start immediately;
 *								  if false, the membership will start after the current
 *								  membership expires
 *
 * @return boolean				- true in case of success, false in case of failure
 *
 */
function buyMembership($iMemberId, $iMembershipId, $sTransactionId, $bStartsNow = false) {
	$iMemberId = (int)$iMemberId;
	$iMembershipId = (int)$iMembershipId;

	$aMembership = db_arr("SELECT 
            `tl`.`ID` AS `ID`, 
            `tlp`.`Days` AS `Days`,
            `tl`.`Active` AS `Active`, 
            `tl`.`Purchasable` AS `Purchasable` 
        FROM `sys_acl_levels` AS `tl` 
        LEFT JOIN `sys_acl_level_prices` AS `tlp` ON `tl`.`ID`=`tlp`.`IDLevel` 
        WHERE `tlp`.`id`='" . $iMembershipId . "'"
    );
	if(!is_array($aMembership) || empty($aMembership))
        return false;

	$iMembershipId = (int)$aMembership['ID'];

	//check for predefined non-purchasable memberships
	if(in_array($iMembershipId, array(MEMBERSHIP_ID_NON_MEMBER, MEMBERSHIP_ID_STANDARD, MEMBERSHIP_ID_PROMOTION)))
        return false;

	//check if membership is active and purchasable
	if($aMembership['Active'] != 'yes' || $aMembership['Purchasable'] != 'yes') 
        return false;

	return setMembership($iMemberId, $iMembershipId, $aMembership['Days'], $bStartsNow, $sTransactionId);
}

/**
 * Set a membership for a member
 *
 * @param int $iMemberId			- member that is going to get the membership
 * @param int $iMembershipId		- membership that is going to be assigned to the member
 * 								  if $iMembershipId == MEMBERSHIP_ID_STANDARD then $days
 *								  and $bStartsNow parameters are not used, so Standard
 *								  membership is always set immediately and `forever`
 *
 * @param int $days				- number of days to set membership for
 *								  if 0, then the membership is set forever
 *
 * @param boolean $bStartsNow	- if true, the membership will start immediately;
 *								  if false, the membership will start after the current
 *								  membership expires
 *
 * @return boolean				- true in case of success, false in case of failure
 *
 *
 */
function setMembership($iMemberId, $iMembershipId, $iDays = 0, $bStartsNow = false, $sTransactionId = '') {
	$iMemberId = (int)$iMemberId;
	$iMembershipId = (int)$iMembershipId;
	$iDays = (int)$iDays;
	$bStartsNow = $bStartsNow ? true : false;

	$SECONDS_IN_DAY = 86400;

	if(!$iMemberId)
		$iMemberId = -1;

	if(empty($sTransactionId)) 
        $sTransactionId = 'NULL';

	//check if member exists
	$aProfileInfo = getProfileInfo($iMemberId);
	if(!$aProfileInfo) 
        return false;

	//check if membership exists
	$iRes = (int)db_value("SELECT COUNT(`ID`) FROM `sys_acl_levels` WHERE `ID`='" . $iMembershipId . "' LIMIT 1");
	if($iRes != 1) 
        return false;

	if($iMembershipId == MEMBERSHIP_ID_NON_MEMBER) 
        return false;

	$aMembershipCurrent = getMemberMembershipInfo($iMemberId);
	$aMembershipLatest = getMemberMembershipInfo_latest($iMemberId);

	/**
	 * Setting Standard membership level
	 */ 
	if($iMembershipId == MEMBERSHIP_ID_STANDARD) {
		if($aMembershipCurrent['ID'] == MEMBERSHIP_ID_STANDARD) 
            return true;

		//delete any present and future memberships
		db_res("DELETE FROM `sys_acl_levels_members` WHERE `IDMember`='" . $iMemberId . "' AND (`DateExpires` IS NULL OR `DateExpires`>NOW())");
		if(db_affected_rows() <= 0)
            return false;
	}

	if($iDays < 0) 
        return false;

	$iDateStarts = time();
	if(!$bStartsNow) {
		/**
		 * make the membership starts after the latest membership expires
		 * or return false if latest membership isn't Standard and is lifetime membership 
		 */
		if(!is_null($aMembershipLatest['DateExpires']))
            $iDateStarts = $aMembershipLatest['DateExpires'];
		else if(is_null($aMembershipLatest['DateExpires']) && $aMembershipLatest['ID'] != MEMBERSHIP_ID_STANDARD)
            return false;
	}
	else
		db_res("DELETE FROM `sys_acl_levels_members` WHERE `IDMember`='" . $iMemberId . "'"); //delete previous profile's membership level

	/**
	 * set lifetime membership if 0 days is used.
	 */
    $iDateExpires = $iDays != 0 ? (int)$iDateStarts + $iDays * $SECONDS_IN_DAY : 'NULL';
	db_res("INSERT `sys_acl_levels_members` (`IDMember`, `IDLevel`, `DateStarts`, `DateExpires`, `TransactionID`) VALUES ('" . $iMemberId . "', '" . $iMembershipId . "', FROM_UNIXTIME(" . $iDateStarts . "), FROM_UNIXTIME(" . $iDateExpires . "), '" . $sTransactionId . "')");
	if(db_affected_rows() <= 0)
	   return false;

	//Set Membership Alert
	bx_import('BxDolAlerts');
	$oZ = new BxDolAlerts('profile', 'set_membership', '', $iMemberId, array('mlevel'=> $iMembershipId, 'days' => $iDays, 'starts_now' => $bStartsNow, 'txn_id' => $sTransactionId));
	$oZ->alert();

	$oEmailTemplate = new BxDolEmailTemplates();
    $aTemplate = $oEmailTemplate->getTemplate('t_MemChanged', $iMemberId);

    $aMembershipInfo = getMembershipInfo($iMembershipId);
    $aTemplateVars = array(
        'MembershipLevel' => $aMembershipInfo['Name']
    );

    sendMail( $aProfileInfo['Email'], $aTemplate['Subject'], $aTemplate['Body'], $iMemberId, $aTemplateVars);

	return true;
}

/**
 * Get the list of existing memberships
 *
 * @param bool $purchasableOnly	- if true, fetches only purchasable memberships;
 * 								  'purchasable' here means that:
 *								  1. MemLevels.Purchasable = 'yes'
 *								  2. MemLevels.Active = 'yes'
 * 								  3. there is at least one pricing option for the membership
 *
 * @return array( membershipID_1 => membershipName_1,  membershipID_2 => membershipName_2, ...) - if no such memberships, then just array()
 *
 *
 */
function getMemberships($purchasableOnly = false)
{
	$result = array();

	$queryPurchasable = '';

	if($purchasableOnly)
	{
		$queryPurchasable = "INNER JOIN `sys_acl_level_prices` ON `sys_acl_level_prices`.IDLevel = `sys_acl_levels`.ID WHERE Purchasable = 'yes' AND Active = 'yes'";
	}

	$resMemLevels = db_res("SELECT DISTINCT `sys_acl_levels`.ID, `sys_acl_levels`.Name FROM `sys_acl_levels` $queryPurchasable");

	while(list($id, $name) = mysql_fetch_row($resMemLevels))
	{
		$result[(int)$id] = $name;
	}

	return $result;
}

/**
 * Get pricing options for the given membership
 *
 * @param int $iMembershipId	- membership to get prices for
 *
 * @return array( days1 => price1, days2 => price2, ...) - if no prices set, then just array()
 *
 *
 */
function getMembershipPrices($iMembershipId)
{
	$iMembershipId = (int)$iMembershipId;
	$result = array();

	$resMemLevelPrices = db_res("SELECT Days, Price FROM `sys_acl_level_prices` WHERE IDLevel = $iMembershipId ORDER BY Days ASC");

	while(list($days, $price) = mysql_fetch_row($resMemLevelPrices))
	{
		$result[(int)$days] = (float)$price;
	}

	return $result;
}

/**
 * Get info about a given membership
 *
 * @param int $iMembershipId	- membership to get info about
 *
 * @return array(	'Name' => name,
 * 					'Active' => active,
 *					'Purchasable' => purchasable,
 *					'Removable' => removable)
 *
 *
 */
function getMembershipInfo($iMembershipId)
{
	$iMembershipId = (int)$iMembershipId;
	$result = array();

	$resMemLevels = db_res("SELECT Name, Active, Purchasable, Removable FROM `sys_acl_levels` WHERE ID = $iMembershipId");

	if(mysql_num_rows($resMemLevels) > 0)
	{
		$result = mysql_fetch_assoc($resMemLevels);
	}

	return $result;
}


/**
 * Define action, dirine defining all names are transl;ated the following way:
 *  my action => BX_MY_ACTION
 *
 * @param $aActions array of actions from sys_acl_actions table, with default array keys (starting from 0) and text values
 */
function defineMembershipActions ($aActionsAll, $sPrefix = 'BX_')
{
    $aActions = array ();
    foreach ($aActionsAll as $sName)
        if (!defined($sPrefix . strtoupper(str_replace(' ', '_', $sName))))
            $aActions[] = $sName;
    if (!$aActions)
        return;

    $sActions = implode("','", $aActions);
    $res = db_res("SELECT `ID`, `Name` FROM `sys_acl_actions` WHERE `Name` IN('$sActions')");
    while ($r = mysql_fetch_array($res)) {
        define ($sPrefix . strtoupper(str_replace(' ', '_', $r['Name'])), $r['ID']);
    }
}		

?>
