<?php
/**
 * InfoLog - history and notifications
 *
 * @link http://www.egroupware.org
 * @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
 * @package tracker
 * @copyright (c) 2007-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
 * @version $Id$
 */

use EGroupware\Api;
use EGroupware\Api\Link;

/**
 * Tracker - tracking object for the tracker
 */
class infolog_tracking extends Api\Storage\Tracking
{
	/**
	 * Application we are tracking (required!)
	 *
	 * @var string
	 */
	var $app = 'infolog';
	/**
	 * Name of the id-field, used as id in the history log (required!)
	 *
	 * @var string
	 */
	var $id_field = 'info_id';
	/**
	 * Name of the field with the creator id, if the creator of an entry should be notified
	 *
	 * @var string
	 */
	var $creator_field = 'info_owner';
	/**
	 * Name of the field with the id(s) of assinged users, if they should be notified
	 *
	 * @var string
	 */
	var $assigned_field = 'info_responsible';
	/**
	 * Translate field-names to 2-char history status
	 *
	 * @var array
	 */
	var $field2history = array(
		'info_type'          => 'Ty',
		'info_from'          => 'Fr',
		'info_link_id'       => 'Li',
		'info_id_parent'     => 'parent',
		'info_cat'           => 'Ca',
		'info_priority'      => 'Pr',
		'info_owner'         => 'Ow',
		'info_access'        => 'Ac',
		'info_status'        => 'St',
		'info_percent'       => 'Pe',
		'info_datecompleted' => 'Co',
		'info_location'      => 'Lo',
		'info_startdate'     => 'st',
		'info_enddate'       => 'En',
		'info_responsible'   => 'Re',
		'info_cc'            => 'cc',
		'info_subject'       => 'Su',
		'info_des'           => 'De',
		'info_location'      => 'Lo',
		// PM fields
		'info_planned_time'  => 'pT',
		'info_replanned_time'  => 'replanned',
		'info_used_time'     => 'uT',
		'pl_id'              => 'pL',
		'info_price'         => 'pr',
		// all custom fields together
		'custom'             => '#c',
	);
	/**
	 * Translate field-names to labels
	 *
	 * @note The order of these fields is used to determine the order for CSV export
	 * @var array
	 */
	var $field2label = array(
		'info_type'      => 'Type',
		'info_from'      => 'Contact',
		'info_subject'   => 'Subject',
		'info_des'       => 'Description',
		'info_link_id'   => 'primary link',
		'info_id_parent' => 'Parent',
		'info_cat'       => 'Category',
		'info_priority'  => 'Priority',
		'info_owner'     => 'Owner',
		'info_modifier'  => 'Modifier',
		'info_access'    => 'Access',
		'info_status'    => 'Status',
		'info_percent'   => 'Completed',
		'info_datecompleted' => 'Date completed',
		'info_datemodified' => 'Last changed',
		'info_location'  => 'Location',
		'info_startdate' => 'Start date',
		'info_enddate'   => 'Enddate',
		'info_responsible' => 'Responsible',
		'info_cc'        => 'Cc',
		// PM fields
		'info_planned_time'  => 'planned time',
		'info_replanned_time'  => 're-planned time',
		'info_used_time'     => 'used time',
		'pl_id'              => 'pricelist',
		'info_price'         => 'price',
		// custom fields
		'custom'             => 'custom fields'
	);

	/**
	 * Instance of the infolog_bo class calling us
	 *
	 * @var infolog_bo
	 */
	private $infolog;

	/**
	 * Constructor
	 *
	 * @param botracker $botracker
	 * @return tracker_tracking
	 */
	function __construct(&$infolog_bo)
	{
		parent::__construct('infolog');	// add custom fields from infolog

		$this->infolog =& $infolog_bo;
	}

	/**
	 * Get the subject for a given entry
	 *
	 * Reimpleneted to use a New|deleted|modified prefix.
	 *
	 * @param array $data
	 * @param array $old
	 * @return string
	 */
	function get_subject($data, $old, $deleted = null, $receiver = null)
	{
		if (!empty($data['prefix']))
		{
			$prefix = $data['prefix'];	// async notification
		}
		elseif (!$old || $old['info_status'] == 'deleted')
		{
			$prefix = lang('New %1',lang($this->infolog->enums['type'][$data['info_type']]));
		}
		elseif($data['info_status'] == 'deleted')
		{
			$prefix = lang('%1 deleted',lang($this->infolog->enums['type'][$data['info_type']]));
		}
		else
		{
			$prefix = lang('%1 modified',lang($this->infolog->enums['type'][$data['info_type']]));
		}
		return $prefix.': '.$data['info_subject'];
	}

	/**
	 * Get the modified / new message (1. line of mail body) for a given entry, can be reimplemented
	 *
	 * @param array $data
	 * @param array $old
	 * @return string
	 */
	function get_message($data, $old, $receiver = null)
	{
		if (!empty($data['message'])) return $data['message'];	// async notification

		if (!$old || $old['info_status'] == 'deleted')
		{
			return lang('New %1 created by %2 at %3',lang($this->infolog->enums['type'][$data['info_type']]),
				Api\Accounts::username($this->infolog->user),$this->datetime('now'));
		}
		elseif($data['info_status'] == 'deleted')
		{
			return lang('%1 deleted by %2 at %3',lang($this->infolog->enums['type'][$data['info_type']]),
				Api\Accounts::username($data['info_modifier']),
				$this->datetime($data['info_datemodified']));
		}
		return lang('%1 modified by %2 at %3',lang($this->infolog->enums['type'][$data['info_type']]),
			Api\Accounts::username($data['info_modifier']),
			$this->datetime($data['info_datemodified']));
	}

	/**
	 * Get the details of an entry
	 *
	 * @param array|object $data
	 * @param int|string $receiver nummeric account_id or email address
	 * @return array of details as array with values for keys 'label','value','type'
	 */
	function get_details($data,$receiver=null)
	{
		//error_log(__METHOD__.__LINE__.' Data:'.array2string($data));
		$responsible = array();
		if ($data['info_responsible'])
		{
			foreach($data['info_responsible'] as $uid)
			{
				$responsible[] = Api\Accounts::username($uid);
			}
		}
		if ($GLOBALS['egw_info']['user']['preferences']['infolog']['show_id'])
		{
			$id = ' #'.$data['info_id'];
		}
		if (is_array($data['info_cat']))
		{
			$data['info_cat'] = array_shift($data['info_cat']);
		}
		foreach(array(
			'info_type'      => lang($this->infolog->enums['type'][$data['info_type']]).$id,
			'info_from'      => $data['info_from'],
			'info_cat'       => $data['info_cat'] ? $GLOBALS['egw']->categories->id2name($data['info_cat']) : '',
			'info_priority'  => lang($this->infolog->enums['priority'][$data['info_priority']]),
			'info_owner'     => Api\Accounts::username($data['info_owner']),
			'info_status'    => lang($data['info_status']=='deleted'?'deleted':$this->infolog->status[$data['info_type']][$data['info_status']]),
			'info_percent'   => (int)$data['info_percent'].'%',
			'info_datecompleted' => $data['info_datecompleted'] ? $this->datetime($data['info_datecompleted']) : '',
			'info_location'  => $data['info_location'],
			'info_startdate' => $data['info_startdate'] ? $this->datetime($data['info_startdate'],null) : '',
			'info_enddate'   => $data['info_enddate'] ? $this->datetime($data['info_enddate'],null) : '',
			'info_responsible' => implode(', ',$responsible),
			'info_subject'   => $data['info_subject'],
		) as $name => $value)
		{
			//error_log(__METHOD__.__LINE__.' Key:'.$name.' val:'.array2string($value));
			if ($name=='info_from' && empty($value))
			{
				if(!empty($data['info_contact']) && is_array($data['link_to']['to_id']))
				{
					$lkeys = array_keys($data['link_to']['to_id']);
					if (in_array($data['info_contact'],$lkeys))
					{
						list($app,$id) = explode(':',$data['info_contact']);
						if (!empty($app)&&!empty($id)) $value = Link::title($app,$id);
					}
				}
				else if ($data['info_link_id'])
				{
					$this->infolog->link_id2from($data);
					if (is_array($data['info_contact'])) $value = $data['info_contact']['title'];
				}
			}
			$details[$name] = array(
				'label' => lang($this->field2label[$name]),
				'value' => $value,
			);
			if ($name == 'info_subject') $details[$name]['type'] = 'summary';
		}
		$details['info_des'] = array(
			'value' => $data['info_des'],
			'type'  => 'multiline',
		);
		// add custom fields for given type
		$details += $this->get_customfields($data, $data['info_type'], $receiver);
		//error_log(__METHOD__."(".array2string($data).", $receiver) returning ".array2string($details));
		return $details;
	}

	/**
	 * Track changes
	 *
	 * Overrides parent to log the modified date in the history, but not to send a notification
	 *
	 * @param array $data current entry
	 * @param ?array $old = null old/last state of the entry or null for a new entry
	 * @param int $user = null user who made the changes, default to current user
	 * @param boolean $deleted = null can be set to true to let the tracking know the item got deleted or undeleted
	 * @param ?array $changed_fields = null changed fields from ealier call to $this->changed_fields($data,$old), to not compute it again
	 * @param boolean $skip_notification = false do NOT send any notification
	 * @return int|boolean false on error, integer number of changes logged or true for new entries ($old == null)
	 */
	public function track(array $data, ?array $old=null, $user=null, $deleted=null, ?array $changed_fields=null, $skip_notification=false)
	{
		//error_log(__METHOD__.__LINE__.' notify?'.($skip_notification?'no':'yes').function_backtrace());
		$this->user = !is_null($user) ? $user : $GLOBALS['egw_info']['user']['account_id'];

		$changes = true;

		if ($old && $this->field2history)
		{
			$changes = $this->save_history($data,$old,$deleted,$changed_fields);
		}

		// Don't notify if the only change was to the modified date
		if(is_null($changed_fields))
		{
			$changed_fields = $this->changed_fields($data, $old);
			$changes = count($changed_fields); // we need that since TRUE evaluates to 1
		}
		//error_log(__METHOD__.__LINE__.array2string($changed_fields));
		if(is_array($changed_fields) && $changes == 1 && in_array('info_datemodified', $changed_fields))
		{
			return count($changes);
		}

		// do not run do_notifications if we have no changes
		if ($changes && !$skip_notification && !$this->do_notifications($data,$old,$deleted))
		{
			$changes = false;
		}
		return $changes;
	}

	/**
	 * Get a notification-config value
	 *
	 * @param string $name
	 *  - 'copy' array of email addresses notifications should be copied too, can depend on $data
	 *  - 'lang' string lang code for copy mail
	 *  - 'sender' string send email address
	 * @param array $data current entry
	 * @param array $old = null old/last state of the entry or null for a new entry
	 * @return mixed
	 */
	function get_config($name,$data,$old=null)
	{
		unset($old);	// not used, but required function signature
		switch($name)
		{
			case 'reply_to':    // for async notification use owner of infolog, otherwise the user causing the change
				if(($reply_to = Api\Accounts::getInstance()
											->id2name(!empty($GLOBALS['egw_info']['flags']['async-service']) ?
														  $data['info_owner'] : $this->user, 'account_email'
											)))
				{
					$config = $reply_to;
				}
				break;
			case 'sender':
				// Use infolog owner as the notification sender
				$config = $data['info_owner'];
				break;
			case 'copy':	// include the info_cc addresses
				if ($data['info_access'] == 'private') return array();	// no copies for private entries
				if ($data['info_cc'])
				{
					$config = preg_split('/, ?/',$data['info_cc']);
				}
				else
				{
					$config = array();
				}
				break;
			case self::CUSTOM_NOTIFICATION:
				$info_config = Api\Config::read('infolog');
				if(!$info_config[self::CUSTOM_NOTIFICATION])
				{
					return '';
				}
				// Per-type notification
				$type_config = $info_config[self::CUSTOM_NOTIFICATION][$data['info_type']] ?? null;
				$global = $info_config[self::CUSTOM_NOTIFICATION]['~global~'];

				// Disabled
				if(empty($type_config['use_custom']) && empty($global['use_custom'])) return '';

				// Type or global
				$config = trim(strip_tags($type_config['message'])) != '' && $type_config['use_custom'] ? $type_config['message'] : $global['message'];
				break;
		}
		return $config ?? null;
	}
}