<?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' => ' · ',
'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 );
}
}
?>