<?php
/**
 * This file implements the Financial Contribution plugin.
 *
 * This file is part of the b2evolution project - {@link http://b2evolution.net/}
 *
 * @license GNU GPL v2 - {@link http://b2evolution.net/about/gnu-gpl-license}
 *
 * @copyright (c)2003-2020 by Francois Planque - {@link http://fplanque.com/}
 *
 * @package plugins
 */
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );


/**
 * Financial Contribution Plugin
 *
 * This plugin displays
 */
class financial_contribution_plugin extends Plugin
{
	var $name;
	var $code = 'fin_contrib';
	var $priority = 50;
	var $version = '7.2.3';
	var $author = 'The b2evo Group';
	var $group = 'widget';
	var $subgroup = 'other';
	var $widget_icon = 'money';


	/**
	 * Init
	 */
	function PluginInit( & $params )
	{
		$this->name = T_('Financial Contribution');
		$this->short_desc = T_('This skin tag displays a form for financial contribution.');
		$this->long_desc = T_('This skin tag displays a form for financial contribution.');
	}


	/**
	 * Table to store financial contributions per Item per User
	 *
	 * @return array
	 */
	function GetDbLayout()
	{
		global $DB;

		return array(
				'CREATE TABLE '.$this->get_sql_table( 'contributions' ).' (
					fnct_item_ID   INT(10) UNSIGNED NOT NULL,
					fnct_user_ID   INT(10) UNSIGNED NOT NULL,
					fnct_amount    DOUBLE UNSIGNED NOT NULL,
					fnct_timestamp TIMESTAMP NOT NULL DEFAULT "2000-01-01 00:00:00",
					PRIMARY KEY( fnct_item_ID, fnct_user_ID )
				) ENGINE = innodb DEFAULT CHARSET = '.$DB->get_connection_charset(),
			);
	}


	/**
	 * Define here default collection/blog settings that are to be made available in the backoffice.
	 *
	 * @see Plugin::GetDefaultSettings()
	 * @param array Associative array of parameters.
	 *    'for_editing': true, if the settings get queried for editing;
	 *                   false, if they get queried for instantiating {@link Plugin::$UserSettings}.
	 * @return
	 */
	function get_coll_setting_definitions( & $params )
	{
		$CurrencyCache = & get_CurrencyCache();
		$CurrencyCache->load_all();
		$currencies = $CurrencyCache->get_option_array();
		foreach( $currencies as $c => $currency )
		{
			unset( $currencies[ $c ] );
			$currencies[ $currency ] = $currency;
		}

		$r = array_merge( array(
			'currency' => array(
				'label' => T_('Currency'),
				'defaultvalue' => 'USD',
				'type' => 'select',
				'options' => $currencies,
			),
		), parent::get_coll_setting_definitions( $params ) );

		return $r;
	}


	/**
	 * Get keys for block/widget caching
	 *
	 * Maybe be overriden by some widgets, depending on what THEY depend on..
	 *
	 * @param integer Widget ID
	 * @return array of keys this widget depends on
	 */
	function get_widget_cache_keys( $widget_ID = 0 )
	{
		global $Collection, $Blog, $Item, $current_User;

		return array(
				'wi_ID'        => $widget_ID, // Have the widget settings changed ?
				'set_coll_ID'  => isset( $Blog ) ? $Blog->ID : NULL, // Have the settings of the blog changed ? (ex: new skin)
				'cont_coll_ID' => isset( $Blog ) ? $Blog->ID : NULL, // Has the content of the displayed blog changed ?
				'item_ID'      => ( empty( $Item->ID ) ? 0 : $Item->ID ), // Has the Item page changed?
				'user_ID'      => ( is_logged_in() ? $current_User->ID : 0 ), // Has the current User changed?
			);
	}


	/**
	 * Get definitions for widget specific editable params
	 *
	 * @see Plugin::GetDefaultSettings()
	 * @param local params like 'for_editing' => true
	 */
	function get_widget_param_definitions( $params )
	{
		return array(
			'check_custom_field' => array(
				'label' => T_('Custom field to check'),
				'size' => 60,
				'defaultvalue' => 'project_cost',
			),
			'title' => array(
				'label' => T_('Title'),
				'size' => 60,
				'defaultvalue' => T_('Contribute to this project'),
			),
			'estimate_text' => array(
				'label' => T_('Estimate text'),
				'size' => 60,
				'defaultvalue' => T_('Estimated Cost'),
			),
			'total_text' => array(
				'label' => T_('Total text'),
				'size' => 60,
				'defaultvalue' => T_('Total contributions so far'),
			),
			'current_contributors_text' => array(
				'label' => T_('Current contributors list text param'),
				'size' => 100,
				'defaultvalue' => T_('Members who have already pledged a contribution'),
			),
			'contribute_text' => array(
				'label' => T_('Contribute text'),
				'size' => 100,
				'defaultvalue' => T_('If you wish to financially support this project, enter the amount you are willing to contribute here'),
			),
			'contribute_button_text' => array(
				'label' => T_('Contribute button text'),
				'size' => 60,
				'defaultvalue' => T_('Pledge to contribute'),
			),
			'modify_button_text' => array(
				'label' => T_('Modify button text'),
				'size' => 60,
				'defaultvalue' => T_('Modify'),
			),
			'contribution_message' => array(
				'label' => T_('Contribution Message'),
				'size' => 100,
				'defaultvalue' => T_('Your contribution has been recorded. Thank you!'),
			),
			'deletion_message' => array(
				'label' => T_('Deletion Message'),
				'size' => 100,
				'defaultvalue' => T_('Your pledge has been removed.'),
			),
			'thank_you_text' => array(
				'label' => T_('Thank you text'),
				'size' => 100,
				'defaultvalue' => T_('You have pledged to contribute'),
			),
		);
	}


	/**
	 * Event handler: SkinTag
	 *
	 * @param array Associative array of parameters
	 * @return boolean did we display?
	 */
	function SkinTag( & $params )
	{
		global $Blog, $Item, $current_User;

		$this->init_widget_params( $params, array(
				'block_start'       => '<div class="evo_widget $wi_class$ panel panel-default">',
				'block_end'         => '</div>',
				'block_title_start' => '<div class="panel-heading"><h4 class="panel-title">',
				'block_title_end'   => '</h4></div>',
				'block_body_start'  => '<div class="panel-body">',
				'block_body_end'    => '</div>',
				'text_row_start'    => '<p>',
				'text_title_start'  => '<b>',
				'text_title_end'    => ': </b>',
				'text_value_start'  => '',
				'text_value_end'    => '',
				'text_separator'    => ' &middot; ',
				'text_row_end'      => '</p>',
			) );

		if( empty( $Blog ) )
		{	// Don't display this widget when no current Collection:
			$this->display_widget_debug_message( 'Plugin widget "'.$this->name.'" is hidden because there is no Collection.' );
			return false;
		}

		if( empty( $Item ) )
		{	// Don't display this widget when no current Item:
			$this->display_widget_debug_message( 'Plugin widget "'.$this->name.'" is hidden because there is no Item.' );
			return false;
		}

		if( ! is_logged_in() )
		{	// Don't display this widget when current User is not logged in:
			$this->display_widget_debug_message( 'Plugin widget "'.$this->name.'" is hidden because you are not logged in.' );
			return false;
		}

		$CurrencyCache = & get_CurrencyCache();
		if( ! ( $Currency = $CurrencyCache->get_by_name( $this->get_coll_setting( 'currency', $Blog ), false, false ) ) )
		{	// Don't display this widget with unknown currency:
			$this->display_widget_debug_message( 'Plugin widget "'.$this->name.'" is hidden because currency is not found, please check collection settings of this plugin.' );
			return false;
		}

		if( $Item->get_custom_field_value( $this->get_widget_setting( 'check_custom_field' ) ) <= 0 )
		{	// Don't display this widget when custom field value is less 0 for the current Item:
			$this->display_widget_debug_message( 'Plugin widget "'.$this->name.'" is hidden because custom field value is less 0.' );
			return false;
		}

		// Get contributed users:
		$contributed_users = $this->get_users_by_item_ID( $Item->ID );

		// Check if current user already contributed for the Item:
		if( isset( $contributed_users[ $current_User->ID ] ) )
		{
			$is_contributed_current_user = true;
			$contributed_current_user_amount = $contributed_users[ $current_User->ID ];
		}
		else
		{
			$is_contributed_current_user = false;
			$contributed_current_user_amount = '';
		}

		// Calculate contribution data:
		$total_amount = 0;
		if( ! empty( $contributed_users ) )
		{	// Display only if at least one user contributed this Item:
			$UserCache = & get_UserCache();
			$UserCache->load_list( array_keys( $contributed_users ) );
			$current_contributors_text = array();
			foreach( $contributed_users as $cont_user_ID => $cont_amount )
			{
				if( $cont_User = & $UserCache->get_by_ID( $cont_user_ID, false, false ) )
				{
					$current_contributors_text[] = $cont_User->get_identity_link().' ('.$Currency->get( 'shortcut' ).number_format( $cont_amount, 0, '', '\'' ).')';
					$total_amount += $cont_amount;
				}
			}
			$current_contributors_text = implode( ', ', $current_contributors_text );
		}

		echo $this->widget_params['block_start'];

		$widget_title = $this->get_widget_setting( 'title' );
		if( ! empty( $widget_title ) )
		{	// We want to display a title for the widget block:
			echo $this->widget_params['block_title_start'];
			echo $widget_title;
			echo $this->widget_params['block_title_end'];
		}

		echo $this->widget_params['block_body_start'];

		$Form = new Form( $this->get_htsrv_url( 'contribute', array(), '&' ), '', 'post' );

		$Form->begin_form();
		$Form->hidden( 'wi_ID', $this->widget_params['wi_ID'] );
		$Form->hidden( 'item_ID', $Item->ID );
		$Form->add_crumb( 'contribute' );

		// Estimate text + Total text:
		$this->display_widget_text( array(
				'estimate_text' => $Item->get_custom_field_formatted( $this->get_widget_setting( 'check_custom_field' ) ),
				'total_text'    => $Currency->get( 'shortcut' ).number_format( $total_amount, 0, '', '\'' ),
			) );

		if( ! empty( $current_contributors_text ) )
		{	// Current contributors list:
			$this->display_widget_text( 'current_contributors_text', $current_contributors_text );
		}

		// Contribute text (amount input + submit button):
		$Form->output = false;
		$Form->switch_layout( 'none' );
		$contribute_input = $Form->text_input( 'contribute_amount', $contributed_current_user_amount, 5, '', '', array( 'maxlength' => 16, 'style' => 'width:auto;display:inline' ) );
		$contribute_button = $Form->button( array( 'submit', 'action_name',
			$this->get_widget_setting( $is_contributed_current_user ? 'modify_button_text' : 'contribute_button_text' ),
			$is_contributed_current_user ? 'btn-default' : 'btn-primary' ) );
		$Form->switch_layout( NULL );
		$Form->output = true;
		$this->display_widget_text( ( $is_contributed_current_user ? 'thank_you_text' : 'contribute_text' ),
			'<span class="nowrap">'.$contribute_input.' '.$this->get_coll_setting( 'currency', $Blog ).'</span> '.$contribute_button );

		$Form->end_form();

		echo $this->widget_params['block_body_end'];

		echo $this->widget_params['block_end'];

		return true;
	}


	/**
	 * Display text field for the widget form
	 *
	 * @param string|array Widget setting name or set of settings
	 * @param string Content
	 */
	function display_widget_text( $setting_name, $content = NULL )
	{
		$settings = is_array( $setting_name ) ? $setting_name : array( $setting_name => $content );
		$settings_num = count( $settings );

		echo $this->widget_params['text_row_start'];

		$s = 0;
		foreach( $settings as $setting_name => $content )
		{
			echo $this->widget_params['text_title_start']
				.$this->get_widget_setting( $setting_name )
			.$this->widget_params['text_title_end']
			.$this->widget_params['text_value_start']
				.$content
			.$this->widget_params['text_value_end'];
			if( $s < $settings_num - 1 )
			{	// Display a separator between several settings:
				echo $this->widget_params['text_separator'];
			}
			$s++;
		}

		echo $this->widget_params['text_row_end'];
	}


	/**
	 * Return the list of Htsrv (HTTP-Services) provided by the plugin.
	 *
	 * This implements the plugin interface for the list of methods that are valid to
	 * get called through htsrv/call_plugin.php.
	 *
	 * @return array
	 */
	function GetHtsrvMethods()
	{
		return array( 'contribute' );
	}

	/**
	 * Plugin action to store a a pledge of contribution
	 *
	 * @param array Params
	 */
	function htsrv_contribute( $params )
	{
		global $Messages, $Session, $current_User, $DB, $localtimenow;

		// Check that this action request is not a CSRF hacked request:
		$Session->assert_received_crumb( 'contribute' );

		if( ! is_logged_in() )
		{	// User must be logged in:
			// (don't translate because for normal work it must not happens):
			$Messages->add( 'You must be logged in to pledge a contribution.', 'error' );
			header_redirect();
			// Exit here.
		}

		$widget_ID = param( 'wi_ID', 'integer', true );
		$item_ID = param( 'item_ID', 'integer', true );
		$contribute_amount = str_replace( ',', '.', param( 'contribute_amount', 'string' ) );

		if( $contribute_amount === '' ||
		    ! is_decimal( $contribute_amount ) ||
		    $contribute_amount < 0 )
		{	// Allow only decimal numbers for amount::
			$Messages->add( T_('Please enter an amount you are willing to contribute.'), 'error' );
			header_redirect();
			// Exit here.
		}

		$WidgetCache = & get_WidgetCache();
		if( ! ( $Widget = & $WidgetCache->get_by_ID( $widget_ID, false, false ) ) )
		{	// Widget is not found by requested ID:
			// (don't translate because for normal work it must not happens):
			$Messages->add( 'Widget #'.$widget_ID.' is not found.', 'error' );
			header_redirect();
			// Exit here.
		}

		$ItemCache = & get_ItemCache();
		if( ! ( $Item = & $ItemCache->get_by_ID( $item_ID, false, false ) ) )
		{	// Item is not found by requested ID:
			// (don't translate because for normal work it must not happens):
			$Messages->add( 'Item #'.$item_ID.' is not found.', 'error' );
			header_redirect();
			// Exit here.
		}

		// Format amount:
		$contribute_amount = floor( $contribute_amount );

		if( $contribute_amount > 0 )
		{	// Insert/Update contribution:
			$DB->query( 'REPLACE INTO '.$this->get_sql_table( 'contributions' ).'
				       ( fnct_item_ID, fnct_user_ID, fnct_amount, fnct_timestamp )
				VALUES ( '.$DB->quote( $item_ID ).', '.$DB->quote( $current_User->ID ).', '.$DB->quote( $contribute_amount ).', '.$DB->quote( date2mysql( $localtimenow ) ).' )' );
			// Inform user about success contribution:
			$Messages->add( $Widget->get_param( 'contribution_message' ), 'success' );
		}
		else
		{	// Delete contribution:
			$DB->query( 'DELETE FROM '.$this->get_sql_table( 'contributions' ).'
				WHERE fnct_item_ID = '.$DB->quote( $item_ID ).'
				  AND fnct_user_ID = '.$DB->quote( $current_User->ID ) );
			// Inform user after delete contribution:
			$Messages->add( $Widget->get_param( 'deletion_message' ), 'warning' );
		}

		// The cached content of the widget must be invalidated:
		BlockCache::invalidate_key( 'wi_ID', $widget_ID );

		// Redirect back to previous page:
		header_redirect();
	}


	/**
	 * Get contributed users data for requested Item
	 *
	 * @param integer Item ID
	 * @return array Key - User ID, Value - Amount
	 */
	function get_users_by_item_ID( $item_ID )
	{
		global $DB;

		if( empty( $item_ID ) )
		{	// Item ID must be defined:
			return array();
		}

		$SQL = new SQL( 'Get contributed users data for Item #'.$item_ID );
		$SQL->SELECT( 'fnct_user_ID, fnct_amount' );
		$SQL->FROM( $this->get_sql_table( 'contributions' ) );
		$SQL->WHERE( 'fnct_item_ID = '.$DB->quote( $item_ID ) );
		$SQL->ORDER_BY( 'fnct_amount DESC, fnct_timestamp ASC' );

		return $DB->get_assoc( $SQL );
	}
}
?>