<?php
	/**
	 * Rules ItemModule
	 * Module will be used to save rules information to rules table
	 */
	class RulesItemModule extends ItemModule
	{
		/**
		 * @var MAPITable contains resource of rules table.
		 */
		var $rulesTable;

		/**
		 * @var MAPITable contains resource of rules modify table.
		 */
		var $rulesModifyTable;

		/**
		 * Constructor
		 * @param int $id unique id.
		 * @param array $data list of all actions.
		 */
		function RulesItemModule($id, $data)
		{
			$this->properties = $GLOBALS['properties']->getRulesProperties();

			$this->rulesTable = null;
			$this->rulesModifyTable = null;

			parent::ItemModule($id, $data);
		}

		/**
		 * Executes all the actions in the $data variable.
		 * @return boolean true on success of false on fialure.
		 */
		function execute()
		{
			$store = $GLOBALS['mapisession']->getDefaultMessageStore();

			foreach($this->data as $actionType => $action)
			{
				try {
					switch($actionType) {
						case 'save':
							$this->saveRules($store, $action);
							break;
						case 'delete':
							$this->deleteRules($store, $action);
							break;
						default:
							$this->handleUnknownActionType($actionType);
					}
				} catch (MAPIException $e) {
					$this->processException($e, $actionType);
				}
			}
		}

		/**
		 * Function will return resource of the rules table that can be used to create/update delegate
		 * meeting rule in current user's store.
		 * @param {MAPIStore} $store (optional) current user's store.
		 * @return {MAPITable} rules table resource.
		 */
		function getRulesTable($store = false)
		{
			if(!$this->rulesTable) {
				$this->rulesTable = mapi_rules_gettable($this->getRulesModifyTable($store));
			}

			return $this->rulesTable;
		}

		/**
		 * Function will return resource of the rules table that can be used to create/update delegate
		 * meeting rule in current user's store.
		 * @param {MAPIStore} $store (optional) current user's store.
		 * @return {MAPITable} rules table resource.
		 */
		function getRulesModifyTable($store = false)
		{
			if(!$this->rulesModifyTable) {
				if($store === false) {
					$store = $GLOBALS['mapisession']->getDefaultMessageStore();
				}

				$inbox = mapi_msgstore_getreceivefolder($store);

				// get IExchangeModifyTable interface
				$this->rulesModifyTable = mapi_folder_openmodifytable($inbox);
			}

			return $this->rulesModifyTable;
		}

		/**
		 * Function will be used to delete a particular rule from user's rules table.
		 * for this function to work we need PR_RULE_ID property should be present in
		 * the rules data received from client.
		 * @param {MAPIStore} $store current user's store.
		 * @param {Array} $rulesData rules data that should be deleted.
		 */
		function deleteRules($store, $rulesData)
		{
			if(is_assoc_array($rulesData)) {
				// wrap single rule in an array
				$rulesData = array($rulesData);
			}

			// delete rules from rules table
			$deleteRules = array();
			for($index = 0, $len = count($rulesData); $index < $len; $index++) {
				$rule = $rulesData[$index];

				$deleteRules[] = array(
					'rowflags' => ROW_REMOVE,
					'properties' => Array(PR_RULE_ID => $rule['rule_id'])
				);
			}

			if(!empty($deleteRules)) {
				mapi_rules_modifytable($this->getRulesModifyTable(), $deleteRules);

				// send feedback to client
				$this->sendFeedback(true);
			}
		}

		/**
		 * Function to generate a new unique value for the PR_RULE_ID property.
		 * This will obtain the highest already-existing PR_RULE_ID from the table,
		 * and return a number that is 1 higher then that maximum. If the rules table
		 * is empty, then this will return the default value '1'.
		 * @return Number The new Rule ID
		 */
		function createNewRuleId()
		{
			$rulesTable = $this->getRulesTable();

			// Restrict the table to only base the ID on the visible rules
			mapi_table_restrict($rulesTable,
				array(RES_CONTENT,
					array(
						FUZZYLEVEL      =>      FL_PREFIX | FL_IGNORECASE,
						ULPROPTAG       =>      PR_RULE_PROVIDER,
						VALUE           =>      array(
							PR_RULE_PROVIDER        =>      'RuleOrganizer'
						)
					)
				),
				TBL_BATCH
			);

			// Sort on unique ID, we want the highest number to be on top.
			mapi_table_sort($rulesTable, array(PR_RULE_ID => TABLE_SORT_DESCEND), TBL_BATCH);

			$rows = mapi_table_queryrows($rulesTable, array( PR_RULE_ID ), 0, 1);

			if (empty($rows)) {
				// If there are no rows, then assume the default id to be 1.
				return 1;
			} else {
				// Otherwise we just increase the current maximum with 1
				return $rows[0][PR_RULE_ID] + 1;
			}
		}

		/**
		 * Function to generate a new unique value for the PR_RULE_SEQUENCE property.
		 * This will obtain the highest already-existing PR_RULE_SEQUENCE from the table,
		 * and return a number that is 1 higher then that maximum. If the rules table
		 * is empty, then this will return the default value '10'.
		 * @return Number The new Rule Sequence
		 */
		function createNewRuleSequence()
		{
			$rulesTable = $this->getRulesTable();

			// Restrict the table to only base the ID on the visible rules
			mapi_table_restrict($rulesTable,
				array(RES_CONTENT,
					array(
						FUZZYLEVEL      =>      FL_PREFIX | FL_IGNORECASE,
						ULPROPTAG       =>      PR_RULE_PROVIDER,
						VALUE           =>      array(
							PR_RULE_PROVIDER        =>      'RuleOrganizer'
						)
					)
				),
				TBL_BATCH
			);

			// Sort on unique ID, we want the highest number to be on top.
			mapi_table_sort($rulesTable, array(PR_RULE_SEQUENCE => TABLE_SORT_DESCEND), TBL_BATCH);

			$rows = mapi_table_queryrows($rulesTable, array( PR_RULE_SEQUENCE ), 0, 1);

			if (empty($rows)) {
				// If there are no rows, then assume the default sequence to be 10.
				return 10;
			} else {
				// Otherwise we just increase the current maximum with 1
				return $rows[0][PR_RULE_SEQUENCE] + 1;
			}
		}

		/**
		 * Function will be used to create/update rule in user's rules table.
		 * This function only usee ROW_MODIFY flag to save rules data, Which is correct when modifying existing rules
		 * but for adding new rules zarafa server automatically checks existence of rule id and if it si not then
		 * use ROW_ADD flag.
		 * @param {MAPIStore} $store current user's store.
		 * @param {Array} $rulesData rules data that should be deleted.
		 */
		function saveRules($store, $rulesData)
		{
			$responseData = array();

			if(is_assoc_array($rulesData)) {
				// wrap single rule in an array
				$rulesData = array($rulesData);
			}

			// save rules in rules table
			$saveRules = array();
			for($index = 0, $len = count($rulesData); $index < $len; $index++) {
				$rule = $rulesData[$index];
				if(!empty($rule['props'])) {
					$rule += $rule['props'];
				}

				$rule = Conversion::mapXML2MAPI($this->properties, $rule);

				if (empty($rule[PR_RULE_ID])) {
					$rule[PR_RULE_ID] = $this->createNewRuleId();
				}

				if (empty($rule[PR_RULE_SEQUENCE])) {
					$rule[PR_RULE_SEQUENCE] = $this->createNewRuleSequence();
				}

				// provide default action and rule if client has not provided
				if(empty($rule[PR_RULE_ACTIONS])) {
					$rule[PR_RULE_ACTIONS] = array(
						array(
							'action' => OP_DEFER_ACTION,
							'dam' => hex2bin('E0C810000120000100000000000000010000000000000001000000360000000200FFFF00000C004352756C65456C656D656E7490010000010000000000000001000000018064000000010000000000000001000000')
						)
					);
				}

				if(empty($rule[PR_RULE_CONDITION])) {
					$rule[PR_RULE_CONDITION] = array(
						RES_EXIST,
						array(
							ULPROPTAG => PR_MESSAGE_CLASS
						)
					);
				}

				if(empty($rule[PR_RULE_NAME])) {
					$rule[PR_RULE_NAME] = _('Untitled rule');
				}

				// generate rule provider data
				$rule[PR_RULE_PROVIDER_DATA] = pack('VVa*', 1, $rule[PR_RULE_ID], Conversion::UnixTimeToCOleDateTime(time()));

				$saveRules[] = array(
					'rowflags' => ROW_MODIFY,
					'properties' => $rule
				);

				array_push($responseData, array(
					'rule_id' => $rule[PR_RULE_ID],
					'props' => array(
						'rule_sequence' => $rule[PR_RULE_SEQUENCE]
					)
				));
			}

			if(!empty($saveRules)) {
				mapi_rules_modifytable($this->getRulesModifyTable(), $saveRules);

				// send response to indicate success
				if(!empty($responseData)) {
					$this->addActionData('update', array('item' => $responseData));
					$GLOBALS['bus']->addData($this->getResponseData());
				}
			}
		}

		/**
		 * Function does customization of MAPIException based on module data.
		 * like, here it will generate display message based on actionType
		 * for particular exception.
		 * 
		 * @param object $e Exception object.
		 * @param string $actionType the action type, sent by the client.
		 * @param MAPIobject $store Store object of the message.
		 * @param string $parententryid parent entryid of the message.
		 * @param string $entryid entryid of the message/folder.
		 * @param array $action the action data, sent by the client.
		 */
		function handleException(&$e, $actionType = null, $store = null, $parententryid = null, $entryid = null, $action = null)
		{
			if(is_null($e->displayMessage)) {
				switch($actionType)
				{
					case 'save':
						$e->setDisplayMessage(_('Could not save rule.'));
						break;
					case 'delete':
						$e->setDisplayMessage(_('Could not delete rule.'));
						break;
				}
			}
		}
	}
?>
