diff --git a/assets/js/admin.js b/assets/js/admin.js index 61e431d..c23cd7e 100644 --- a/assets/js/admin.js +++ b/assets/js/admin.js @@ -46,7 +46,7 @@ function cfc_select2_defaults( selector, value ) { } } - jQuery( selector ).cfcSelect2({ + jQuery( selector ).cfcSelect2({ ajax: { url: ajaxurl, dataType: 'json', @@ -102,5 +102,46 @@ function cfc_select2_defaults( selector, value ) { .append( new Option( e.params.data.text, e.params.data.id, false, false ) ) .trigger( 'select2:close' ); } ) - }); -} \ No newline at end of file + }); +} + +// decorate formJSON to add price field options for autopopulate field conditionals +jQuery( document ).ready( function( $ ) { + + $.fn.originalFormJSON = $.fn.formJSON; + + $.fn.formJSON = function() { + + var form = $( this ).originalFormJSON(); + + if ( ! form.config || ! form.config.fields ) return form; + + for ( var field_id in form.config.fields ) { + + var config = form.config.fields[field_id].config; + + if ( config.auto && ( config.auto_type.indexOf( 'price_field_' ) !== -1 || config.auto_type.indexOf( 'custom_' ) !== -1 ) ) { + + form.config.fields[field_id].config.option = {}; + // cfc_price_field_ or custom_ + var preset_name = config.auto_type.replace( 'cfc_', '' ), + options = preset_options[preset_name].data; + + if ( options.constructor !== Array ) return form; + options.map( function( option ) { + + var parts = option.split( '|' ); + + form.config.fields[field_id].config.option[parts[0]] = { + value: parts[0], + label: parts[1], + calc_value: parts[2] + }; + + } ); + } + } + + return form; + } +} ); \ No newline at end of file diff --git a/assets/js/autopop_conditionals.js b/assets/js/autopop_conditionals.js new file mode 100644 index 0000000..2b63935 --- /dev/null +++ b/assets/js/autopop_conditionals.js @@ -0,0 +1,67 @@ +// processor conditionals, add price field options for autopopulate +jQuery( document ).ready( function( $ ) { + $( '.caldera-editor-body' ).on( 'change', '.caldera-conditional-field-set', function( e ) { + + var field = $( this ), + field_compare = field.parent().find( '.compare-type' ), + type = field.data( 'condition' ), + pid = field.data( 'id' ), + name = "config[" + type + "][" + pid + "][conditions][group][" + field.data('row') + "][" + field.data('line') + "]", + lineid = field.data( 'line' ), + target = $( '#' + lineid + "_value" ), + curval = target.find( '.caldera-conditional-value-field' ).first(); + + var field_id = this.value, + form = core_form.formJSON(); + + if ( field_id.indexOf( '{' ) >= 0 ) return; + + if ( ! field_id || ! form || ( ! form.config && ! form.config[field_id] ) ) return; + + var config = form.config.fields[field_id].config; + + if ( curval.length ) { + if ( curval.val().length ) + target.data( 'value', curval.val() ); + } else if ( 0 === target.val() ) { + target.data( 'value', 0 ); + } else if ( '0' === target.val() ) { + target.data( 'value', '0' ); + } + + field_compare.show(); + + if ( config.auto && ( config.auto_type.indexOf( 'price_field_' ) !== -1 || config.auto_type.indexOf( 'custom_' ) !== -1 ) ) { + // cfc_price_field_ or custom_ + var preset_name = config.auto_type; + preset_name = config.auto_type.replace( 'cfc_', '' ); + + var options_rows = preset_options[preset_name].data, + out = ''; + + target.html( out ); + + } + + } ); +} ); \ No newline at end of file diff --git a/caldera-forms-civicrm.php b/caldera-forms-civicrm.php index e5f3195..a617064 100644 --- a/caldera-forms-civicrm.php +++ b/caldera-forms-civicrm.php @@ -2,7 +2,7 @@ /** * Plugin Name: Caldera Forms CiviCRM * Description: CiviCRM integration for Caldera Forms. - * Version: 0.4.4 + * Version: 1.0.2 * Author: Andrei Mondoc * Author URI: https://github.com/mecachisenros * Plugin URI: https://github.com/mecachisenros/caldera-forms-civicrm @@ -16,7 +16,7 @@ * * @since 0.1 */ -define( 'CF_CIVICRM_INTEGRATION_VER', '0.4.4' ); +define( 'CF_CIVICRM_INTEGRATION_VER', '1.0.2' ); define( 'CF_CIVICRM_INTEGRATION_URL', plugin_dir_url( __FILE__ ) ); define( 'CF_CIVICRM_INTEGRATION_PATH', plugin_dir_path( __FILE__ ) ); @@ -137,6 +137,15 @@ class CiviCRM_Caldera_Forms { */ public $html; + /** + * CiviDiscount helper object. + * + * @since 1.0 + * @access public + * @var object $cividiscount The CiviDiscount helper object + */ + public $cividiscount; + /** * Returns a single instance of this object when called. * @@ -183,7 +192,7 @@ public static function instance() { private function check_dependencies() { // Bail if Caldera Forms is not available - if ( ! defined( 'CFCORE_VER' ) || ! version_compare( CFCORE_VER, '1.7', '>=' ) ) { + if ( ! defined( 'CFCORE_VER' ) || ! version_compare( CFCORE_VER, '1.8.1', '>=' ) ) { add_action( 'admin_notices', [$this, 'caldera_forms_version_notice'] ); return false; } @@ -228,6 +237,8 @@ private function include_files() { include CF_CIVICRM_INTEGRATION_PATH . 'includes/class-civicrm-caldera-forms-transient.php'; // Include html class include CF_CIVICRM_INTEGRATION_PATH . 'includes/class-civicrm-caldera-forms-html.php'; + // include CiviDiscount helper class + include CF_CIVICRM_INTEGRATION_PATH . 'includes/class-civicrm-caldera-forms-cividiscount.php'; } @@ -260,6 +271,9 @@ private function setup_objects() { $this->assets = new CiviCRM_Caldera_Forms_Assets( $this ); // init html class $this->html = new CiviCRM_Caldera_Forms_HTML( $this ); + // init cividiscount class + if ( $this->processors->enabled_extensions && in_array( 'org.civicrm.module.cividiscount', $this->processors->enabled_extensions ) ) + $this->cividiscount = new CiviCRM_Caldera_Forms_CiviDiscount( $this ); } @@ -283,7 +297,7 @@ private function register_hooks() { public function caldera_forms_version_notice() { ?>
-

+

'Organization', - 'organization_name' => $mapped_field, - ] ); - } else { - $employer = civicrm_api3( 'Contact', 'get', [ - 'contact_id' => $mapped_field, - 'return' => 'organization_name' - ] ); - } - return $employer['values'][$employer['id']]['organization_name']; + if ( $field['type'] != 'civicrm_contact_reference' ) return $mapped_field; + + if ( $civi_field != 'current_employer' ) return $mapped_field; + + if ( ! is_numeric( $mapped_field ) && isset( $field['config']['new_organization'] ) ) { + $employer = civicrm_api3( 'Contact', 'create', [ + 'contact_type' => 'Organization', + 'organization_name' => $mapped_field, + ] ); + } else { + $employer = civicrm_api3( 'Contact', 'get', [ + 'contact_id' => $mapped_field, + 'return' => 'organization_name' + ] ); } + if ( isset( $employer['count'] ) && $employer['count'] ) + return [ + 'organization_name' => $employer['values'][$employer['id']]['organization_name'], + 'employer_id' => $employer['id'] + ]; + return $mapped_field; } @@ -131,10 +138,20 @@ public function handle_current_employer_field( $mapped_field, $civi_field, $fiel * @param array $config processor config */ public function pre_render_current_employer_value( $value, $civi_field, $field, $entity, $config ) { - if ( $civi_field == 'current_employer' && $field['type'] == 'civicrm_contact_reference' ) { - $employer = civicrm_api3( 'Contact', 'get', [ 'contact_type' => 'Organization', 'organization_name' => $entity[$civi_field] ] ); - return $employer['id']; - } + + if ( $field['type'] != 'civicrm_contact_reference' ) return $value; + + if ( $civi_field != 'current_employer' ) return $value; + + $employer = civicrm_api3( 'Contact', 'get', [ + 'contact_type' => 'Organization', + 'organization_name' => $entity[$civi_field], + 'return' => 'organization_name', + 'options' => [ 'limit' => 1 ] + ] ); + + if ( isset( $employer['count'] ) && $employer['count'] ) return $employer['id']; + return $value; } diff --git a/fields/civicrm_premium/class-civicrm-premium.php b/fields/civicrm_premium/class-civicrm-premium.php new file mode 100644 index 0000000..353f15b --- /dev/null +++ b/fields/civicrm_premium/class-civicrm-premium.php @@ -0,0 +1,150 @@ +plugin = $plugin; + // register Caldera Forms callbacks + $this->register_hooks(); + + } + + /** + * Register hooks. + * + * @since 1.0 + */ + public function register_hooks() { + + // add custom fields to Caldera UI + add_filter( 'caldera_forms_get_field_types', [ $this, 'register_field_type' ] ); + // add classes + add_filter( 'caldera_forms_render_field_classes_type-' . $this->key_name, [ $this, 'add_classes' ], 10, 3 ); + + add_filter( 'caldera_forms_render_get_field', [ $this, 'filter_field_config' ], 10, 2 ); + + } + + /** + * Adds the field definition for this field type to Caldera UI. + * + * @uses 'caldera_forms_get_field_types' filter + * + * @since 1.0 + * + * @param array $field_types The existing fields configuration + * @return array $field_types The modified fields configuration + */ + public function register_field_type( $field_types ) { + + $field_types[$this->key_name] = [ + 'field' => __( 'CiviCRM Premium', 'caldera-forms-civicrm' ), + 'file' => CF_CIVICRM_INTEGRATION_PATH . 'fields/civicrm_premium/field.php', + 'category' => __( 'CiviCRM', 'caldera-forms-civicrm' ), + 'description' => __( 'CiviCRM Premiums for Order processors', 'caldera-forms-civicrm' ), + 'setup' => [ + 'template' => CF_CIVICRM_INTEGRATION_PATH . 'fields/civicrm_premium/config.php', + 'preview' => CF_CIVICRM_INTEGRATION_PATH . 'fields/civicrm_premium/preview.php', + 'default' => [ + 'active_class' => 'btn-success', + 'default_class' => 'btn-default', + 'no_thanks' => 'No thank you' + ] + ], + // borough styles form CF's toggle_switch field + 'styles' => [ + CFCORE_URL . 'fields/toggle_switch/css/setup.css', + CFCORE_URL . 'fields/toggle_switch/css/toggle.css' + ], + 'scripts' => [ + CF_CIVICRM_INTEGRATION_URL . 'fields/civicrm_premium/js/premium.js' + ] + ]; + + return $field_types; + + } + + /** + * Add cf-toggle-switch class. + * + * @since 1.0 + * @param array $field_classes The classes array + * @param array $field The field config + * @param array $form The form config + */ + public function add_classes( $field_classes, $field, $form ) { + $field_classes['control_wrapper'][] = 'cf-toggle-switch'; + return $field_classes; + } + + /** + * Filter this field type and adds premium/product data to it's config. + * + * @since 1.0 + * @param array $field The field config + * @param array $form The form config + * @return array $field The filtered field + */ + public function filter_field_config( $field, $form ) { + + if ( $field['type'] != $this->key_name ) return $field; + + if ( isset( $field['config']['premium_id'] ) ) + $premium = civicrm_api3( 'Product', 'getsingle', [ 'id' => $field['config']['premium_id'] ] ); + + if ( ! $premium ) return $field; + + $premium_config = [ + 'name' => $premium['name'], + 'desc' => $premium['description'], + 'image' => $premium['image'] ? $premium['image'] : false, + 'thumbnail' => $premium['thumbnail'] ? $premium['thumbnail'] : false, + 'min' => sprintf( __( 'Minimum: %s', 'caldera-forms-civicrm' ), $premium['min_contribution'] ), + 'min_clean' => $premium['min_contribution'], + 'options' => $premium['options'] ? $this->to_array( $premium['options'] ) : false + ]; + + $field['config'] = array_merge( $field['config'], $premium_config ); + + return $field; + } + + /** + * Format product/premium options. + * + * @since 1.0 + * @param string $options Comma separated values + * @return array $options The options array + */ + public function to_array( string $options ) { + $options = preg_replace( '/\s+/', '', $options ); + return explode( ',', $options ); + } + +} diff --git a/fields/civicrm_premium/config.php b/fields/civicrm_premium/config.php new file mode 100644 index 0000000..c333773 --- /dev/null +++ b/fields/civicrm_premium/config.php @@ -0,0 +1,150 @@ + +
+ +
+ +
+

+ +

+
+ + +
+ +
+ +
+

+ +

+
+ + +
+ +
+ +
+

+ +

+
+ + +
+ +
+ +
+

+ +

+
+ + +
+ +
+ +
+
+ +
+ +
+ +
+
+ + +
+ +
+ {{{_field slug="calc"}}} +
+

+ +

+
+ + + + diff --git a/fields/civicrm_premium/field.php b/fields/civicrm_premium/field.php new file mode 100644 index 0000000..2233c27 --- /dev/null +++ b/fields/civicrm_premium/field.php @@ -0,0 +1,148 @@ + 0 ? 'disabled="true"' : 'disabled="false"'; + $default = $field['config']['default']; +?> + + + +
+ + + +
+
+ +
+ <?php echo esc_attr( $field['config']['name'] ) ?> +
+ +
+
+

+ + +

+
+
+
+ +
+ +
+ + + + + +after( $script_template, $field[ 'grid_location' ] ); + } else { + echo $script_template; + } \ No newline at end of file diff --git a/fields/civicrm_premium/js/premium.js b/fields/civicrm_premium/js/premium.js new file mode 100644 index 0000000..900b225 --- /dev/null +++ b/fields/civicrm_premium/js/premium.js @@ -0,0 +1,22 @@ +jQuery( function( $ ) { + $( 'body' ).on( 'click', '.cf-toggle-group-premium a', function(){ + + var clicked = $( this ), + parent = clicked.closest( '.caldera-config-field' ), + input = parent.find( '[data-ref="' + clicked.attr( 'id' ) + '"]' ), + premium_wrapper = parent.find( '.premium-wrapper' ); + + parent.find( '.btn' ).removeClass( clicked.data( 'active' ) ).addClass( clicked.data( 'default' ) ); + clicked.addClass( clicked.data( 'active' ) ).removeClass( clicked.data( 'default' ) ); + input.prop( 'checked', true ).trigger( 'change' ); + + if ( clicked.attr( 'id' ).indexOf( 'premium' ) !== -1 ) { + premium_wrapper.find( '.premium-mini' ).hide(); + premium_wrapper.find( '.premium-full' ).show(); + } else { + premium_wrapper.find( '.premium-mini' ).show(); + premium_wrapper.find( '.premium-full' ).hide(); + } + + } ); +} ); \ No newline at end of file diff --git a/fields/civicrm_premium/preview.php b/fields/civicrm_premium/preview.php new file mode 100644 index 0000000..9bef662 --- /dev/null +++ b/fields/civicrm_premium/preview.php @@ -0,0 +1,7 @@ +
+ {{#unless hide_label}}{{label}}{{#if required}} *{{/if}}{{/unless}} +
+ + {{caption}} +
+
\ No newline at end of file diff --git a/fields/civicrm_state/field.php b/fields/civicrm_state/field.php index 31e9d1c..a01ea78 100644 --- a/fields/civicrm_state/field.php +++ b/fields/civicrm_state/field.php @@ -7,7 +7,7 @@ - > plugin = $plugin; + // register Caldera Forms callbacks + $this->register_hooks(); + + } + + /** + * Register hooks. + * + * @since 1.0 + */ + public function register_hooks() { + + // add custom fields to Caldera UI + add_filter( 'caldera_forms_get_field_types', [ $this, 'register_field_type' ] ); + add_filter( 'caldera_forms_render_get_form', [ $this, 'localize_scripts' ], 10 ); + + } + + /** + * Adds the field definition for this field type to Caldera UI. + * + * @uses 'caldera_forms_get_field_types' filter + * + * @since 1.0 + * + * @param array $field_types The existing fields configuration + * @return array $field_types The modified fields configuration + */ + public function register_field_type( $field_types ) { + + $field_types[$this->key_name] = [ + 'field' => __( 'CiviCRM Discount', 'caldera-forms-civicrm' ), + 'file' => CF_CIVICRM_INTEGRATION_PATH . 'fields/discount/field.php', + 'category' => __( 'CiviCRM', 'caldera-forms-civicrm' ), + 'description' => __( 'CiviCRM Discount field (CiviDiscount integration)', 'caldera-forms-civicrm' ), + 'setup' => [ + 'template' => CF_CIVICRM_INTEGRATION_PATH . 'fields/discount/config.php', + 'preview' => CF_CIVICRM_INTEGRATION_PATH . 'fields/discount/preview.php', + 'default' => [ + 'placeholder' => __( 'Discount Code', 'caldera-forms-civicrm' ), + 'button_text' => __( 'Apply Discount', 'caldera-forms-civicrm' ), + ], + ], + 'scripts' => [ + CF_CIVICRM_INTEGRATION_URL . 'fields/discount/js/cividiscount.js' + ] + ]; + + return $field_types; + + } + + /** + * Localize scripts + * + * @since 1.0 + * @param array $form Form config + * @return array $form Form config + */ + public function localize_scripts( $form ) { + + if ( Caldera_Forms_Field_Util::has_field_type( $this->key_name, $form ) ) + wp_localize_script( 'cf-cividiscountjs', 'cfc', [ 'url' => admin_url( 'admin-ajax.php' ) ] ); + + return $form; + } + +} diff --git a/fields/discount/config.php b/fields/discount/config.php new file mode 100644 index 0000000..c2ba262 --- /dev/null +++ b/fields/discount/config.php @@ -0,0 +1,12 @@ +
+ +
+ +
+
+
+ +
+ +
+
\ No newline at end of file diff --git a/fields/discount/field.php b/fields/discount/field.php new file mode 100644 index 0000000..ca0930f --- /dev/null +++ b/fields/discount/field.php @@ -0,0 +1,86 @@ + + + + + + + + + + + +append( $script_template, $field[ 'grid_location' ] ); + } else { + echo $script_template; + } diff --git a/fields/discount/js/cividiscount.js b/fields/discount/js/cividiscount.js new file mode 100644 index 0000000..f23e764 --- /dev/null +++ b/fields/discount/js/cividiscount.js @@ -0,0 +1,16 @@ +jQuery( document ).on( 'cfc.discount.apply', function ( event, data ) { + + var state = cfstate[data.form_id]; + + for ( option_id in data.options ) { + + var option = data.options[option_id], + field_id = option.field_id + '_' + data.instance; + + state.mutateState( field_id, option.value ); + + state.rebind( field_id ); + + } + +} ); diff --git a/fields/discount/preview.php b/fields/discount/preview.php new file mode 100644 index 0000000..c42eb11 --- /dev/null +++ b/fields/discount/preview.php @@ -0,0 +1,8 @@ +
+ {{#unless hide_label}}{{label}}{{#if required}} *{{/if}}{{/unless}} +
+ + + {{caption}} +
+
\ No newline at end of file diff --git a/fields/presets/class-civicrm-custom-fields-presets.php b/fields/presets/class-civicrm-custom-fields-presets.php index 2f37622..40fdee4 100644 --- a/fields/presets/class-civicrm-custom-fields-presets.php +++ b/fields/presets/class-civicrm-custom-fields-presets.php @@ -21,7 +21,7 @@ class CiviCRM_Caldera_Forms_Custom_Fields_Presets { * @access public * @var array $processors The custom fields data array */ - public $custom_fields_data = []; + public $custom_fields = []; /** * Allowed CiviCRM field tyes. @@ -32,6 +32,15 @@ class CiviCRM_Caldera_Forms_Custom_Fields_Presets { */ public $allowed_html_types = [ 'Select', 'Radio', 'CheckBox', 'Multi-Select', 'AdvMulti-Select' ]; + /** + * The entites the custom fields extend. + * + * @since 1.0 + * @access public + * @var array The entities + */ + public $extend_entities = []; + /** * Initialises this object. * @@ -73,38 +82,34 @@ public function register_hooks() { public function custom_fields_options_presets( $presets ) { // get all custom fields - $customFields = $this->custom_fields_data_get(); - - $custom = []; - if ( $customFields && ! $customFields['is_error'] && $customFields['count'] != 0 ) { - foreach ( $customFields['values'] as $key => $field ) { - if ( in_array( $field['html_type'], $this->allowed_html_types ) && isset( $field['option_group_id'] ) && ! empty( $field['option_group_id'] ) ) { - // get custom group - $params['id'] = $field['custom_group_id']; - $customGroup = []; - CRM_Core_BAO_CustomGroup::retrieve( $params, $customGroup ); - - // get options - $customOptions = CRM_Core_OptionGroup::valuesByID( (int)$field['option_group_id'] ); - - // contact types and activity for filtering - $extends = array_merge( [ 'Contact', 'Activity' ], CRM_Contact_BAO_ContactType::basicTypes(), CRM_Contact_BAO_ContactType::subTypes() ); - - if ( in_array( $customGroup['extends'], $extends ) ) { - $options = []; - foreach ( $customOptions as $key => $value ) { - $options[] = $key.'|'.$value; - } - $custom[$field['name']] = [ - 'name' => sprintf( __( 'CiviCRM - %1$s - %2$s', 'caldera-forms-civicrm' ), $customGroup['title'], $field['label'] ), - 'data' => $options, - ]; - } - } - } - } - - $presets = array_merge( $custom, $presets ); + $custom_fields = $this->custom_fields_get(); + + if ( ! $custom_fields ) return $presets; + + $extends = $this->entities_extend_get(); + + array_map( function( $field ) use ( &$presets, $extends ) { + + if ( ! in_array( $field['html_type'], $this->allowed_html_types ) ) return; + + if ( ! in_array( $field['custom_group_id.extends'], $extends ) ) return; + + if ( ! $field['option_group_id'] ) return; + + $custom_options = $this->option_values_get( $field['option_group_id'] ); + + if ( ! $custom_options ) return; + + $presets['custom_' . $field['id']] = [ + 'name' => sprintf( __( 'CiviCRM - %1$s - %2$s', 'caldera-forms-civicrm' ), $field['custom_group_id.title'], $field['label'] ), + 'data' => array_reduce( $custom_options, function( $options, $option ) { + $options[] = $option['value'] . '|' . $option['label']; + return $options; + }, [] ) + ]; + + }, $custom_fields ); + return $presets; } @@ -120,25 +125,23 @@ public function custom_fields_options_presets( $presets ) { public function autopopulate_custom_fields_types() { // get all custom fields - $customFields = $this->custom_fields_data_get(); - - if ( $customFields && !$customFields['is_error'] && $customFields['count'] != 0 ) { - - $custom = []; - foreach ( $customFields['values'] as $key => $field ) { - if ( in_array( $field['html_type'], $thia->allowed_html_types ) && isset( $field['option_group_id'] ) && ! empty( $field['option_group_id'] ) ) { - // get custom group - $params['id'] = $field['custom_group_id']; - $customGroup = []; - CRM_Core_BAO_CustomGroup::retrieve( $params, $customGroup ); - - $extends = array_merge( [ 'Contact', 'Activity' ], CRM_Contact_BAO_ContactType::basicTypes(), CRM_Contact_BAO_ContactType::subTypes() ); - if ( in_array( $customGroup['extends'], $extends ) ) { - echo ""; - } - } - } - } + $custom_fields = $this->custom_fields_get(); + + if ( ! $custom_fields ) return; + + $extends = $this->entities_extend_get(); + + array_map( function( $field ) use ( $extends ) { + + if ( ! in_array( $field['html_type'], $this->allowed_html_types ) ) return; + + if ( ! in_array( $field['custom_group_id.extends'], $extends ) ) return; + + if ( ! $field['option_group_id'] ) return; + + echo ""; + + }, $custom_fields ); } @@ -155,29 +158,32 @@ public function autopopulate_custom_fields_types() { */ public function autopopulate_custom_fields_values( $field, $form ) { - if ( ! empty( $field['config']['auto'] ) ) { - - // get all custom fields - $customFields = $this->custom_fields_data_get(); - - if ( $customFields && ! $customFields['is_error'] && $customFields['count'] != 0 ) { - - foreach ( $customFields['values'] as $key => $civiField ) { - if ( in_array( $civiField['html_type'], $this->allowed_html_types ) && isset( $field['option_group_id'] ) && ! empty( $field['option_group_id'] ) ) { - switch ( $field['config']['auto_type'] ) { - case 'custom_' . $civiField['id']: - $customOptions = CRM_Core_OptionGroup::valuesByID( (int)$civiField['option_group_id'] ); - foreach ( $customOptions as $key => $value ) { - $field['config']['option'][$key] = [ - 'value' => $key, - 'label' => $value - ]; - } - break; - } - } - } - } + if ( ! isset( $field['config']['auto'] ) ) return $field; + + if ( strpos( $field['config']['auto_type'], 'custom_' ) === false ) return $field; + + // it's a custom field, get all custom fields + $custom_fields = $this->custom_fields_get(); + + if ( ! $custom_fields ) return $field; + + foreach ( $custom_fields as $key => $custom_field ) { + + if ( ! in_array( $custom_field['html_type'], $this->allowed_html_types ) && ! isset( $custom_field['option_group_id'] ) ) continue; + + if ( $field['config']['auto_type'] !== 'custom_' . $custom_field['id'] ) continue; + + $custom_options = $this->option_values_get( $custom_field['option_group_id'] ); + + if ( ! $custom_options ) continue; + + $field['config']['option'] = array_reduce( $custom_options, function( $options, $option ) { + $options[$option['value']] = [ + 'value' => $option['value'], + 'label' => $option['label'] + ]; + return $options; + }, [] ); } @@ -190,20 +196,64 @@ public function autopopulate_custom_fields_values( $field, $form ) { * * @since 0.2 * - * @return array $presets The modified presets + * @return array|bool $custom_fields The custom fields, or false */ - public function custom_fields_data_get() { + public function custom_fields_get( ) { // return data if it's already retrieved - if ( ! empty( $this->custom_fields_data ) ) return $this->custom_fields_data; + if ( ! empty( $this->custom_fields ) ) return $this->custom_fields; // get all custom fields - $this->custom_fields_data = civicrm_api3( 'CustomField', 'get', [ + $custom_fields = civicrm_api3( 'CustomField', 'get', [ 'sequential' => 1, - 'options' => [ 'limit' => 0 ], 'is_active' => 1, + 'return' => [ 'name', 'label', 'custom_group_id', 'option_group_id', 'html_type', 'custom_group_id.extends', 'custom_group_id.title' ], + 'options' => [ 'limit' => 0 ], ] ); + + if ( ! is_array( $custom_fields ) && ! $custom_fields['count'] ) return false; + + // get option values + // $option_group_ids = array_column( $custom_fields['values'], 'option_group_id' ); + + $this->custom_fields = $custom_fields['values']; + + return $this->custom_fields; + + } + + /** + * Get option values for a given option group. + * + * @since 1.0 + * @param int $option_group_id The option group id + * @return array|false $option_values The option values, or false + */ + public function option_values_get( $option_group_id ) { + + $option_values = civicrm_api3( 'OptionValue', 'get', [ + 'sequential' => 1, + 'option_group_id' => $option_group_id + ] ); + + if ( $option_values['count'] && ! $option_values['is_error'] ) return $option_values['values']; + + return false; + + } + + /** + * Get extend entities to return custom fields for autopopulation and presets. + * + * @since 1.0 + * @return array $extends The entites + */ + public function entities_extend_get() { + + $extends = array_merge( [ 'Contact', 'Activity' ], CRM_Contact_BAO_ContactType::basicTypes(), CRM_Contact_BAO_ContactType::subTypes() ); + + return apply_filters( 'cfc_custom_fields_extends_entities', $extends ); } } diff --git a/fields/presets/class-civicrm-price-sets-presets.php b/fields/presets/class-civicrm-price-sets-presets.php index 18296de..9124149 100644 --- a/fields/presets/class-civicrm-price-sets-presets.php +++ b/fields/presets/class-civicrm-price-sets-presets.php @@ -23,6 +23,15 @@ class CiviCRM_Caldera_Forms_Price_Sets_Presets { */ public $price_sets; + /** + * Disable all fields flag. + * + * @since 1.0 + * @access public + * @var boolean $disable_all_fields + */ + public $disable_all_fields = false; + /** * Initialises this object. * @@ -48,7 +57,10 @@ public function register_hooks() { // auto-populate Price Fields add_action( 'caldera_forms_autopopulate_types', [ $this, 'autopopulate_price_field_types' ] ); - add_filter( 'caldera_forms_render_get_field', [ $this, 'autopopulate_price_field_values' ], 20, 2 ); + add_filter( 'caldera_forms_render_get_field', [ $this, 'autopopulate_price_field_values' ], 10, 2 ); + add_filter( 'caldera_forms_render_setup_field', [ $this, 'autopopulate_price_field_values' ], 10, 2 ); + + add_filter( 'caldera_forms_render_field_structure', [ $this, 'autopopulate_price_field_values' ], 10, 2 ); } @@ -78,7 +90,7 @@ public function price_field_options_presets( $presets ) { ]; } } - $presets = array_merge( $price_fields, $presets ); + $presets = array_merge( $presets, $price_fields ); } return $presets; @@ -113,26 +125,163 @@ public function autopopulate_price_field_types() { */ public function autopopulate_price_field_values( $field, $form ) { - if ( $this->price_sets ) { - if ( ! empty( $field['config']['auto'] ) ) { - foreach ( $this->price_sets as $price_set_id => $price_set ) { - foreach ( $price_set['price_fields'] as $price_field_id => $price_field ) { - if( $field['config']['auto_type'] == 'cfc_price_field_' . $price_field_id ) { - foreach ( $price_field['price_field_values'] as $value_id => $price_field_value) { - $field['config']['option'][$value_id] = [ - 'value' => $value_id, - 'label' => $price_field_value['label'] . ' - ' . $field['config']['price_field_currency'] . ' ' . $price_field_value['amount'], - 'calc_value' => $price_field_value['amount'] - ]; - } - } - } - } + // filter field structure + if ( current_filter() == 'caldera_forms_render_field_structure' ) + $field = $this->filter_price_field_structure( $field, $form ); + + // disable field options + if ( $this->disable_all_fields ) + $field = $this->disable_all_price_field_options( $field ); + + if ( ! $this->is_price_field_field( $field, $form ) ) return $field; + + /** + * if we reach here, current $field is a 'price_field' field + */ + $price_field = $this->get_price_field_from_config( $field ); + + // remove field if not active + if ( ! $this->is_price_field_active( $price_field ) ) + return false; + + // populate field options + $field['config']['option'] = array_reduce( $price_field['price_field_values'], function( $options, $price_field_value ) use ( $field ) { + + $option = [ + 'value' => $price_field_value['id'], + 'label' => sprintf( '%1$s - %2$s', $price_field_value['label'], $this->plugin->helper->format_money( $price_field_value['amount'] ) ), + 'calc_value' => $price_field_value['amount'], + 'disabled' => $this->disable_all_fields + ]; + + if ( isset( $price_field_value['tax_amount'] ) && $this->plugin->helper->get_tax_settings()['invoicing'] ) { + $option['calc_value'] += $price_field_value['tax_amount']; + $option['label'] = $this->plugin->helper->format_tax_label( $price_field_value['label'], $price_field_value['amount'], $price_field_value['tax_amount'] ); } - } + + $options[$price_field_value['id']] = $option; + return $options; + + }, [] ); + + /** + * Filter autopopulated price fields. + * + * Triggers for each autopopualted price field at both config and setup stages, + * uses both 'caldera_forms_render_get_field' and 'caldera_forms_render_setup_field' filters. + * + * @since 1.0 + * @param array $field The field config + * @param array $form The form config + * @param array $price_field The price field and it's price_field_values + * @param string $current_filter The current filter + */ + $field = apply_filters( 'cfc_filter_price_field_config', $field, $form, $price_field, $current_filter = current_filter() ); return $field; } -} + /** + * Filter autopopulated price field's field structure. + * + * @since 1.0 + * @param array $field The field structure + * @param array $form The form config + * @return array $field The filtered field + */ + public function filter_price_field_structure( $field, $form ) { + + if ( empty( $field['field']['config']['auto'] ) ) return $field; + + if ( strpos( $field['field']['config']['auto_type'], 'cfc_price_field_' === false ) ) return $field; + /** + * if we reach here, current $field is a 'price_field' field + */ + $price_field = $this->get_price_field_from_config( $field['field'] ); + + /** + * Chance to alter autopopulated price fields. + * + * @since 1.0 + * @param array $field The field config + * @param array $form The form config + * @param array $price_field The price field and it's price_field_values + */ + $field = apply_filters( 'cfc_filter_price_field_structure', $field, $form, $price_field ); + + return $field; + } + + /** + * Check if price field is active based on active_on/expire_on. + * + * @since 1.0 + * @param array $price_field The price field + * @return boolean $is_active Whether is active or not + */ + public function is_price_field_active( $price_field ) { + + $now = date( 'Y-m-d H:m:i' ); + $active_on = isset( $price_field['active_on'] ) && array_key_exists( 'active_on', $price_field ) ? $price_field['active_on'] : $now; + $expire_on = isset( $price_field['expire_on'] ) && array_key_exists( 'expire_on', $price_field ) ? $price_field['expire_on'] : $now; + + return ( $now >= $active_on ) && ( $now <= $expire_on ); + + } + + /** + * Get price field config from field config. + * + * @since 1.0 + * @param array $field Field config + * @return array $price_field Price field config + */ + public function get_price_field_from_config( $field ) { + $price_field_id = ( int ) str_replace( 'cfc_price_field_', '', $field['config']['auto_type'] ); + return $this->plugin->helper->get_price_set_column_by_id( $price_field_id, 'price_field' ); + } + + /** + * Check if field is a price field. + * + * @since 1.0 + * @param array $field Field config + * @param array $form Form config + * @return boolean $is_price_field_field Whether the field is a price field or not + */ + public function is_price_field_field( $field, $form ) { + + if ( empty( $field['config']['auto'] ) ) return false; + + if ( strpos( $field['config']['auto_type'], 'cfc_price_field_' ) === false ) return false; + + if ( ! $this->price_sets ) return false; + + return true; + } + + /** + * Disable all field's options. + * + * @since 1.0 + * @param array $field The field config + * @return array $field The field config + */ + public function disable_all_price_field_options( $field ) { + + if ( ! isset( $field['config']['option'] ) ) return $field; + + if ( ! in_array( $field['ID'], $this->plugin->processors->processors['participant']->price_field_refs ) ) return $field; + + array_map( function( $option_id ) use ( &$field ) { + + $field['config']['option'][$option_id]['disabled'] = $this->disable_all_fields; + + }, array_keys( $field['config']['option'] ) ); + + return $field; + + } + +} diff --git a/includes/class-civicrm-caldera-forms-ajax.php b/includes/class-civicrm-caldera-forms-ajax.php index 7bfaf0c..1a4c2aa 100644 --- a/includes/class-civicrm-caldera-forms-ajax.php +++ b/includes/class-civicrm-caldera-forms-ajax.php @@ -36,7 +36,11 @@ public function register_hooks() { add_action( 'wp_ajax_flush_price_set_cache', [ $this, 'flush_price_set_cache' ] ); add_action( 'wp_ajax_civicrm_contact_reference_get', [ $this, 'civicrm_contact_reference_get' ] ); add_action( 'wp_ajax_nopriv_civicrm_contact_reference_get', [ $this, 'civicrm_contact_reference_get' ] ); - + // event code discount + add_action( 'wp_ajax_do_code_cividiscount', [ $this, 'do_code_cividiscount' ] ); + add_action( 'wp_ajax_nopriv_do_code_cividiscount', [ $this, 'do_code_cividiscount' ] ); + // premiums + add_action( 'wp_ajax_civicrm_get_premiums', [ $this, 'civicrm_get_premiums' ] ); } /** @@ -155,4 +159,47 @@ public function flush_price_set_cache() { } die; } + + public function do_code_cividiscount() { + + if ( ! wp_verify_nonce( $_POST['nonce'], 'civicrm_cividiscount_code' ) ) return; + if ( isset( $_POST['cividiscount_code'] ) ) $code = $_POST['cividiscount_code']; + if ( isset( $_POST['form_id'] ) ) $form_id = $_POST['form_id']; + if ( isset( $_POST['form_id_attr'] ) ) $form_id_attr = $_POST['form_id_attr']; + + $discount = $this->plugin->cividiscount->get_by_code( $code ); + + if ( $discount ) { + // form config + $form = Caldera_Forms::get_form( $form_id ); + // add count + $form['form_count'] = str_replace( $form['ID'].'_', '', $form_id_attr ); + + $discounted_options = $this->plugin->cividiscount->do_code_discount( $discount, $form ); + + } + + echo json_encode( $discounted_options ); + die; + } + + public function civicrm_get_premiums() { + if ( isset( $_POST['search'] ) ) $search_term = $_POST['search']; + if ( isset( $_POST['premium_id'] ) ) $premium_id = $_POST['premium_id']; + + if ( ! wp_verify_nonce( $_POST['nonce'], 'admin_get_premiums' ) ) return; + + $params = [ + 'sequential' => 1, + 'is_active' => 1, + ]; + + if ( isset( $premium_id ) ) $params['id'] = $premium_id; + if ( isset( $search_term ) && ! empty( $search_term ) ) $params['name'] = [ 'LIKE' => '%' . $search_term . '%' ]; + + $premiums = civicrm_api3( 'Product', 'get', $params ); + + echo json_encode( $premiums['values'] ); + die; + } } diff --git a/includes/class-civicrm-caldera-forms-assets.php b/includes/class-civicrm-caldera-forms-assets.php index 53ccd53..cb8d967 100644 --- a/includes/class-civicrm-caldera-forms-assets.php +++ b/includes/class-civicrm-caldera-forms-assets.php @@ -35,6 +35,8 @@ public function register_hooks() { // enqueue scripts and js in form editor add_action( 'caldera_forms_admin_assets_scripts_registered', [ $this, 'enqueue_civicrm_scripts' ] ); add_action( 'caldera_forms_admin_assets_styles_registered', [ $this, 'enqueue_civicrm_styles' ] ); + // enqueue late in editor + add_action( 'caldera_forms_editor_footer', [ $this, 'enqueue_in_editor_footer' ] ); } /** @@ -48,6 +50,8 @@ public function register_scripts_and_styles() { wp_register_style( 'cfc-select2', CF_CIVICRM_INTEGRATION_URL . 'assets/css/select2.min.css', [], CF_CIVICRM_INTEGRATION_VER ); // admin script wp_register_script( 'cfc-admin', CF_CIVICRM_INTEGRATION_URL . 'assets/js/admin.js', [ 'jquery' ], CF_CIVICRM_INTEGRATION_VER ); + // auto populate price field conditional options + wp_register_script( 'cfc-autopop-conditionals', CF_CIVICRM_INTEGRATION_URL . 'assets/js/autopop_conditionals.js', [ 'jquery' ], CF_CIVICRM_INTEGRATION_VER ); // frontend script wp_register_script( 'cfc-front', CF_CIVICRM_INTEGRATION_URL . 'assets/js/front.js', [ 'jquery' ], CF_CIVICRM_INTEGRATION_VER ); } @@ -85,6 +89,15 @@ public function enqueue_civicrm_styles(){ } + /** + * Enqueue conditional script in editor footer. + * + * @since 1.0 + */ + public function enqueue_in_editor_footer() { + wp_enqueue_script( 'cfc-autopop-conditionals' ); + } + /** * Check if are in Caldera Forms admin context. * diff --git a/includes/class-civicrm-caldera-forms-cividiscount.php b/includes/class-civicrm-caldera-forms-cividiscount.php new file mode 100644 index 0000000..c99cdb7 --- /dev/null +++ b/includes/class-civicrm-caldera-forms-cividiscount.php @@ -0,0 +1,514 @@ + => ] + */ + public $event_cividiscounts; + + /** + * CiviDiscount criteria/filters, + * whether the criteria is met for a processor_id. + * + * @since 1.0 + * @access public + * @var array $event_autodiscounts Reference to [ => true|false ] + */ + public $event_autodiscounts; + + /** + * CiviDiscount criteria/filters, + * whether the criteria is met for a processor_id. + * + * @since 1.0 + * @access public + * @var array $event_autodiscounts Reference to [ => true|false ] + */ + public $options_ids_refs; + + /** + * Initialises this object. + * + * @since 1.0 + */ + public function __construct( $plugin ) { + $this->plugin = $plugin; + } + + /** + * Get CiviDiscounts. + * + * @since 1.0 + * @return array|bool $cividiscounts + */ + public function get_cividiscounts() { + + if ( is_array( $this->cividiscounts ) ) return $this->cividiscounts; + + $discounts = civicrm_api3( 'DiscountCode', 'get', [ + 'is_active' => 1, + 'options' => [ 'limit' => 0 ] + ] ); + + if ( $discounts['count'] && ! $discounts['is_error'] ) { + $discounts = array_map( function( $discount ) { + $discount['autodiscount'] = json_decode( $discount['autodiscount'], true ); + return $discount; + }, $discounts['values'] ); + + // filter discounts by date + $discounts = array_filter( $discounts, [ $this, 'is_discount_active' ] ); + $this->cividiscounts = $discounts; + return $this->cividiscounts; + } + + return false; + + } + + /** + * Get cividiscounts by entity. + * + * @since 1.0 + * @param string $entity_name The entity name, events, memberships, or pricesets + * @return array $cividiscounts + */ + public function get_cividiscounts_by_entity( $entity_name, $is_autodiscount = null ) { + + $discounts = $this->get_cividiscounts(); + + if ( ! $discounts ) return; + + return array_filter( $discounts, function( $discount ) use ( $entity_name, $is_autodiscount ) { + if ( $is_autodiscount === true ) { + return array_key_exists( $entity_name, $discount ) && ! empty( $discount['autodiscount'] ); + } elseif ( $is_autodiscount === false ) { + return array_key_exists( $entity_name, $discount ) && empty( $discount['autodiscount'] ); + } else { + return array_key_exists( $entity_name, $discount ); + } + } ); + + } + + /** + * Get CiviDiscounts for price field options. + * + * @since 1.0 + * @param array $event_ids The event ids to get discounts for + * @return array $event_cividiscounts Discounts per processors array + */ + public function get_event_cividiscounts( $event_ids ) { + + if ( is_array( $this->event_cividiscounts ) && ! empty( $this->event_cividiscounts ) ) return $this->event_cividiscounts; + + $event_cividiscounts = $this->get_cividiscounts_by_entity( 'events' ); + + if ( ! isset( $event_discounts ) ) return; + + $event_discounts = array_reduce( $event_ids, function( $discounts, $event_id ) use ( $event_ids, $event_cividiscounts ) { + + $processor_id = array_search( $event_id, $event_ids ); + + foreach ( $event_cividiscounts as $discount_id => $discount ) { + if ( in_array( $event_id, $discount['events'] ) ) { + $discounts[$processor_id] = $discount; + } + } + + return $discounts; + + }, [] ); + + $this->event_cividiscounts = $event_discounts; + + return $this->event_cividiscounts; + + } + + /** + * Build options ids for price fields. + * + * @since 1.0 + * @param array $form The form config + * @return array|boolean $options_ids_refs References to [ => ], or false + */ + public function build_options_ids_refs( $price_field_refs, $form = false ) { + + $discounted_options = $this->get_cividiscounts_by_entity( 'pricesets' ); + + if ( ! isset( $discounted_options ) ) return; + + $options_ids_refs = array_reduce( $price_field_refs, function( $refs, $field_id ) use ( $price_field_refs, $discounted_options, $form ) { + + $processor_id = array_search( $field_id, $price_field_refs ); + + $processor_id = $this->plugin->processors->processors['participant']->parse_processor_id( $processor_id ); + + $price_field_field = Caldera_Forms_Field_Util::get_field( $field_id, $form, $apply_filters = true ); + + if ( ! $price_field_field || ! isset( $price_field_field['ID'] ) ) return $refs; + + foreach ( $discounted_options as $discount_id => $discount ) { + if ( ! empty( array_intersect( $discount['pricesets'], array_keys( $price_field_field['config']['option'] ) ) ) ) + $refs[$field_id] = [ + 'field_id' => $field_id, + 'processor_id' => $processor_id, + 'field_options' => array_intersect( $discount['pricesets'], array_keys( $price_field_field['config']['option'] ) ) + ]; + } + + return $refs; + + }, [] ); + + $this->options_ids_refs = $options_ids_refs; + + return $options_ids_refs; + + } + + /** + * Get option based CiviDiscounts. + * + * @since 1.0 + * @param array $options_ids_refs Field id with references to the discunted options (price_field_value ids) + * @return array $options_cividiscounts The discounts + */ + public function get_options_cividiscounts( $options_ids_refs ) { + + if ( ! isset( $options_ids_refs ) ) return; + + if ( is_array( $this->options_cividiscounts ) && ! empty( $this->options_cividiscounts ) ) return $this->options_cividiscounts; + + $options_cividiscounts = $this->get_cividiscounts_by_entity( 'pricesets' ); + + if ( ! isset( $options_cividiscounts ) ) return; + + $discounts = []; + array_map( function( $field_id, $options ) use ( &$discounts, $options_ids_refs, $options_cividiscounts ) { + + foreach ( $options_cividiscounts as $discount_id => $discount ) { + if ( ! empty( array_intersect( $options['field_options'], $discount['pricesets'] ) ) ) { + $discounts[$field_id] = $discount; + break; + } + } + + }, array_keys( $options_ids_refs ), $options_ids_refs ); + + $this->options_cividiscounts = $discounts; + + return $this->options_cividiscounts; + + } + + /** + * Check autodiscount for a contact. + * + * @since 1.0 + * @param array $autodiscount CiviDiscount autodiscount (criteria/filter) property + * @param int $contact_id The contact id + * @param string $processor_id The praticipant processor id + * @return bool $is_autodiscount + */ + public function check_autodiscount( $autodiscount, $contact_id, $processor_id ) { + + if ( isset( $this->event_autodiscounts[$processor_id] ) ) return $this->event_autodiscounts[$processor_id]; + + $is_autodiscount = false; + + if ( ! empty( $autodiscount ) ) { + foreach ( $autodiscount as $entity => $params ) { + $params['contact_id'] = $contact_id; + try { + $result = civicrm_api3( $entity, 'getsingle', $params ); + if ( $result['count'] || isset( $result['id'] ) ) { + $is_autodiscount = true; + break; + } + } catch ( CiviCRM_API3_Exception $e ) { + + } + } + } + $this->event_autodiscounts[$processor_id] = $is_autodiscount; + + return $is_autodiscount; + } + + + /** + * Get discount by discount code. + * + * @since 1.0 + * @param string $code The discount code + * @return array $discount The discount + */ + public function get_by_code( $code ) { + + try { + $discount = civicrm_api3( 'DiscountCode', 'getsingle', [ + 'sequential' => 1, + 'code' => $code, + 'is_active' => 1 + ] ); + } catch ( CiviCRM_API3_Exception $e ) { + + } + + if ( $discount && ! $discount['is_error'] ) + return $discount; + + return false; + } + + /** + * Do option discount. + * + * @since 1.0 + * @param array $option The option config + * @param array $field The field config + * @param array $price_field_value The price field value + * @param array $event_discount The discount config + * @return array $option The filtered option config + */ + public function do_discounted_option( $option, $field, $price_field_value, $discount ) { + + $label = sprintf( __( '%1$s (Includes automatic discount of: ', 'caldera-forms-civicrm' ), $price_field_value['label'] ); + + // percentage discount + if ( $discount['amount_type'] == 1 ) { + $discounted_amount = $price_field_value['amount'] - $this->plugin->helper->calculate_percentage( $price_field_value['amount'], $discount['amount'] ); + $label .= $discount['amount'] . __( '%)', 'caldera-forms-civicrm' ); + } + // fixed discount + if ( $discount['amount_type'] == 2 ) { + $discounted_amount = $price_field_value['amount'] - $discount['amount']; + $label .= $this->plugin->helper->format_money( $discount['amount'] ) . __( ')', 'caldera-forms-civicrm' ); + } + // filtered option + $option = [ + 'value' => $price_field_value['id'], + 'label' => sprintf( '%1$s - %2$s', $label, $this->plugin->helper->format_money( $discounted_amount ) ), + 'calc_value' => $discounted_amount, + 'disabled' => $option['disabled'] + ]; + + add_filter( 'cfc_filter_price_field_value_get', function( $field_value, $field_value_id ) use ( $price_field_value, $discounted_amount, $label ) { + if ( ! is_array( $field_value_id ) && $field_value_id == $price_field_value['id'] ) { + $field_value['amount'] = $discounted_amount; + $field_value['label'] = $label; + } elseif ( is_array( $field_value_id ) ) { + if ( in_array( $price_field_value['id'], $field_value_id ) ) { + $field_value[$price_field_value['id']]['amount'] = $discounted_amount; + $field_value[$price_field_value['id']]['label'] = $label; + } + } + + return $field_value; + + }, 10, 2 ); + + // has tax + if ( isset( $price_field_value['tax_amount'] ) && $this->plugin->helper->get_tax_settings()['invoicing'] ) { + $option['calc_value'] += $price_field_value['tax_amount']; + $option['label'] = $this->plugin->helper->format_tax_label( $label, $discounted_amount, $price_field_value['tax_amount'] ); + } + + return $option; + } + + /** + * Do code discounts. + * + * @since 1.0 + * @param array $discount The discount config + * @param array $form The form config + * @return array $options The discounted field options + */ + public function do_code_discount( $discount, $form ) { + + if ( ! isset( $discount ) || empty( $discount ) ) return; + + $options = $this->do_code_event_discount_options( $discount, $form ); + $options = $this->do_code_options_discount_options( $discount, $form ); + + return $options; + } + + /** + * Do code discounts for event based CiviDiscounts. + * + * @since 1.0 + * @param array $discount The discount config + * @param array $form The forms config + * @return array $options The field options + */ + public function do_code_event_discount_options( $discount, $form ) { + + if ( ! $discount ) return; + + // participant processors + $participants = $this->plugin->helper->get_processor_by_type( 'civicrm_participant', $form ); + // price field references + $price_field_refs = $this->plugin->processors->processors['participant']->build_price_field_refs( $form ); + // options fields refs + $price_field_option_refs = $this->build_options_ids_refs( $price_field_refs, $form ); + // filter out option based discounts + $discounted_fields = array_filter( $price_field_refs, function( $field_id ) use ( $price_field_option_refs ) { + return ! array_key_exists( $field_id, $price_field_option_refs ); + } ); + // field configs + $fields = array_reduce( $discounted_fields, function( $fields, $field_id ) use ( $form ) { + $fields[$field_id] = Caldera_Forms_Field_Util::get_field( $field_id, $form, $apply_filters = true ); + return $fields; + }, [] ); + + return array_reduce( $discounted_fields, function( $discounted_options, $field_id ) use ( $fields, $form, $participants, $discount, $discounted_fields ) { + + $processor_id = array_search( $field_id, $discounted_fields ); + + if ( ! in_array( $participants[$processor_id]['config']['id'], $discount['events'] ) ) return $discounted_options; + + $field = $fields[$field_id]; + + $price_field = $this->plugin->fields->presets_objects['civicrm_price_sets']->get_price_field_from_config( $field ); + + // $field = $this->plugin->processors->processors['participant']->do_event_autodiscounts( $field, $form, $processor_id, $price_field ); + + $field['config']['option'] = array_reduce( $price_field['price_field_values'], function( $options, $price_field_value ) use ( $field, $discount ) { + + $option = $field['config']['option'][$price_field_value['id']]; + + // do discounted option + $options[$price_field_value['id']] = $this->do_discounted_option( $option, $field, $price_field_value, $discount ); + + return $options; + + }, [] ); + + $options = array_reduce( $field['config']['option'], function( $options, $option ) use ( $field_id, $field, $form ) { + $field_option_id = $field_id . '_' . $form['form_count'] . '_' . $option['value']; + $options[$field_option_id] = $field['config']['option'][$option['value']]; + $options[$field_option_id]['field_id'] = $field_id; + return $options; + }, [] ); + + return $discounted_options + $options; + + }, [] ); + } + + /** + * Do code discounts for options based CiviDiscounts. + * + * @since 1.0 + * @param array $discount The discount config + * @param array $form The forms config + * @return array $options The field options + */ + public function do_code_options_discount_options( $discount, $form ) { + + if ( ! $discount ) return; + + // price field references + $price_field_refs = $this->plugin->processors->processors['participant']->build_price_field_refs( $form ); + // options fields refs + $price_field_option_refs = $this->build_options_ids_refs( $price_field_refs, $form ); + // field configs + $fields = array_reduce( $price_field_option_refs, function( $fields, $ref ) use ( $form ) { + $fields[$ref['field_id']] = Caldera_Forms_Field_Util::get_field( $ref['field_id'], $form, $apply_filters = true ); + return $fields; + }, [] ); + + return array_reduce( $price_field_option_refs, function( $discounted_options, $ref ) use ( $fields, $form, $discount, $discounted_fields ) { + + $field = $fields[$ref['field_id']]; + + if ( empty( array_intersect( $discount['pricesets'], array_keys( $field['config']['option'] ) ) ) ) return $discounted_options; + + $price_field = $this->plugin->fields->presets_objects['civicrm_price_sets']->get_price_field_from_config( $field ); + + $field['config']['option'] = array_reduce( $price_field['price_field_values'], function( $options, $price_field_value ) use ( $field, $discount ) { + + $option = $field['config']['option'][$price_field_value['id']]; + + // do discounted option + $options[$price_field_value['id']] = in_array( $price_field_value['id'], $discount['pricesets'] ) ? + $this->do_discounted_option( $option, $field, $price_field_value, $discount ) : + $option; + + return $options; + + }, [] ); + + $options = array_reduce( $field['config']['option'], function( $options, $option ) use ( $ref, $field, $form ) { + $field_option_id = $ref['field_id'] . '_' . $form['form_count'] . '_' . $option['value']; + $options[$field_option_id] = $field['config']['option'][$option['value']]; + $options[$field_option_id]['field_id'] = $ref['field_id']; + return $options; + }, [] ); + return $discounted_options + $options; + + }, [] ); + + } + + /** + * Get fields of type 'civicrm_discount'. + * + * @since 1.0 + * @param array $form The form config + * @return array $discount_fields Array holding the discount fields configs + */ + public function get_discount_fields( $form ) { + return array_filter( $form['fields'], function( $field ) { + return $field['type'] === 'civicrm_discount'; + } ); + } + + /** + * Check if discount is active based on active_on/expire_on. + * + * @since 1.0 + * @param array $discount The discount + * @return boolean $is_active Whether is active or not + */ + public function is_discount_active( $discount ) { + + $now = date( 'Y-m-d H:m:i' ); + $active_on = isset( $discount['active_on'] ) && array_key_exists( 'active_on', $discount ) ? $discount['active_on'] : $now; + $expire_on = isset( $discount['expire_on'] ) && array_key_exists( 'expire_on', $discount ) ? $discount['expire_on'] : $now; + + return ( $now >= $active_on ) && ( $now <= $expire_on ); + + } + +} diff --git a/includes/class-civicrm-caldera-forms-fields.php b/includes/class-civicrm-caldera-forms-fields.php index 328b584..bbc0cc1 100644 --- a/includes/class-civicrm-caldera-forms-fields.php +++ b/includes/class-civicrm-caldera-forms-fields.php @@ -71,9 +71,13 @@ private function include_files() { // include civicrm field presets include CF_CIVICRM_INTEGRATION_PATH . 'fields/presets/class-civicrm-core-fields-presets.php'; include CF_CIVICRM_INTEGRATION_PATH . 'fields/presets/class-civicrm-custom-fields-presets.php'; - if ( in_array( 'CiviContribute', $this->plugin->processors->enabled_components ) ) + if ( in_array( 'CiviContribute', $this->plugin->processors->enabled_components ) ) { include CF_CIVICRM_INTEGRATION_PATH . 'fields/presets/class-civicrm-price-sets-presets.php'; - + if ( $this->plugin->processors->enabled_extensions && in_array( 'org.civicrm.module.cividiscount', $this->plugin->processors->enabled_extensions ) ) + include CF_CIVICRM_INTEGRATION_PATH . 'fields/discount/class-civicrm-discount.php'; + // premium field + include CF_CIVICRM_INTEGRATION_PATH . 'fields/civicrm_premium/class-civicrm-premium.php'; + } } /** @@ -92,8 +96,15 @@ private function setup_objects() { // autopopulate and bulk insert/presets $this->presets_objects['civicrm_core_fields'] = new CiviCRM_Caldera_Forms_Core_Fields_Presets( $this->plugin ); $this->presets_objects['civicrm_custom_fields'] = new CiviCRM_Caldera_Forms_Custom_Fields_Presets( $this->plugin ); - if ( in_array( 'CiviContribute', $this->plugin->processors->enabled_components ) ) + if ( in_array( 'CiviContribute', $this->plugin->processors->enabled_components ) ) { $this->presets_objects['civicrm_price_sets'] = new CiviCRM_Caldera_Forms_Price_Sets_Presets( $this->plugin ); + // discount field for cividiscount integration + if ( $this->plugin->processors->enabled_extensions && in_array( 'org.civicrm.module.cividiscount', $this->plugin->processors->enabled_extensions ) ) + $this->field_objects['civicrm_discount'] = new CiviCRM_Caldera_Forms_Field_Discount( $this->plugin ); + // premium field + $this->field_objects['civicrm_premium'] = new CiviCRM_Caldera_Forms_Field_Premium( $this->plugin ); + } + } diff --git a/includes/class-civicrm-caldera-forms-forms.php b/includes/class-civicrm-caldera-forms-forms.php index ade46d6..9f8a1f8 100644 --- a/includes/class-civicrm-caldera-forms-forms.php +++ b/includes/class-civicrm-caldera-forms-forms.php @@ -53,7 +53,7 @@ public function register_hooks() { // form render transient add_filter( 'caldera_forms_render_get_form', [ $this, 'set_form_transient' ], 1 ); - add_action( 'caldera_forms_render_end', [ $this, 'delete_form_transient' ] ); + add_action( 'caldera_forms_render_end', [ $this, 'delete_form_transient' ], 1 ); // form submission transient add_filter( 'caldera_forms_submit_get_form', [ $this, 'set_form_transient' ] ); @@ -67,9 +67,15 @@ public function register_hooks() { add_filter( 'caldera_forms_magic_summary_should_use_label', [ $this, 'summary_use_label' ], 10, 3 ); // exclude hidden fields from summary add_filter( 'caldera_forms_summary_magic_fields', [ $this, 'exclude_hidden_fields_in_summary' ], 10, 2 ); + // render notices field + add_action( 'caldera_forms_render_get_form', [ $this, 'render_notices_field' ], 30, 2 ); + + // rebuild calculation field formula + add_filter( 'caldera_forms_render_get_field', [ $this, 'rebuild_calculation_field_formula' ], 20, 2 ); + add_filter( 'caldera_forms_render_setup_field', [ $this, 'rebuild_calculation_field_formula' ], 20, 2 ); } - + /** * Set form transient. * @@ -83,13 +89,9 @@ public function set_form_transient( $form ) { // bail if no processors if ( empty( $form['processors'] ) ) return $form; - $has_contact_processor = false; - if ( Caldera_Forms::get_processor_by_type( 'civicrm_contact', $form ) ) - $has_contact_processor = true; - - // set transient structure - if ( $has_contact_processor ) $this->set_transient_structure( $form ); + // set transient structure + $this->set_transient_structure( $form ); return $form; } @@ -115,43 +117,61 @@ public function delete_form_transient() { public function set_transient_structure( $form ) { $structure = ( object ) []; + $structure->contacts = ( object ) []; + $structure->memberships = ( object ) []; + $structure->participants = ( object ) []; + $structure->events = ( object ) []; + $structure->line_items = ( object ) []; + $structure->orders = ( object ) []; - foreach ( $form['processors'] as $id => $processor ) { - if ( isset( $processor['runtimes'] ) ) { - if ( $processor['type'] == 'civicrm_contact' ) { - $structure->contacts = new stdClass(); - $structure->contacts->$id = new stdClass(); - } - - if ( $processor['type'] == 'civicrm_membership' ) { - $structure->memberships = new stdClass(); - $structure->memberships->$id = new stdClass(); - } - - if ( $processor['type'] == 'civicrm_participant' ) { - $structure->participants = new stdClass(); - $structure->participants->$id = new stdClass(); - } + array_map( function( $id, $processor ) use ( $structure, $form ) { - if ( $processor['type'] == 'civicrm_order' ) { - $structure->orders = new stdClass(); - $structure->orders->$id = new stdClass(); - } - - if ( $processor['type'] == 'civicrm_line_item' ) { - $structure->line_items = new stdClass(); - $structure->line_items->$id = new stdClass(); + if ( ! isset( $processor['runtimes'] ) ) return; + // contacts + if ( $processor['type'] == 'civicrm_contact' ) { + $structure->contacts->$id = ( object ) []; + // get current logged in/checksum contact if any + $contact = $this->plugin->helper->current_contact_data_get(); + if ( $contact ) { + // FIXME + // revise use of 'processor_id' or 'cid_x' + $structure->contacts->$id = $contact['contact_id']; + $structure->contacts->{'cid_'.$processor['config']['contact_link']} = $contact['contact_id']; } - + return; + } + // memberships + if ( $processor['type'] == 'civicrm_membership' ) { + $structure->memberships->$id = ( object ) []; + return; + } + // participants and events + if ( $processor['type'] == 'civicrm_participant' ) { + $structure->participants->$id = ( object ) []; + // add events and corresponding event_id + $structure->events->$id = ( object ) []; + $structure->events->$id->event_id = $form['processors'][$id]['config']['id']; + return; + } + // line items + if ( $processor['type'] == 'civicrm_line_item' ) { + $structure->line_items->$id = ( object ) []; + return; + } + // orders + if ( $processor['type'] == 'civicrm_order' ) { + $structure->orders->$id = ( object ) []; + return; } - } + + }, array_keys( $form['processors'] ), $form['processors'] ); /** * Transient structure, fires at form subsmission and at render time. * * @since 0.4.4 * - * @param object $transient The transient stricture + * @param object $structure The transient structure * @param array $form Form config */ apply_filters( 'cfc_filter_transient_structure', $structure, $form ); @@ -161,6 +181,59 @@ public function set_transient_structure( $form ) { return $form; } + /** + * Render notices field at the top of the form. + * @since 1.0 + * @param array $form Form config + */ + public function render_notices_field( $form ) { + /** + * Filter to replace the notices template. + * @since 1.0 + * @var string $template_path The notices template path + */ + $template_path = apply_filters( 'cfc_notices_template_path', CF_CIVICRM_INTEGRATION_PATH . 'templates/notices.php', $form ); + + $html = $this->plugin->html->generate( [ 'form' => $form ], $template_path ); + + // mock html field + $field = [ + 'ID' => 'caldera_forms_civicrm_notices', + 'type' => 'html', + 'label' => 'caldera_forms_civicrm_notices', + 'slug' => 'caldera_forms_civicrm_notices', + 'conditions' => [ + 'type' => '' + ], + 'caption' => '', + 'config' => [ + 'custom_calss' => '', + 'default' => $html + ] + ]; + + // add row placeholder for our new field at the begining of the structure + $form['layout_grid']['structure'] = '12|' . $form['layout_grid']['structure']; + + // new grid, adjust field rows number + $new_grid = array_map( function( $grid ) { + + $parts = explode( ':', $grid ); + $parts[0] = ++$parts[0]; + + return implode( ':', $parts ); + + }, $form['layout_grid']['fields'] ); + + // new grid with the html field + $form['layout_grid']['fields'] = array_merge( [ 'caldera_forms_civicrm_notices' => '1:1' ], $new_grid ); + // add field + $form['fields']['caldera_forms_civicrm_notices'] = $field; + + return $form; + + } + /** * Add CiviCRM panel. * @@ -192,7 +265,7 @@ public function add_civicrm_tab( $panels ) { */ public function summary_use_label( $use, $field, $form ) { - if( Caldera_Forms::get_processor_by_type( 'civicrm_contact', $form ) ) + if ( Caldera_Forms::get_processor_by_type( 'civicrm_contact', $form ) ) return true; return $use; @@ -207,13 +280,12 @@ public function summary_use_label( $use, $field, $form ) { * @param array $form Form config */ public function exclude_hidden_fields_in_summary( $fields, $form ) { - if( Caldera_Forms::get_processor_by_type( 'civicrm_contact', $form ) ) { - foreach ( $fields as $id => $field ) { - if ( $field['type'] == 'hidden' ) { - unset( $fields[$id] ); - } - } - } + + if ( Caldera_Forms::get_processor_by_type( 'civicrm_contact', $form ) ) + return array_filter( $fields, function( $field ) { + return $field['type'] !== 'hidden'; + } ); + return $fields; } @@ -230,23 +302,76 @@ public function reorder_contact_processors( $form ) { // continue as normal if form has no processors if ( empty( $form['processors'] ) ) return $form; - $contact_processors = $rest_processors = []; - foreach ( $form['processors'] as $pId => $processor ) { - if ( $processor['type'] == 'civicrm_contact' ) { - $contact_processors[$pId] = $processor; - } - if ( $processor['type'] != 'civicrm_contact' ) { - $rest_processors[$pId] = $processor; - } - } - - // Sort Contact processors based on Contact Link - uasort( $contact_processors, function( $a, $b ) { + // contact processors + $contacts = array_filter( $form['processors'], function( $processor ) { + return $processor['type'] === 'civicrm_contact'; + } ); + // sort contact processors by contact_link + uasort( $contacts, function( $a, $b ) { return $a['config']['contact_link'] - $b['config']['contact_link']; } ); - $form['processors'] = array_merge( $contact_processors, $rest_processors ); + $form['processors'] = array_merge( $contacts, array_diff_assoc( $form['processors'], $contacts ) ); return $form; } + + /** + * Rebuild calculation field formular. + * + * When fields are removed/hidden through 'caldera_forms_render_get_field' and + * 'caldera_forms_render_setup_field' filters, if the removed field is part of the + * Calculation field formula, it breaks. The formula becomes ( 10+fld_123456 ). + * + * This method filters the calculation field to check for + * hidden/reomved fields and rebuild the formula. + * + * @since 1.0 + * @param array $field The field config + * @param array $form The form config + * @return array $field The filtered field + */ + public function rebuild_calculation_field_formula( $field, $form ) { + + if ( $field['type'] != 'calculation' ) return $field; + + if ( ! isset( $field['config']['formular'] ) ) return $field; + + if ( ! isset( $field['config']['config']['group'] ) ) return $field; + + $do_group_lines = function( $lines ) use ( $form ) { + + $formula = '( '; + + foreach ( $lines as $line_id => $line ) { + if ( ! empty( $form['fields'][$line['field']] ) ) + $formula .= ! $line_id ? $line['field'] + : ( is_numeric( substr( $formula, -1 ) ) ? + $line['operator'] . $line['field'] + : $line['field'] + ); + } + + return $formula . ' )'; + + }; + + $formula = ''; + // rebuild formula + foreach ( $field['config']['config']['group'] as $gid => $group ) { + + $formula .= ! $gid ? + $do_group_lines( $group['lines'] ) + : ( isset( $group['operator'] ) ? + ' ' . $group['operator'] . ' ' + : $do_group_lines( $group['lines'] ) + ); + + } + + $field['config']['formular'] = $formula; + + return $field; + + } } \ No newline at end of file diff --git a/includes/class-civicrm-caldera-forms-helper.php b/includes/class-civicrm-caldera-forms-helper.php index a88ade6..4f980ac 100644 --- a/includes/class-civicrm-caldera-forms-helper.php +++ b/includes/class-civicrm-caldera-forms-helper.php @@ -50,6 +50,26 @@ class CiviCRM_Caldera_Forms_Helper { */ public $states; + public $current_contact_data; + + /** + * CiviCRM tax and invoicing settings. + * + * @since 1.0 + * @access public + * @var array $tax_settings + */ + public $tax_settings; + + /** + * CiviCRM tax rates. + * + * @since 1.0 + * @access public + * @var array $tax_rates Holds tax rates in the form of [ => ] + */ + public $tax_rates; + /** * Initialises this object. * @@ -145,7 +165,12 @@ public function get_wp_civi_contact( $id ) { 'domain_id' => CRM_Core_BAO_Domain::getDomain()->id, ]; - $wp_civicrm_contact = civicrm_api3( 'UFMatch', 'getsingle', $params ); + try { + $wp_civicrm_contact = civicrm_api3( 'UFMatch', 'getsingle', $params ); + } catch ( CiviCRM_API3_Exception $e ) { + Civi::log()->debug( 'Unable to match contact for user with id ' . $id ); + } + return $wp_civicrm_contact['contact_id']; } @@ -351,6 +376,8 @@ public function map_fields_to_processor( $config, $form, &$form_values, $process foreach ( ( $processor ? $config[$processor] : $config ) as $civi_field => $field_id ) { if ( ! empty( $field_id ) ) { + if ( is_array( $field_id ) ) continue; + // do bracket magic tag if ( strpos( $field_id, '{' ) !== false ) { $mapped_field = Caldera_Forms_Magic_Doer::do_bracket_magic( $field_id, $form, NULL, NULL, NULL ); @@ -439,6 +466,8 @@ public function map_fields_to_prerender( $config, &$form, $ignore_fields, $entit // don't prerender hidden field values unless pre_render enable if ( $field['type'] == 'hidden' && ! isset( $field['config']['pre_render'] ) ) continue; + $value = ! empty( $entity[$civi_field] ) ? $entity[$civi_field] : ''; + /** * Filter prerenderd value (default value), fires for every processor field. * @@ -450,11 +479,11 @@ public function map_fields_to_prerender( $config, &$form, $ignore_fields, $entit * @param array $entity The current entity, i.e. Contact, Address, etc * @param array $config processor config */ - $form['fields'][$field['ID']]['config']['default'] = apply_filters( 'cfc_filter_mapped_field_to_prerender', $entity[$civi_field], $civi_field, $field, $entity, $config ); + $form['fields'][$field['ID']]['config']['default'] = apply_filters( 'cfc_filter_mapped_field_to_prerender', $value, $civi_field, $field, $entity, $config ); if ( $field['type'] == 'radio' ) { $options = Caldera_Forms_Field_Util::find_option_values( $field ); - $form['fields'][$field['ID']]['config']['default'] = array_search( $entity[$civi_field], $options ); + $form['fields'][$field['ID']]['config']['default'] = array_search( $value, $options ); } } } @@ -472,6 +501,8 @@ public function get_enabled_extensions(){ try { $result = civicrm_api3( 'Extension', 'get', [ 'sequential' => 1, + 'status' => 'installed', + 'statusLabel' => 'Enabled', 'options' => [ 'limit' => 0 ], ] ); } catch ( CiviCRM_API3_Exception $e ) { @@ -564,6 +595,107 @@ public function get_field_data_by_slug( $slug, $form ) { return Caldera_Forms::get_field_data( $field['ID'], $form ); } + /** + * Get CiviCRM tax and invoicing settings. + * + * @since 1.0 + * @return array $tax_settings + */ + public function get_tax_settings() { + if ( is_array( $this->tax_settings ) ) return $this->tax_settings; + $this->tax_settings = $this->get_civicrm_settings( 'contribution_invoice_settings' ); + return $this->tax_settings; + } + + /** + * Get CiviCRM tax rates. + * + * @since 1.0 + * @return array|bool Array of tax rates in the form of [ => ] + */ + public function get_tax_rates() { + + if ( is_array( $this->tax_rates ) ) return $this->tax_rates; + + $tax_financial_accounts = civicrm_api3( 'EntityFinancialAccount', 'get', [ + 'return' => [ + 'id', + 'entity_table', + 'entity_id', + 'account_relationship', + 'financial_account_id', + 'financial_account_id.financial_account_type_id', + 'financial_account_id.tax_rate' + ], + 'financial_account_id.is_active' => 1, + 'financial_account_id.is_tax' => 1, + 'options' => [ 'limit' => 0 ] + ] ); + + if ( $tax_financial_accounts['count'] ) { + // buils tax rates + $this->tax_rates = array_reduce( $tax_financial_accounts['values'], function( $tax_rates, $financial_account ) { + $tax_rates[$financial_account['entity_id']] = $financial_account['financial_account_id.tax_rate']; + return $tax_rates; + }, [] ); + + return $this->tax_rates; + + } + + return false; + } + + /** + * Calculate percentage for a given amount. + * + * @since 1.0 + * @param string $amount The amount + * @param string $percentage The percentage + * @return string $amount Calculated percentage amount + */ + public function calculate_percentage( $amount, $percentage ) { + return ( $percentage / 100 ) * $amount; + } + + /** + * Format tax label as per CiviCRM. + * + * @param string $label The label + * @param string $amount The amount + * @param string $tax_amount The tax amount + * @return string $label The formated label + */ + public function format_tax_label( $label, $amount, $tax_amount, $currency = false ) { + + $tax_settings = $this->get_tax_settings(); + $tax_term = $tax_settings['tax_term']; + $taxed_amount = $this->format_money( $amount + $tax_amount, $currency ); + $tax_amount = $this->format_money( $tax_amount, $currency ); + $amount = $this->format_money( $amount, $currency ); + + $format = [ + 'Do_not_show' => sprintf( '%1$s - %2$s', $label, $taxed_amount ), + 'Inclusive' => sprintf( '%1$s - %2$s (includes %3$s of %4$s)', $label, $taxed_amount, $tax_term, $tax_amount ), + 'Exclusive' => sprintf( '%1$s - %2$s + %3$s %4$s', $label, $amount, $tax_amount, $tax_term ) + ]; + + return $format[$tax_settings['tax_display_settings']]; + + } + + /** + * Format money, as per CiviCRM settings. + * + * @since 1.0 + * @param string $amount The amount + * @param string $currency Optional, the currency + * @return string $formated_amount The formated amount + */ + public function format_money( $amount, $currency = false ) { + return CRM_Utils_Money::format( $amount, $currency ); + } + /** * Get price sets. * @@ -571,6 +703,11 @@ public function get_field_data_by_slug( $slug, $form ) { * @return array $price_sets The active price sets with their corresponding price fields and price filed values */ public function get_price_sets() { + + // get tax settings + $tax_settings = $this->get_tax_settings(); + // get tax rates + $tax_rates = $this->get_tax_rates(); $price_set_params = [ 'sequential' => 1, @@ -585,6 +722,7 @@ public function get_price_sets() { ], ]; + try { $result_price_sets = civicrm_api3( 'PriceSet', 'get', $price_set_params ); } catch ( CiviCRM_API3_Exception $e ) { @@ -606,7 +744,7 @@ public function get_price_sets() { $price_field_values = []; foreach ( $all_price_field_values['values'] as $id => $price_field_value ) { - $price_field_value['amount'] = number_format( $price_field_value['amount'], 2, '.', '' ); + $price_field_value['amount'] = $price_field_value['amount']; $price_field_values[$id] = $price_field_value; } @@ -615,8 +753,14 @@ public function get_price_sets() { $price_set['price_set_id'] = $price_set_id = $price_set['id']; $price_set['price_fields'] = $price_set['api.PriceField.get']['values']; foreach ( $price_set['price_fields'] as $price_field_id => $price_field ) { + $price_set['price_fields'][$price_field_id]['price_field_id'] = $price_field_id; foreach ( $price_field_values as $value_id => $price_field_value) { + $price_field_value['price_field_value_id'] = $value_id; if ( $price_field_id == $price_field_value['price_field_id'] ) { + if ( $tax_settings['invoicing'] && $tax_rates && array_key_exists( $price_field_value['financial_type_id'], $tax_rates ) ) { + $price_field_value['tax_rate'] = $tax_rates[$price_field_value['financial_type_id']]; + $price_field_value['tax_amount'] = $this->calculate_percentage( $price_field_value['amount'], $price_field_value['tax_rate'] ); + } $price_set['price_fields'][$price_field_id]['price_field_values'][$value_id] = $price_field_value; } } @@ -658,27 +802,73 @@ public function cached_price_sets() { * @return array $price_field_value The Price Field Value */ public function get_price_field_value( $id ) { - // when using a checkbox the value that gets passed is an array if ( is_array( $id ) ) $id = array_pop( $id ); - try { - $price_field_value = civicrm_api3( 'PriceFieldValue', 'getsingle', [ - 'return' => [ 'id', 'price_field_id', 'label', 'amount', 'count', 'membership_type_id', 'membership_num_terms', 'financial_type_id' ], - 'id' => $id, - 'is_active' => 1, - ] ); - } catch ( CiviCRM_API3_Exception $e ) { + // $id = str_replace( 'price_field_value_id_', '', $id ); + + // single option + if ( ! strpos( $id, ',' ) ) { + $price_field_value = $this->get_price_set_column_by_id( $id, 'price_field_value' ); + } else { + // multiple options + $id = explode( ', ', $id ); + + $price_field_value = array_reduce( $id, function( $options, $option_id ) { + $option_id = ( int ) $option_id; + $options[$option_id] = $this->get_price_set_column_by_id( $option_id, 'price_field_value' ); + return $options; + }, [] ); } - if ( ! $price_field_value['is_error'] ) { - $price_field_value['amount'] = number_format( $price_field_value['amount'], 2, '.', '' ); - return $price_field_value; + // filter price field value + $price_field_value = apply_filters( 'cfc_filter_price_field_value_get', $price_field_value, $id ); + + return $price_field_value; + } + + /** + * Get price_set/price_field/price_field_value by id specifing the column name. + * + * @since 1.0 + * @param int $id The entity id + * @param string $column_name The column name, price_set|price_field|price_field_value + * @return array $column The requested entity or false + */ + public function get_price_set_column_by_id( $id, $column_name ) { + + $price_sets = $this->cached_price_sets(); + + if ( $column_name == 'price_set' && array_key_exists( $id, $price_sets ) ) { + $column = $price_sets[$id]; + } + + if ( $column_name == 'price_field' ) { + foreach ( $price_sets as $price_set_id => $price_set ) { + foreach ( $price_set['price_fields'] as $price_field_id => $price_field ) { + if ( array_key_exists( $id, $price_set['price_fields'] ) ) + $column = $price_set['price_fields'][$id]; + } + } + } + + if ( $column_name == 'price_field_value' ) { + foreach ( $price_sets as $price_set_id => $price_set ) { + foreach ( $price_set['price_fields'] as $price_field_id => $price_field ) { + foreach ( $price_field['price_field_values'] as $price_field_value_id => $price_field_value ) { + if ( array_key_exists( $id, $price_field['price_field_values'] ) ) + $column = $price_field['price_field_values'][$id]; + } + } + } } + if ( isset( $column ) ) return $column; + return false; + } /** @@ -758,8 +948,9 @@ public function civi_contact_dedupe( $contact, $contact_type, $dedupe_rule_id ) // Check dupes $cids = CRM_Dedupe_Finder::dupesByParams( $dedupeParams, $contact_type, NULL, [], $dedupe_rule_id ); + $cids = array_reverse( $cids ); - return $cids ? array_pop( array_reverse( $cids ) ) : 0; + return $cids ? array_pop( $cids ) : 0; } /** @@ -773,6 +964,8 @@ public function civi_contact_dedupe( $contact, $contact_type, $dedupe_rule_id ) */ public function current_contact_data_get() { + if ( ! empty( $this->current_contact_data ) ) return $this->current_contact_data; + $contact = false; // checksum links first @@ -790,8 +983,10 @@ public function current_contact_data_get() { } // logged in overrides checksum - if ( is_user_logged_in() ) + if ( is_user_logged_in() ) { $contact = $this->get_current_contact(); + $this->current_contact_data = $contact; + } return $contact; @@ -812,4 +1007,93 @@ public function get_current_contact() { return false; } + /** + * Get a Participant custom fields. + * + * @since 1.0 + * @return array $custom_fields The array of custom fields - e.g. ['custom_x' => 'Label of custom_x'] + */ + public static function get_participant_custom_fields() { + + try { + $custom_groups = civicrm_api3( 'CustomGroup', 'get', [ + 'sequential' => 1, + 'is_active' => 1, + 'extends' => 'Participant', + 'api.CustomField.get' => [ 'is_active' => 1, 'options' => [ 'limit' => 0 ] ], + 'options' => [ 'limit' => 0 ], + ] ); + } catch ( CiviCRM_API3_Exception $e ) { + return [ 'note' => $e->getMessage(), 'type' => 'error' ]; + } + + $custom_fields = []; + foreach ( $custom_groups['values'] as $key => $custom_group ) { + foreach ( $custom_group['api.CustomField.get']['values'] as $k => $custom_field ) { + $custom_fields['custom_' . $custom_field['id']] = [ + 'label' => $custom_field['label'], + 'extends_entity_column_id' => $custom_group['extends_entity_column_id'], + 'extends_entity_column_value' => $custom_group['extends_entity_column_value'] + ]; + } + } + + return $custom_fields; + + } + + /** + * Get processor by type. + * + * @since 1.0 + * @param string $processor_type The processor type + * @param array $form Form config + * @return array $processors The processors config + */ + public function get_processor_by_type( $processor_type, $form ) { + // get form processors + $processors = Caldera_Forms::get_processor_by_type( $processor_type, $form ); + // filter out non associative keys + if ( $processors ) + return array_filter( $processors, function( $processor, $id ) { + return $id === $processor['ID']; + }, ARRAY_FILTER_USE_BOTH ); + + return false; + } + + /** + * Get processor id from magic tag. + * + * @since 1.0 + * @param string $magic_tag The processor_id magig tag + * @param array|boolean $form The form config or false + * @return string|boolean $processor_id The processor_id or false otherwise + */ + public function get_processor_from_magic( $magic_tag, $form = false ) { + + if ( ! is_string( $magic_tag ) ) return false; + + if ( strpos( $magic_tag, '{' ) === false ) return false; + + if ( strpos( $magic_tag, 'processor_id' ) === false ) return false; + + // clean up magic tag + $magic_tag = str_replace( [ '{', '}' ], '', $magic_tag ); + // get parts + $parts = explode( ':', $magic_tag ); + + if( ! $form ) global $form; + + // if form has more than one processor of same type + // the magic tag has the format of processor_type:processor_id: + // otherwise the format is processor_type:processor_id + if ( count( $parts ) > 2 ) { + return array_pop( $parts ); + } else { + return key( $this->get_processor_by_type( $parts[0], $form ) ); + } + + } + } diff --git a/includes/class-civicrm-caldera-forms-processors.php b/includes/class-civicrm-caldera-forms-processors.php index f41669b..ee14e3b 100644 --- a/includes/class-civicrm-caldera-forms-processors.php +++ b/includes/class-civicrm-caldera-forms-processors.php @@ -68,7 +68,6 @@ private function include_files() { // Include processor classes include CF_CIVICRM_INTEGRATION_PATH . 'processors/contact/class-contact-processor.php'; - // include CF_CIVICRM_INTEGRATION_PATH . 'processors/order/class-order-processor.php'; if ( in_array( 'CiviContribute', $this->enabled_components ) ) { include CF_CIVICRM_INTEGRATION_PATH . 'processors/order/class-order-processor.php'; include CF_CIVICRM_INTEGRATION_PATH . 'processors/line-item/class-line-item-processor.php'; @@ -76,6 +75,8 @@ private function include_files() { } if ( in_array( 'CiviMember', $this->enabled_components ) ) include CF_CIVICRM_INTEGRATION_PATH . 'processors/membership/class-membership-processor.php'; + if ( in_array( 'CiviEvent', $this->enabled_components ) ) + include CF_CIVICRM_INTEGRATION_PATH . 'processors/participant/class-participant-processor.php'; include CF_CIVICRM_INTEGRATION_PATH . 'processors/group/class-group-processor.php'; include CF_CIVICRM_INTEGRATION_PATH . 'processors/activity/class-activity-processor.php'; include CF_CIVICRM_INTEGRATION_PATH . 'processors/relationship/class-relationship-processor.php'; @@ -109,6 +110,8 @@ private function setup_objects() { } if ( in_array( 'CiviMember', $this->enabled_components ) ) $this->processors['membership'] = new CiviCRM_Caldera_Forms_Membership_Processor( $this->plugin ); + if ( in_array( 'CiviEvent', $this->enabled_components ) ) + $this->processors['participant'] = new CiviCRM_Caldera_Forms_Participant_Processor( $this->plugin ); if ( in_array( 'CiviCase', $this->enabled_components ) ) $this->processors['case'] = new CiviCRM_Caldera_Forms_Case_Processor( $this->plugin ); $this->processors['activity'] = new CiviCRM_Caldera_Forms_Activity_Processor( $this->plugin ); diff --git a/processors/contact/class-contact-processor.php b/processors/contact/class-contact-processor.php index 014b918..dde0513 100644 --- a/processors/contact/class-contact-processor.php +++ b/processors/contact/class-contact-processor.php @@ -197,6 +197,14 @@ public function pre_processor( $config, $form, $processid ) { $form_values['civicrm_contact']['image_URL'] = CRM_Utils_System::url( 'civicrm/contact/imagefile', ['photo' => $file['uri']], true ); } + // contact reference field for organization maps to an array [ 'organization_name' => , 'employer_id' => ] + if ( ! empty( $form_values['civicrm_contact']['current_employer'] ) && is_array( $form_values['civicrm_contact']['current_employer'] ) ) { + $org = $form_values['civicrm_contact']['current_employer']; + $form_values['civicrm_contact']['current_employer'] = $org['organization_name']; + // need to be set in case of duplicate orgs with same name + $form_values['civicrm_contact']['employer_id'] = $org['employer_id']; + } + try { $create_contact = civicrm_api3( 'Contact', 'create', $form_values['civicrm_contact'] ); } catch ( CiviCRM_API3_Exception $e ) { diff --git a/processors/line-item/class-line-item-processor.php b/processors/line-item/class-line-item-processor.php index fff62ac..d6c9c44 100644 --- a/processors/line-item/class-line-item-processor.php +++ b/processors/line-item/class-line-item-processor.php @@ -16,15 +16,6 @@ class CiviCRM_Caldera_Forms_Line_Item_Processor { */ public $plugin; - /** - * Contact link. - * - * @since 0.4.4 - * @access protected - * @var string $contact_link The contact link - */ - protected $contact_link; - /** * The processor key. * @@ -81,40 +72,38 @@ public function register_processor( $processors ) { * @param array $form Form configuration */ public function pre_processor( $config, $form, $processid ) { - + } public function processor( $config, $form, $processid ) { - global $transdata; - $transient = $this->plugin->transient->get(); - - $this->contact_link = 'cid_' . $config['contact_link']; // price field value params aka 'line_item' $price_field_value = isset( $config['is_fixed_price_field'] ) ? $this->plugin->helper->get_price_field_value( $config['fixed_price_field_value'] ) : $this->plugin->helper->get_price_field_value( Caldera_Forms::do_magic_tags( $config['price_field_value'] ) ); - if ( ! empty( $config['entity_table'] ) ) { + + if ( ! empty( $config['entity_table'] ) && $price_field_value ) { if ( $config['entity_table'] == 'civicrm_membership' ) { - $price_field_value['entity_table'] = $config['entity_table']; + $price_field_value = $this->build_price_field_values_array( $price_field_value, $config['entity_table'] ); $this->process_membership( $config, $form, $transient, $price_field_value ); } if ( $config['entity_table'] == 'civicrm_participant' ) { + $price_field_value = $this->build_price_field_values_array( $price_field_value, $config['entity_table'] ); $this->process_participant( $config, $form, $transient, $price_field_value ); } if ( $config['entity_table'] == 'civicrm_contribution' ) { - $price_field_value['entity_table'] = $config['entity_table']; + $price_field_value = $this->build_price_field_values_array( $price_field_value, $config['entity_table'] ); $this->process_contribution( $config, $form, $transient, $price_field_value ); } - } else { - $entity_table = $this->guess_entity_table( $price_field_value ); - $price_field_value['entity_table'] = $entity_table; - $entity = str_replace( 'civicrm_', '', $entity_table ); + } elseif ( $price_field_value ) { + $price_field_value = $this->build_price_field_values_array( $price_field_value ); + // get first one? + $entity = str_replace( 'civicrm_', '', $price_field_value[0]['entity_table'] ); $this->{'process_' . $entity}( $config, $form, $transient, $price_field_value ); } @@ -125,6 +114,7 @@ public function guess_entity_table( $price_field_value, $entity_table = false ) // FIXME // only for memberships or contributions, need to find a way that checks for all tables + if ( ! isset( $this->price_sets ) ) $this->price_sets = $this->plugin->helper->cached_price_sets(); if ( ! empty( $entity_table ) ) return $entity_table; @@ -133,7 +123,6 @@ public function guess_entity_table( $price_field_value, $entity_table = false ) // find entity table from priceset? - // $this->price_sets = $this->plugin->helper->cached_price_sets(); // foreach ( $this->price_sets as $price_set_id => $price_set ) { // foreach ( $price_set['price_fields'] as $price_field_id => $price_field ) { @@ -161,16 +150,11 @@ public function guess_entity_table( $price_field_value, $entity_table = false ) */ public function process_membership( $config, $form, $transient, $price_field_value ) { - global $transdata; - - $price_field_value['price_field_value_id'] = $price_field_value['id']; + if ( isset( $config['is_other_amount'] ) ) { + $form_values = $this->plugin->helper->map_fields_to_processor( $config, $form, $form_values ); + $price_field_value[0]['line_total'] = $price_field_value[0]['unit_price'] = $price_field_value[0]['amount'] = $form_values['amount']; + } - // $price_field_value['entity_table'] = $config['entity_table']; - $price_field_value['field_title'] = $price_field_value['label']; - $price_field_value['unit_price'] = $price_field_value['amount']; - $price_field_value['qty'] = 1; - $price_field_value['line_total'] = $price_field_value['amount'] * $price_field_value['qty']; - // membership params aka 'params' $processor_id = Caldera_Forms::do_magic_tags( $config['entity_params'] ); if ( isset( $transient->memberships->$processor_id->params ) && ! empty( $config['entity_params'] ) ) { @@ -184,15 +168,15 @@ public function process_membership( $config, $form, $transient, $price_field_val } unset( - $price_field_value['membership_num_terms'], - $price_field_value['contribution_type_id'], - $price_field_value['id'], - $price_field_value['amount'], $entity_params['price_field_value'], $entity_params['is_price_field_based'] ); + + $transient->memberships->$processor_id->params = $entity_params; + $line_item = [ - 'line_item' => [ $price_field_value ], + 'processor_entity' => $processor_id, + 'line_item' => $price_field_value, 'params' => $entity_params ]; @@ -205,7 +189,7 @@ public function process_membership( $config, $form, $transient, $price_field_val /** * Process Participant Line Item. * - * @since 0.4.4 + * @since 1.0 * * @param array $config Processor config * @param array $form Form config @@ -214,6 +198,61 @@ public function process_membership( $config, $form, $transient, $price_field_val */ public function process_participant( $config, $form, $transient, $price_field_value ) { + // if price field is disabled by cfc we won't have a price_field_value + // if ( ! $price_field_value['id'] ) return; + + $form_values = $this->plugin->helper->map_fields_to_processor( $config, $form, $form_values ); + + if ( isset( $config['is_other_amount'] ) ) + $price_field_value[0]['line_total'] = $price_field_value[0]['unit_price'] = $price_field_value[0]['amount'] = $form_values['amount']; + + // handle qty and head count + if ( isset( $form_values['qty'] ) && isset( $config['is_fixed_price_field'] ) && ! isset( $config['is_other_amount'] ) ) { + $price_field_value[0]['qty'] = $form_values['qty']; + $price_field_value[0]['count'] = $form_values['qty']; + $price_field_value[0]['line_total'] = $price_field_value[0]['line_total'] * $form_values['qty']; + } + + // get price field + $price_field = $this->plugin->helper->get_price_set_column_by_id( $price_field_value[0]['price_field_id'], 'price_field' ); + + // participant params aka 'params' + $processor_id = Caldera_Forms::do_magic_tags( $config['entity_params'] ); + + if ( isset( $transient->participants->$processor_id->params ) && ! empty( $config['entity_params'] ) ) { + + $entity_params = $transient->participants->$processor_id->params; + + $entity_params['source'] = ! empty( $entity_params['source'] ) ? + $entity_params['source'] : + $form['name']; + + // need to set price set id, otherwise Participant.create from Order.create + // will create a non-linked LineItem as the contribution has been created yet + // should only have one price_field_value + $entity_params['price_set_id'] = $price_field['price_set_id']; + $entity_params['fee_level'] = $price_field_value[0]['label']; + $entity_params['fee_amount'] = $price_field_value[0]['line_total']; + + } + + unset( + $entity_params['price_field_value'], + $entity_params['is_price_field_based'] + ); + + $transient->participants->$processor_id->params = $entity_params; + + $line_item = [ + 'processor_entity' => $processor_id, + 'line_item' => $price_field_value, + 'params' => $entity_params + ]; + + $transient->line_items->{$config['processor_id']}->params = $line_item; + + $this->plugin->transient->save( $transient->ID, $transient ); + } /** @@ -227,32 +266,68 @@ public function process_participant( $config, $form, $transient, $price_field_va * @param array $price_field_value The price field value */ public function process_contribution( $config, $form, $transient, $price_field_value ) { - - if ( ! isset( $price_field_value['price_field_value_id'] ) ) - $price_field_value['price_field_value_id'] = $price_field_value['id']; - - if( ! isset( $price_field_value['unit_price'], $price_field_value['line_total'] ) ) - $price_field_value['unit_price'] = $price_field_value['line_total'] = $price_field_value['amount']; - - if ( ! isset( $price_field_value['field_title'] ) ) - $price_field_value['field_title'] = $price_field_value['label']; - // assume 1 unit as there's currently no way to change/map this in the processor - if ( ! isset( $price_field_value['qty'] ) ) - $price_field_value['qty'] = 1; + // should only have one price_field_value if ( isset( $config['is_other_amount'] ) ) { $form_values = $this->plugin->helper->map_fields_to_processor( $config, $form, $form_values ); - $price_field_value['line_total'] = $price_field_value['unit_price'] = $price_field_value['amount'] = $form_values['amount']; + $price_field_value[0]['line_total'] = $price_field_value[0]['unit_price'] = $price_field_value[0]['amount'] = $form_values['amount']; } - - unset( $price_field_value['contribution_type_id'], $price_field_value['id'] ); - + + if ( isset( $config['entity_params'] ) && ! empty( $config['entity_params'] ) ) + $processor_id = Caldera_Forms::do_magic_tags( $config['entity_params'] ); + $line_item = [ - 'line_item' => [ $price_field_value ] + 'processor_entity' => isset( $processor_id ) ? $processor_id : false, + 'line_item' => $price_field_value ]; $transient->line_items->{$config['processor_id']}->params = $line_item; $this->plugin->transient->save( $transient->ID, $transient ); } + + /** + * Build price field values array. + * + * @since 1.0 + * @param int|array $price_field_value The price field value id, or array of price field values ids + * @param string|bool $entity_table The entity table or false + * @return array $price_field_value The formated price field value/values array + */ + public function build_price_field_values_array( $price_field_value, $entity_table = false ) { + + if ( array_key_exists( 'id', $price_field_value ) ) $price_field_value = [ $price_field_value ]; + + $field_values = array_map( function( $field_value ) use ( $entity_table ) { + + $price_field = $this->plugin->helper->get_price_set_column_by_id( $field_value['price_field_id'], 'price_field' ); + + $field_value['qty'] = 1; + $field_value['label'] = $field_value['label']; + $field_value['field_title'] = $price_field['label']; + $field_value['unit_price'] = $field_value['amount']; + $field_value['line_total'] = $field_value['amount'] * $field_value['qty']; + $field_value['price_field_value_id'] = $field_value['id']; + + $field_value['entity_table'] = $entity_table ? $entity_table : $this->guess_entity_table( $price_field_value ); + + unset( + $field_value['id'], + $field_value['name'], + $field_value['amount'], + $field_value['weight'], + $field_value['is_default'], + $field_value['is_active'], + $field_value['visibility_id'], + $field_value['membership_num_terms'], + $field_value['contribution_type_id'] + ); + + return $field_value; + + }, $price_field_value ); + + return array_values( $field_values ); + } + } diff --git a/processors/line-item/line_item_config.php b/processors/line-item/line_item_config.php index fbaba18..f7d1dc4 100644 --- a/processors/line-item/line_item_config.php +++ b/processors/line-item/line_item_config.php @@ -32,6 +32,15 @@ + +
+ +
+ +

When \'Entity Table\' is set to CiviCRM Contribution, set the Participant processor magic tag if this is a Contribution Line Item associated to a particular Participant, like for example a Donation.', 'caldera-forms-civicrm') );?>

+
+
+
@@ -63,11 +72,18 @@
- -
- + +
+
- + +
+
+ + +
+
+
@@ -99,17 +115,14 @@ is_other_amount = '#' + prId + '_is_other_amount', amount = '#' + prId + '_amount'; - $( price_field_value + ' .is_fixed input' ).on( 'change', function( i, el ) { var is_fixed = $( this ).prop( 'checked' ); $( '.binded_price_field', $( price_field_value ) ).toggle( ! is_fixed ); $( '.fixed_price_field', $( price_field_value ) ).toggle( is_fixed ); - } ).trigger( 'change' ); - - $( entity_table + ' select' ).on( 'change', function( i, el ) { - var entity = $( this ).val(); - $( amount_wrapper ).toggle( entity == 'civicrm_contribution' ); - $( entity_data ).toggle( entity != 'civicrm_contribution' ); + // qty section + var condition = $( entity_table + ' select' ).val() == 'civicrm_participant' && is_fixed; + $( '#' + prId + '_qty' ).toggle( condition ); + $( '#' + prId + '_use_qty_as_count' ).toggle( condition ); } ).trigger( 'change' ); $( is_other_amount + ' input' ).on( 'change', function( i, el ) { diff --git a/processors/membership/class-membership-processor.php b/processors/membership/class-membership-processor.php index f03c759..15b30f5 100644 --- a/processors/membership/class-membership-processor.php +++ b/processors/membership/class-membership-processor.php @@ -63,7 +63,7 @@ public function __construct( $plugin ) { // filter form before rendering add_filter( 'caldera_forms_render_get_form', [ $this, 'pre_render' ] ); // render membership notices - add_action( 'caldera_forms_render_start', [ $this, 'current_membership_notices' ] ); + add_action( 'cfc_notices_to_render', [ $this, 'render_notices' ] ); } @@ -296,25 +296,24 @@ public function get_membership_statuses_current() { /** * Membership notices. * - * @since 0.4.4 - * - * @param array $form Form config + * @since 1.0 + * @param array $notices The array of notices to render + * @return array $notices The filtered notices */ - public function current_membership_notices( $form ) { + public function render_notices( $notices ) { // output if ( isset( $this->has_memberships ) ) { - $class = "cfc-notices-{$form['ID']}"; - $out = "
"; foreach ( $this->has_memberships as $key => $membership ) { // FIXME // use CiviCRM's date setting $end_date = date_format( date_create( $membership['end_date'] ), 'F d, Y' ); - $out .= '
'; - $out .= sprintf( __( 'Your %1$s membership expires on %2$s.', 'caldera-forms-civicrm' ), $membership['membership_name'], $end_date ); - $out .= '
'; + $notices[] = [ + 'type' => 'warning', + 'note' => sprintf( __( 'Your %1$s membership expires on %2$s.', 'caldera-forms-civicrm' ), $membership['membership_name'], $end_date ) + ]; } - $out .= '
'; - echo $out; } + + return $notices; } } diff --git a/processors/order/class-order-processor.php b/processors/order/class-order-processor.php index bfdc979..5063515 100644 --- a/processors/order/class-order-processor.php +++ b/processors/order/class-order-processor.php @@ -43,6 +43,15 @@ class CiviCRM_Caldera_Forms_Order_Processor { */ public $is_pay_later; + /** + * Total tax amount. + * + * @since 1.0.1 + * @access public + * @var float $total_tax_amount + */ + public $total_tax_amount = 0; + /** * The order result. * @@ -96,6 +105,7 @@ public function register_processor( $processors ) { 'pre_processor' => [ $this, 'pre_processor' ], 'processor' => [ $this, 'processor' ], 'post_processor' => [ $this, 'post_processor'], + 'magic_tags' => [ 'order_id' ] ]; return $processors; @@ -131,9 +141,6 @@ public function processor( $config, $form, $processid ) { $transient = $this->plugin->transient->get(); $this->contact_link = 'cid_' . $config['contact_link']; - $config_line_items = $config['line_items']; - unset( $config['line_items'] ); - // Get form values $form_values = $this->plugin->helper->map_fields_to_processor( $config, $form, $form_values ); @@ -145,8 +152,8 @@ public function processor( $config, $form, $processid ) { $form_values['currency'] = $config['currency']; - // $form_values['receipt_date'] = date( 'YmdHis' ); - + if ( ! empty( $config['campaign_id'] ) ) $form_values['campaign_id'] = $config['campaign_id']; + // contribution page for reciepts if ( isset( $config['contribution_page_id'] ) ) $form_values['contribution_page_id'] = $config['contribution_page_id']; @@ -166,27 +173,14 @@ public function processor( $config, $form, $processid ) { $form_values['contact_id'] = $transient->contacts->{$this->contact_link}; // line items - $line_items = []; - $count = 0; - foreach ( $config_line_items as $item => $processor ) { - if( ! empty( $processor ) ) { - $processor = Caldera_Forms::do_magic_tags( $processor ); - if ( ! strpos( $processor, 'civicrm_line_item' ) ) { - $line_items[$count] = $transient->line_items->$processor->params; - if ( - isset( $line_items[$count]['params']['membership_type_id'] ) && $this->is_pay_later ) { - // set membership as pending - $line_items[$count]['params']['status_id'] = 'Pending'; - $line_items[$count]['params']['is_override'] = 1; - } - } - $count++; - } else { - unset( $config_line_items[$item] ); - } - } + $line_items = $this->build_line_items_params( $transient, $config, $form ); - $form_values['line_items'] = $line_items; + if ( $this->has_participant_item( $line_items ) ) + $line_items = $this->maybe_format_line_items_to_entity( $line_items, $config, $form ); + + // add tax amount + if ( $this->total_tax_amount ) + $form_values['tax_amount'] = $this->total_tax_amount; // stripe metadata if ( $this->charge_metadata ) $form_values = array_merge( $form_values, $this->charge_metadata ); @@ -205,14 +199,26 @@ public function processor( $config, $form, $processid ) { $form_values = array_merge( $form_values, $metadata ); } + $form_values['line_items'] = $line_items; + try { $create_order = civicrm_api3( 'Order', 'create', $form_values ); - $this->order = $create_order; + + $this->order = ( $create_order['count'] && ! $create_order['is_error'] ) ? $create_order['values'][$create_order['id']] : false; + + // create product + if ( $this->order ) + $this->create_premium( $this->order, $form_values, $config ); + } catch ( CiviCRM_API3_Exception $e ) { $transdata['error'] = true; - $transdata['note'] = $e->getMessage() . '

getTraceAsString() . ''; + $transdata['note'] = $e->getMessage() . '

' . $e->getTraceAsString() . '
'; } + // return order_id magic tag + if ( is_array( $create_order ) && ! $create_order['is_error'] ) + return $create_order['id']; + } /** @@ -230,7 +236,13 @@ public function post_processor( $config, $form, $processid ) { $transient = $this->plugin->transient->get(); // preserve join dates - $this->preserve_membership_join_date( $config, $form, $processid ); + $this->preserve_membership_join_date( $form ); + + $line_items = civicrm_api3( 'LineItem', 'get', [ + 'contribution_id' => $this->order['id'] + ] ); + + $this->order = array_merge( $this->order, [ 'line_items' => $line_items['values'] ] ); if ( true ) { //$config['is_thank_you'] ) { add_filter( 'caldera_forms_ajax_return', function( $out, $_form ) use ( $transdata, $transient ){ @@ -243,12 +255,13 @@ public function post_processor( $config, $form, $processid ) { * @param string $template_path The template path * @param array $form Form config */ - $template_path = apply_filters( 'cfc_order_thank_you_template_path', CF_CIVICRM_INTEGRATION_PATH . 'template/thank-you.php', $_form ); + $template_path = apply_filters( 'cfc_order_thank_you_template_path', CF_CIVICRM_INTEGRATION_PATH . 'templates/thank-you.php', $_form ); $form_values = Caldera_Forms::get_submission_data( $_form ); $data = [ 'values' => $form_values, + 'order' => $this->order, 'form' => $_form, 'transdata' => $transdata, 'transient' => $transient @@ -263,11 +276,164 @@ public function post_processor( $config, $form, $processid ) { }, 10, 2 ); } + /** + * Runs when Order processor is post_processed if an order has been created. + * + * @since 1.0 + * @param array $order The created order result + * @param array $config The processor config + * @param array $form The form config + * @param string $processid The process id + */ + do_action( 'cfc_order_post_processor', $this->order, $config, $form, $processid ); + // send confirmation/receipt $this->maybe_send_confirmation( $this->order, $config ); } + /** + * Builds line items parameters array formatted for Order.create. + * + * @since 1.0.1 + * @param object $transient The Caldera Forms CiviCRM transient object + * @param array $config The processor config + * @param array $form The form config + * @return array $line_items The formatted line items array + */ + public function build_line_items_params( $transient, $config, $form ) { + + if ( empty( $config['line_items'] ) ) return []; + + return array_reduce( $config['line_items'], function( $line_items, $item_processor_tag ) use ( $transient, $form ) { + + if ( empty( $item_processor_tag ) ) return $line_items; + + $item_processor_id = Caldera_Forms::do_magic_tags( $item_processor_tag ); + + if ( strpos( $item_processor_id, 'civicrm_line_item' ) || empty( ( array ) $transient->line_items->$item_processor_id ) ) return $line_items; + + $line_item = $transient->line_items->$item_processor_id->params; + + if ( isset( $line_item['line_item'][0]['tax_amount'] ) && $this->plugin->helper->get_tax_settings()['invoicing'] ) + $this->total_tax_amount += $line_item['line_item'][0]['tax_amount']; + + // set membership as pending + if ( isset( $line_item['params']['membership_type_id'] ) && $this->is_pay_later ) { + $line_item['params']['status_id'] = 'Pending'; + $line_item['params']['is_override'] = 1; + } + + // set participant as pending + if ( isset( $line_item['params']['event_id'] ) && $this->is_pay_later ) + $line_item['params']['status_id'] = 'Pending from pay later'; + + // less line item total errors removing entities that are not being processed + if ( isset( $line_item['processor_entity'] ) && ! empty( $line_item['processor_entity'] ) ) { + $processor_entity = Caldera_Forms::do_magic_tags( $line_item['processor_entity'] ); + if ( strpos( $processor_entity, 'civicrm' ) ) unset( $line_item['processor_entity'] ); + } + + $line_item['processor_id'] = $item_processor_id; + + if ( isset( $line_item['line_item'] ) ) + $line_items[] = $line_item; + + return $line_items; + + }, [] ); + + } + + /** + * Reformats the line items to correctly add otpions like donations + * assigned to the right enity, participant in this case. + * + * @since 1.0.1 + * @param array $line_items The formated line items + * @param array $config The processor config + * @param array $form The form config + * @return array $line_items The reformatted line items + */ + public function maybe_format_line_items_to_entity( $line_items, $config, $form ) { + + $participant_processors = $this->plugin->helper->get_processor_by_type( 'civicrm_participant', $form ); + $membership_pprocessors = $this->plugin->helper->get_processor_by_type( 'civicrm_membership', $form ); + $processors = []; + + $transient = $this->plugin->transient->get(); + + if ( is_array( $participant_processors ) ) + $processors = array_merge( $processors, $participant_processors ); + + if ( is_array( $membership_pprocessors ) ) + $processors = array_merge( $processors, $membership_pprocessors ); + + if ( empty( $processors ) ) return $line_items; + + $formatted_items = []; + + array_map( function( $item ) use ( &$formatted_items, $processors, $transient ) { + + if ( ! isset( $item['processor_entity'] ) || empty( $item['processor_entity'] ) ) { + + $formatted_items[$item['processor_id']] = $item; + + } else { + + $item['line_item'] = array_map( function( $line ) use ( $item, $processors, $transient ) { + // only override entity_table for participants being processed + if ( ! empty( ( array ) $transient->participants->{$item['processor_entity']} ) ) + $line['entity_table'] = $processors[$item['processor_entity']]['type']; + + return $line; + + }, $item['line_item'] ); + + if ( isset( $formatted_items[$item['processor_entity']]['line_item'] ) ) { + + $formatted_items[$item['processor_entity']]['line_item'] = array_reduce( $item['line_item'], function( $lines, $line ) { + + $price_field_values_ids = array_column( $lines, 'price_field_value_id' ); + + // there cannot be duplicated price field options for the same item + if ( ! in_array( $line['price_field_value_id'], $price_field_values_ids ) ) + $lines[] = $line; + + return $lines; + + }, $formatted_items[$item['processor_entity']]['line_item'] ); + + } else { + + $formatted_items[$item['processor_entity']]['line_item'] = $item['line_item']; + + } + + if ( ! isset( $formatted_items[$item['processor_entity']]['params'] ) && isset( $item['params'] ) ) + $formatted_items[$item['processor_entity']]['params'] = $item['params']; + + // recalculate all item selections for this line_item and update fee amount + if ( isset( $formatted_items[$item['processor_entity']]['params'] ) ) { + + $fees = array_column( $formatted_items[$item['processor_entity']]['line_item'], 'line_total' ); + + $taxes = array_column( $formatted_items[$item['processor_entity']]['line_item'], 'tax_amount' ); + + $formatted_items[$item['processor_entity']]['params']['fee_amount'] = ! empty( $taxes ) + ? array_sum( array_merge( $fees, $taxes ) ) + : array_sum( $fees ); + + } + + } + + }, $line_items ); + + return $formatted_items; + + } + /** * Preserve join date for current membership being processed. * @@ -275,11 +441,9 @@ public function post_processor( $config, $form, $processid ) { * previous membership of the same type. * * @since 0.4.4 - * @param array $config Processor configuration * @param array $form Form configuration - * @param string $processid The process id */ - function preserve_membership_join_date( $config, $form, $processid ) { + function preserve_membership_join_date( $form ) { $transient = $this->plugin->transient->get(); @@ -344,7 +508,6 @@ function preserve_membership_join_date( $config, $form, $processid ) { } } - return $form; } /** @@ -449,6 +612,146 @@ public function add_payment_processor_hooks( $form, $referrer, $process_id ) { } } + /** + * Create premium. + * + * @since 1.0 + * @param array $order The order/contribution + * @param array $form_values The submitted form values + * @param array $config The processor config + */ + public function create_premium( $order, $form_values, $config ) { + + global $transdata; + + if ( ! isset( $form_values['product_id'] ) ) return; + + if ( ! $order ) return; + + $params = [ + 'product_id' => $form_values['product_id'], + 'contribution_id' => $order['id'], + 'quantity' => 1 // FIXME, can this be set via UI? + ]; + + if ( isset( $transdata['data'][$config['product_id'] . '_option'] ) ) + $params['product_option'] = $transdata['data'][$config['product_id'] . '_option']; + + try { + $premium = civicrm_api3( 'ContributionProduct', 'create', $params ); + } catch ( CiviCRM_API3_Exception $e ) { + // log error + } + } + + /** + * Track CiviDiscounts. + * + * @since 1.0 + * @param array $order The order with it's line items + */ + public function track_cividiscounts( $order ) { + + if ( ! $order || ! isset( $order['id'] ) ) return; + + if ( ! isset( $this->plugin->cividiscount ) ) return; + + if ( empty( $this->plugin->processors->processors['participant']->discounts_used ) ) return; + + if ( empty( $this->plugin->processors->processors['participant']->price_field_refs ) || empty( $this->plugin->processors->processors['participant']->price_field_option_refs ) ) return; + + $price_field_refs = $this->plugin->processors->processors['participant']->price_field_refs; + $price_field_option_refs = $this->plugin->processors->processors['participant']->price_field_option_refs; + $discounts_used = $this->plugin->processors->processors['participant']->discounts_used; + + $price_field_option_refs = array_reduce( $price_field_option_refs, function( $refs, $ref ) { + $refs[$ref['processor_id']] = $ref['field_id']; + return $refs; + }, [] ); + + $participant_ids = array_reduce( $order['line_items'], function( $ids, $item ) { + if ( $item['entity_table'] == 'civicrm_participant' ) + $ids[] = $item['entity_id']; + + return $ids; + }, [] ); + + $participant_items = array_reduce( $order['line_items'], function( $items, $item ) { + if ( $item['entity_table'] == 'civicrm_participant' ) + $items[$item['entity_id']] = $item; + + return $items; + }, [] ); + + $participants = civicrm_api3( 'Participant', 'get', [ + 'id' => [ 'IN' => $participant_ids ], + 'options' => [ 'limit' => 0 ] + ] ); + + if ( $participants['is_error'] && ! $participants['count'] ) return; + + $participants = array_reduce( $participants['values'], function( $participants, $participant ) { + $participants[] = $participant; + return $participants; + }, [] ); + + $refs = array_merge( $price_field_refs, $price_field_option_refs ); + + $transient = $this->plugin->transient->get(); + + array_map( function( $processor_id, $field_id ) use ( $discounts_used, $transient, $order, $participants, $participant_items ) { + + $discount = isset( $discounts_used[$field_id] ) ? $discounts_used[$field_id] : false; + + if ( ! $discount ) return; + + $processor_id = $this->plugin->processors->processors['participant']->parse_processor_id( $processor_id ); + + $event_id = $transient->events->$processor_id->event_id; + + $participant = array_filter( $participants, function( $participant ) use ( $event_id ) { + return $participant['event_id'] == $event_id; + } ); + + $participant = array_pop( $participant ); + + if ( ! $participant ) return; + + try { + $discount_track = civicrm_api3( 'DiscountTrack', 'create', [ + 'item_id' => $discount['id'], + 'contact_id' => $order['contact_id'], + 'contribution_id' => $order['id'], + 'entity_table' => $participant_items[$participant['id']]['entity_table'], + 'entity_id' => $participant['id'], + 'description' => [ $participant_items[$participant['id']]['label'] ] + ] ); + } catch ( CiviCRM_API3_Exception $e ) { + Civi::log()->debug( 'Unable to track discount ' . $discount['code'] . ' for contribution id ' . $order['id'] ); + } + + }, array_keys( $refs ), $refs ); + + } + + /** + * Order has participants. + * + * @since 1.0.1 + * @param array $form_values The submitted values + * @return bool $has_participant + */ + public function has_participant_item( $line_items ) { + + if ( ! is_array( $line_items ) || empty( $line_items ) ) return false; + + $participant_line_items = array_filter( $line_items, function( $item ) { + return $item['line_item'][0]['entity_table'] === 'civicrm_participant'; + } ); + + return ! empty( $participant_line_items ); + } + /** * Send email confirmation/receipt. * @@ -459,9 +762,9 @@ public function add_payment_processor_hooks( $form, $referrer, $process_id ) { */ public function maybe_send_confirmation( $order, $config ) { - if ( ! $order || ! isset( $order ) ) return; + if ( ! $order ) return; - if ( ! $order['is_error'] && isset( $order['id'] ) && $config['is_email_receipt'] ) { + if ( isset( $order['id'] ) && isset( $config['is_email_receipt'] ) ) { try { civicrm_api3( 'Contribution', 'sendconfirmation', [ 'id' => $order['id'] ] ); } catch ( CiviCRM_API3_Exception $e ) { diff --git a/processors/order/order_config.php b/processors/order/order_config.php index c7974b0..5621d67 100644 --- a/processors/order/order_config.php +++ b/processors/order/order_config.php @@ -32,6 +32,12 @@ 'is_test' => 0, ] ); +$campaigns = civicrm_api3( 'Campaign', 'get', [ + 'sequential' => 1, + 'is_active' => 1, + 'options' => [ 'limit' => 0 ], +] ); + ?>

Note: This processor does not process payment transactions on it\'s own, it just creates a Contribution in CiviCRM with single or multiple line items. In order to process live payment transaction, a Caldera Forms add-on is needed. Currently this processor intergrates with Caldera Forms\' Stripe and Authorize.net add-ons for single/one-off payments.', 'caldera-forms-civicrm' ) ); ?>

@@ -102,6 +108,19 @@
+ +
+ +
+ +
+
+
@@ -184,6 +203,14 @@
+ +
+ +
+ {{{_field slug="product_id" type="civicrm_premium"}}} +
+
+
diff --git a/processors/participant/class-participant-processor.php b/processors/participant/class-participant-processor.php index e69de29..42eb06c 100644 --- a/processors/participant/class-participant-processor.php +++ b/processors/participant/class-participant-processor.php @@ -0,0 +1,1199 @@ +plugin = $plugin; + // register this processor + add_filter( 'caldera_forms_get_form_processors', array( $this, 'register_processor' ) ); + + // build price field references, at both render and submission start + add_filter( 'caldera_forms_render_get_form', [ $this, 'get_set_necessary_data' ] ); + add_filter( 'caldera_forms_submit_get_form', [ $this, 'get_set_necessary_data' ], 20 ); + + // filter price fields for notices and discount price fields + add_filter( 'cfc_filter_price_field_config', [ $this, 'filter_price_field_config' ], 10, 4 ); + add_filter( 'cfc_filter_price_field_structure', [ $this, 'render_notices_for_paid_events' ], 10, 3 ); + + // filter form when it renders + add_filter( 'caldera_forms_render_get_form', [ $this, 'pre_render' ] ); + + add_filter( 'cfc_custom_fields_extends_entities', [ $this, 'custom_fields_extend_participant' ] ); + + } + + /** + * Adds this processor to Caldera Forms. + * + * @since 1.0 + * @uses 'caldera_forms_get_form_processors' filter + * @param array $processors The existing processors + * @return array $processors The modified processors + */ + public function register_processor( $processors ) { + + $processors[$this->key_name] = [ + 'name' => __( 'CiviCRM Participant', 'caldera-forms-civicrm' ), + 'description' => __( 'Add CiviCRM Participant to event (for Event registration).', 'caldera-forms-civicrm' ), + 'author' => 'Andrei Mondoc', + 'template' => CF_CIVICRM_INTEGRATION_PATH . 'processors/participant/config.php', + 'pre_processor' => [ $this, 'pre_processor' ], + 'processor' => [ $this, 'processor' ], + 'magic_tags' => [ 'processor_id' ] + ]; + + return $processors; + + } + + /** + * Form pre processor callback. + * + * @since 1.0 + * @param array $config Processor configuration + * @param array $form Form configuration + * @param string $processid The process id + */ + public function pre_processor( $config, $form, $processid ) { + + // cfc transient object + $transient = $this->plugin->transient->get(); + $this->contact_link = 'cid_' . $config['contact_link']; + + // Get form values + $form_values = $this->plugin->helper->map_fields_to_processor( $config, $form, $form_values ); + + + if ( ! empty( $transient->contacts->{$this->contact_link} ) ) { + // event + $event = $this->events[$config['processor_id']]; + + $form_values['contact_id'] = $transient->contacts->{$this->contact_link}; + $form_values['event_id'] = $config['id']; + $form_values['role_id'] = ( $config['role_id'] == 'default_role_id' ) ? $event['default_role_id'] : $config['role_id']; + $form_values['status_id'] = ( $config['status_id'] == 'default_status_id' ) ? 'Registered' : $config['status_id']; // default is registered + + if ( ! empty( $config['campaign_id'] ) ) $form_values['campaign_id'] = $config['campaign_id']; + + // if multiple participant processors, we need to update $this->registrations + $this->registrations = $this->get_participant_registrations( $this->event_ids, $form ); + + $is_registered = is_array( $this->registrations[$config['processor_id']] ); + + // store data in transient if is not registered + if ( ! $is_registered || $this->is_registered_and_same_email_allowed( $is_registered, $event ) ) { + $transient->participants->{$config['processor_id']}->params = $form_values; + $this->plugin->transient->save( $transient->ID, $transient ); + + if ( isset( $config['is_email_receipt'] ) ) { + + add_action( 'cfc_order_post_processor', function( $order, $order_config, $form, $processid ) use ( $event, $config ) { + + if ( ! $order ) return; + + foreach ( $order['line_items'] as $key => $item ) { + + if ( $item['entity_table'] == 'civicrm_participant' ) { + + $participant = civicrm_api3( 'Participant', 'get', [ 'id' => $item['entity_id'] ] ); + + if ( is_array( $participant ) && ! $participant['is_error'] && $participant['values'][$item['entity_id']]['event_id'] == $event['id'] ) { + + $this->send_mail( $participant['values'][$participant['id']], $event, $order ); + break; + } + + } + + } + + }, 10, 4 ); + + } + } + + if ( ( ! $config['is_monetary'] && ! $is_registered ) || ( ! $config['is_monetary'] && $this->is_registered_and_same_email_allowed( $is_registered, $event ) ) ) { + try { + $create_participant = civicrm_api3( 'Participant', 'create', $form_values ); + if ( ! $create_participant['is_error'] && $config['is_email_receipt'] ) { + $this->send_mail( $create_participant['values'][$create_participant['id']], $event ); + } + } catch ( CiviCRM_API3_Exception $e ) { + $error = $e->getMessage() . '

' . $e->getTraceAsString() . '
'; + return [ 'note' => $error, 'type' => 'error' ]; + } + } + } + + } + + /** + * Form processor callback. + * + * @since 1.0 + * @param array $config Processor configuration + * @param array $form Form configuration + * @param string $processid The process id + */ + public function processor( $config, $form, $porcessid ) { + return [ 'processor_id' => $config['processor_id'] ]; + } + + /** + * Autopopulates Form with Civi data. + * + * @uses 'caldera_forms_render_get_form' filter + * @since 1.0 + * @param array $form The form + * @return array $form The modified form + */ + public function pre_render( $form ) { + + // render notices for non paid events + $this->render_notices_for_non_paid_events( $form ); + + return $form; + } + + /** + * Get and set necessary data. + * + * @since 1.0 + * @param array $form The form config + * @return array $form The form config + */ + public function get_set_necessary_data( $form ) { + + // participant processors + $participants = $this->plugin->helper->get_processor_by_type( 'civicrm_participant', $form ); + // bail early + if ( ! $participants ) return $form; + // get event ids + $this->event_ids = $this->get_events_ids( $participants ); + // get events data + $this->events = $this->get_events_config( $this->event_ids ); + // build price field references + $this->price_field_refs = $this->build_price_field_refs( $form ); + // get event registrations + $this->registrations = $this->get_participant_registrations( $this->event_ids, $form ); + // get cividiscounts + if ( isset( $this->plugin->cividiscount ) ) { + $this->event_cividiscounts = $this->plugin->cividiscount->get_event_cividiscounts( $this->event_ids ); + $this->price_field_option_refs = $this->plugin->cividiscount->build_options_ids_refs( $this->price_field_refs, $form ); + $this->options_cividiscounts = $this->plugin->cividiscount->get_options_cividiscounts( $this->price_field_option_refs ); + } + + return $form; + } + + /** + * Build Price Field fields references from Line Item processors for paid events. + * + * @since 1.0 + * @param array $form The form config + * @return array|boolean $price_field_ref References to [ => ], or false + */ + public function build_price_field_refs( $form ) { + + // line item processors + $line_items = $this->plugin->helper->get_processor_by_type( 'civicrm_line_item', $form ); + + if ( ! $line_items ) return false; + + $rendered_fields = array_reduce( $form['fields'], function( $fields, $field ) use ( $form ) { + $config = Caldera_Forms_Field_Util::get_field( $field['ID'], $form, true ); + $fields[] = $config['slug']; + return $fields; + }, [] ); + + return array_reduce( $line_items, function( $refs, $line_item ) use ( $form, $rendered_fields ) { + + if ( ! empty( $line_item['config']['entity_table'] ) && in_array( $line_item['config']['entity_table'], [ 'civicrm_participant', 'civicrm_membership' ] ) ) { + + $price_field_slug = $line_item['config']['price_field_value']; + + if ( strpos( $price_field_slug, '%' ) !== false && substr_count( $price_field_slug, '%' ) > 2 ) { + + $price_field_slug = array_filter( explode( '%', $price_field_slug ) ); + + $price_field_slug = array_intersect( $price_field_slug, $rendered_fields ); + + } else { + + $price_field_slug = str_replace( '%', '', $price_field_slug ); + + } + + // participant processor id + $participant_pid = $this->plugin->helper->get_processor_from_magic( $line_item['config']['entity_params'], $form ); + + // there's no entity_params for civicrm_contribution line_items + if ( ! $participant_pid ) + $participant_pid = $line_item['ID']; + + // price_field field config + if ( is_array( $price_field_slug ) ) { + + if ( count( $price_field_slug ) > 1 ) { + foreach ( $price_field_slug as $key => $field_id ) { + + if ( $key == 0 ) { + + $price_field_field = Caldera_Forms_Field_Util::get_field_by_slug( $field_id, $form ); + + $refs[$participant_pid] = $price_field_field['ID']; + } else { + + $price_field_field = Caldera_Forms_Field_Util::get_field_by_slug( $field_id, $form ); + + $refs[$participant_pid . '#' . $key ] = $price_field_field['ID']; + } + + } + } else { + + $price_field_slug = array_pop( $price_field_slug ); + + $price_field_field = Caldera_Forms_Field_Util::get_field_by_slug( $price_field_slug, $form ); + + $refs[$participant_pid] = $price_field_field['ID']; + } + + } else { + + $price_field_field = Caldera_Forms_Field_Util::get_field_by_slug( $price_field_slug, $form ); + + $refs[$participant_pid] = $price_field_field['ID']; + + } + + } + + return $refs; + + }, [] ); + + } + + /** + * Get events ids. + * + * @since 1.0 + * @param array $participant_processors Array holding participant processor config + * @return array|boolean $event_ids References to [ => ], or false + */ + public function get_events_ids( $participant_processors ) { + + if ( ! $participant_processors ) return false; + + // event ids set in form's participant processors + return array_reduce( $participant_processors, function( $event_ids, $processor ) { + $event_ids[$processor['ID']] = $processor['config']['id']; + return $event_ids; + }, [] ); + + } + + /** + * Get events settings. + * + * @since 1.0 + * @param array $event_ids Array of event ids to get the settings for + * @return array|boolean $events References to [ => (array) ], or false + */ + public function get_events_config( $event_ids ) { + + if ( ! $event_ids ) return false; + + try { + $events_result = civicrm_api3( 'Event', 'get', [ + 'id' => [ 'IN' => array_values( $event_ids ) ] + ] ); + } catch ( CiviCRM_API3_Exception $e ) { + + } + + if ( $events_result['count'] ) + return array_reduce( array_keys( $event_ids ), function( $events, $processor_id ) use ( $event_ids, $events_result ) { + $event = $events_result['values'][$event_ids[$processor_id]]; + $event['participant_count'] = CRM_Event_BAO_Event::getParticipantCount( $event_ids[$processor_id] ); + $events[$processor_id] = $event; + return $events; + }, [] ); + + return false; + } + + /** + * Check and filter discounted price fields options. + * + * @since 1.0 + * @param array $field The field structure + * @param array $form The form config + * @param array $price_field Price field and it's price_field_values + * @param string $current_filter The current filter + * @return array $field The field structure + */ + public function filter_price_field_config( $field, $form, $price_field, $current_filter ) { + + if ( ! $this->price_field_refs ) return $field; + + if ( ! array_search( $field['ID'], $this->price_field_refs ) ) return $field; + + array_map( function( $processor_id, $field_id ) use ( &$field, $form, $price_field, $current_filter ) { + + if ( $field_id != $field['ID'] ) return; + + $processor_id = $this->parse_processor_id( $processor_id ); + + $processor = $form['processors'][$processor_id]; + + $notice = $this->get_notice( $processor_id, $form ); + + $field['config']['option'] = array_reduce( $price_field['price_field_values'], function( $options, $price_field_value ) use ( &$field, $notice, $processor ) { + + $option = $field['config']['option'][$price_field_value['id']]; + // disable option and make sure field is not required + if ( $notice && $notice['disabled'] ) { + $option['disabled'] = true; + $field['required'] = 0; + + // set disable all fields flag + if ( isset( $processor['config']['disable_all_fields'] ) ) $this->plugin->fields->presets_objects['civicrm_price_sets']->disable_all_fields = true; + + } + + $options[$price_field_value['id']] = $option; + + return $options; + }, [] ); + + // check for discounted price field events + $field = $this->handle_discounted_events( $field, $form, $processor_id, $price_field ); + + // do event cividiscounts + if ( isset( $this->plugin->cividiscount ) ) + $field = $this->do_event_autodiscounts( $field, $form, $processor_id, $price_field ); + + if ( $current_filter != 'caldera_forms_render_field_structure' ) + $field = $this->do_event_code_discounts( $field, $form, $processor_id, $price_field ); + + $field = $this->handle_max_count_participants( $field, $form, $processor_id, $price_field ); + + return $field; + + }, array_keys( $this->price_field_refs ), $this->price_field_refs ); + + $field = $this->do_options_autodiscounts( $field, $form, $price_field, $current_filter ); + $field = $this->do_options_code_discounts( $field, $form, $price_field, $current_filter ); + + return $field; + } + + /** + * Filter price field options for discounted events. + * + * @since 1.0 + * @param array $field Field config + * @param array $form Form cofig + * @param string $processor_id Processor id + * @param array $price_field The price field and it's price field values + * @return array $field The filtered field + */ + public function handle_discounted_events( $field, $form, $processor_id, $price_field ) { + + $processor_id = $this->parse_processor_id( $processor_id ); + + // processor config + $processor = $form['processors'][$processor_id]; + + if ( $processor['type'] != $this->key_name ) return $field; + + $event_id = $processor['config']['id']; + + $discount_entity_id = CRM_Core_BAO_Discount::findSet( $event_id, 'civicrm_event' ); + + if ( ! $discount_entity_id ) return $field; + + // get price set id for discounted price set + $price_set_id = CRM_Core_DAO::getFieldValue( 'CRM_Core_BAO_Discount', $discount_entity_id, 'price_set_id' ); + + $price_set = $this->plugin->helper->cached_price_sets()[$price_set_id]; + + // discounted price field + $price_field = array_pop( $price_set['price_fields'] ); + + // filter field options + $field['config']['option'] = array_reduce( $price_field['price_field_values'], function( $options, $price_field_value ) use ( $field ) { + + $option = [ + 'value' => $price_field_value['id'], + 'label' => sprintf( '%1$s - %2$s', $price_field_value['label'], $this->plugin->helper->format_money( $price_field_value['amount'] ) ), + 'calc_value' => $price_field_value['amount'] + ]; + + if ( $price_field_value['tax_amount'] && $this->plugin->helper->get_tax_settings()['invoicing'] ) { + $option['calc_value'] += $price_field_value['tax_amount']; + $option['label'] = $this->plugin->helper->format_tax_label( $price_field_value['label'], $price_field_value['amount'], $price_field_value['tax_amount'] ); + } + + $options[$price_field_value['id']] = $option; + + return $options; + }, [] ); + + return $field; + } + + /** + * Filter price field options for discounted events. + * + * @since 1.0 + * @param array $field Field config + * @param array $form Form cofig + * @param string $processor_id Processor id + * @param array $price_field The price field and it's price field values + * @return array $field The filtered field + */ + public function handle_max_count_participants( $field, $form, $processor_id, $price_field ) { + + $processor_id = $this->parse_processor_id( $processor_id ); + + // processor config + $processor = $form['processors'][$processor_id]; + + if ( $processor['type'] != $this->key_name ) return $field; + + if ( ! count( array_column( $price_field['price_field_values'], 'max_value' ) ) ) return $field; + + $event_id = $processor['config']['id']; + + // filter field options + $field['config']['option'] = array_reduce( $price_field['price_field_values'], function( $options, $price_field_value ) use ( $field, $event_id ) { + + $option = $field['config']['option'][$price_field_value['id']]; + + $current_count = CRM_Event_BAO_Participant::priceSetOptionsCount( $event_id ); + + // disable option based on max value count + if ( array_key_exists( 'max_value', $price_field_value ) && $current_count[$price_field_value['id']] >= $price_field_value['max_value'] ) { + $option['disabled'] = true; + $option['label'] .= ' ' . __( '(Sold out!)', 'caldera-forms-civicrm' ); + } + + $options[$price_field_value['id']] = $option; + + return $options; + }, [] ); + + return $field; + } + + /** + * Do event autidiscounts. + * + * @since 1.0 + * @param array $field Field config + * @param array $form Form cofig + * @param string $processor_id Processor id + * @param array $price_field The price field and it's price field values + * @return array $field The filtered field + */ + public function do_event_autodiscounts( $field, $form, $processor_id, $price_field ) { + + if ( ! isset( $this->plugin->cividiscount ) ) return $field; + + // only for logged in/checksum users + if ( ! $this->plugin->helper->current_contact_data_get() ) return $field; + + $processor_id = $this->parse_processor_id( $processor_id ); + + // processor config + $processor = $form['processors'][$processor_id]; + + if ( $processor['type'] != $this->key_name ) return $field; + + $event_discount = $this->event_cividiscounts[$processor_id]; + + if ( ! $event_discount ) return $field; + + $transient = $this->plugin->transient->get(); + + $contact_link = 'cid_' . $processor['config']['contact_link']; + $contact_id = property_exists( $transient->contacts, $contact_link ) && ! empty( $transient->contacts->$contact_link ) ? $transient->contacts->$contact_link : false; + + // does the contact meet the autodiscount criteria? + if ( $contact_id ) + $is_autodiscount = $this->plugin->cividiscount->check_autodiscount( $event_discount['autodiscount'], $transient->contacts->$contact_link, $processor_id ); + + // bail if not + if ( ! $is_autodiscount ) return $field; + + $this->discounts_used[$field['ID']] = $event_discount; + + // filter field options + $field['config']['option'] = array_reduce( $price_field['price_field_values'], function( $options, $price_field_value ) use ( $field, $event_discount ) { + + $option = $field['config']['option'][$price_field_value['id']]; + + // do discounted option + $options[$price_field_value['id']] = $this->plugin->cividiscount->do_discounted_option( $option, $field, $price_field_value, $event_discount ); + + return $options; + }, [] ); + + return $field; + } + + /** + * Filter price field options for discounted options (pricesets). + * + * @since 1.0 + * @param array $field The field structure + * @param array $form The form config + * @param array $price_field Price field and it's price_field_values + * @param string $current_filter The current filter + * @return array $field The field structure + */ + public function do_options_autodiscounts( $field, $form, $price_field, $current_filter ) { + + if ( ! isset( $this->plugin->cividiscount ) ) return $field; + + // only for logged in/checksum users + if ( ! $this->plugin->helper->current_contact_data_get() ) return $field; + + if ( ! $this->price_field_option_refs ) return $field; + + if ( ! array_key_exists( $field['ID'], $this->price_field_option_refs ) ) return $field; + + array_map( function( $field_id, $options_refs ) use ( &$field, $form, $price_field, $current_filter ) { + + if ( $field_id != $field['ID'] ) return; + + $processor = $form['processors'][$options_refs['processor_id']]; + + if ( $processor['type'] != $this->key_name ) return $field; + + $options_discount = $this->options_cividiscounts[$field['ID']]; + + if ( ! $options_discount ) return $field; + + $transient = $this->plugin->transient->get(); + + $contact_link = 'cid_' . $processor['config']['contact_link']; + $contact_id = property_exists( $transient->contacts, $contact_link ) && ! empty( $transient->contacts->$contact_link ) ? $transient->contacts->$contact_link : false; + + if ( $contact_id ) + $is_autodiscount = $this->plugin->cividiscount->check_autodiscount( $options_discount['autodiscount'], $transient->contacts->$contact_link, $options_refs['processor_id'] ); + + // bail if not + if ( ! $is_autodiscount ) return $field; + + // filter field options + $field['config']['option'] = array_reduce( $price_field['price_field_values'], function( $options, $price_field_value ) use ( $field, $options_discount ) { + + $option = $field['config']['option'][$price_field_value['id']]; + + if ( in_array( $option['value'], $options_discount['pricesets'] ) ) { + + $this->discounts_used[$field['ID']] = $options_discount; + + // do discounted option + $options[$price_field_value['id']] = $this->plugin->cividiscount->do_discounted_option( $option, $field, $price_field_value, $options_discount ); + } else { + $options[$price_field_value['id']] = $option; + } + + return $options; + + }, [] ); + + return $field; + + }, array_keys( $this->price_field_option_refs ), $this->price_field_option_refs ); + + return $field; + + } + + /** + * Do code event discounts. + * + * @since 1.0 + * @param array $field Field config + * @param array $form Form cofig + * @param string $processor_id Processor id + * @param array $price_field The price field and it's price field values + * @return array $field The filtered field + */ + public function do_event_code_discounts( $field, $form, $processor_id, $price_field ) { + + if ( ! isset( $this->plugin->cividiscount ) ) return $field; + + $discount_fields = $this->plugin->cividiscount->get_discount_fields( $form ); + + if ( ! $discount_fields ) return $field; + + $processor_id = $this->parse_processor_id( $processor_id ); + + array_map( function( $discount_field_id, $discount_field ) use ( &$field, $form, $processor_id, $price_field ) { + + $code = Caldera_Forms::get_field_data( $discount_field_id, $form ); + + if ( ! $code ) return; + + $discount = $this->plugin->cividiscount->get_by_code( $code ); + + if ( ! $discount || ! isset( $discount['events'] ) ) return; + + if ( ! in_array( $this->event_ids[$processor_id], $discount['events'] ) ) return; + + $this->discounts_used[$field['ID']] = $discount; + + $field['config']['option'] = array_reduce( $price_field['price_field_values'], function( $options, $price_field_value ) use ( &$field, $discount ) { + + $option = $field['config']['option'][$price_field_value['id']]; + + // do discounted option + $options[$price_field_value['id']] = $this->plugin->cividiscount->do_discounted_option( $option, $field, $price_field_value, $discount ); + + return $options; + + }, [] ); + + return $field; + + }, array_keys( $discount_fields ), $discount_fields ); + + return $field; + + } + + /** + * Do code discounts for options based cividiscounts. + * + * @since 1.0 + * @param array $field The field structure + * @param array $form The form config + * @param array $price_field Price field and it's price_field_values + * @param string $current_filter The current filter + * @return array $field The field structure + */ + public function do_options_code_discounts( $field, $form, $price_field, $current_filter ) { + + if ( ! isset( $this->plugin->cividiscount ) ) return $field; + + $discount_fields = $this->plugin->cividiscount->get_discount_fields( $form ); + + if ( ! $discount_fields ) return $field; + + array_map( function( $discount_field ) use ( &$field, $form, $price_field ) { + + $code = Caldera_Forms::get_field_data( $discount_field['ID'], $form ); + + if ( ! $code ) return; + + $discount = $this->plugin->cividiscount->get_by_code( $code ); + + if ( ! $discount || ! isset( $discount['pricesets'] ) ) return; + + $field['config']['option'] = array_reduce( $price_field['price_field_values'], function( $options, $price_field_value ) use ( $field, $discount ) { + + $option = $field['config']['option'][$price_field_value['id']]; + + if ( in_array( $option['value'], $discount['pricesets'] ) ) { + + $this->discounts_used[$field['ID']] = $discount; + + // do discounted option + $options[$price_field_value['id']] = $this->plugin->cividiscount->do_discounted_option( $option, $field, $price_field_value, $discount ); + } else { + $options[$price_field_value['id']] = $option; + } + + return $options; + + }, [] ); + + }, $discount_fields ); + + return $field; + + } + + /** + * Render notices for paid events. + * + * @since 1.0 + * @param array $field The field structure + * @param array $form The form config + * @param array $price_field Price field and it's price_field_values + * @param string $current_filter The current filter + * @return array $field The field structure + */ + public function render_notices_for_paid_events( $field, $form, $price_field ) { + + if ( ! $this->price_field_refs ) return $field; + + if ( ! array_search( $field['id'], $this->price_field_refs ) ) return $field; + + $processors = $this->plugin->helper->get_processor_by_type( $this->key_name, $form ); + + array_map( function( $processor_id, $field_id ) use ( &$field, $form, $processors ) { + + if ( $field_id != $field['id'] ) return; + + $processor_id = $this->parse_processor_id( $processor_id ); + + // only paid events will have a price set/price field + if ( ! $processors[$processor_id]['config']['is_monetary'] ) return; + + $notice = $this->get_notice( $processor_id, $form ); + + if ( ! $notice ) return; + + // notice html + $template_path = CF_CIVICRM_INTEGRATION_PATH . 'templates/notice.php'; + $html = $this->plugin->html->generate( $notice, $template_path ); + + $field['label_after'] = $field['label_after'] . $html; + + }, array_keys( $this->price_field_refs ), $this->price_field_refs ); + + return $field; + } + + /** + * Render notices for paid events, at the top of the form. + * + * @since 1.o + * @param array $form The form config + * @return $form + */ + public function render_notices_for_non_paid_events( $form ) { + + // participant processors + $processors = $this->plugin->helper->get_processor_by_type( $this->key_name, $form ); + + if ( ! $processors ) return; + + array_map( function( $processor_id, $processor ) use ( $form ) { + + // only for non paid events + if ( $processor['config']['is_monetary'] ) return; + // render notice + $this->get_notice( $processor_id, $form, $add_filter = true ); + + }, array_keys( $processors ), $processors ); + + } + + /** + * Get notice for participant processor. + * + * @since 1.0 + * @param string $processor_id The processor id + * @param array $form The form config + * @param boolean $add_filter Wheather to add 'cfc_notices_to_render' filter + * @return array $notice Notice data array + */ + public function get_notice( $processor_id, $form, $add_filter = false ) { + + $processor = $form['processors'][$processor_id]; + $event = $this->events[$processor_id]; + $participant = $this->registrations[$processor_id]; + + if ( isset( $event['allow_same_participant_emails'] ) && $event['allow_same_participant_emails'] ) return; + + // notices filter + $filter = 'cfc_notices_to_render'; + // cfc_notices_to_render filter callback + $callback = function( $notices ) use ( $event, &$notice ) { + $notices[] = $notice; + return $notices; + }; + + // is registered + if ( $participant && $participant['event_id'] == $event['id'] ) { + $notice = [ + 'type' => 'warning', + 'note' => sprintf( __( 'Oops. It looks like you are already registered for the event %1$s. If you want to change your registration, or you think that this is an error, please contact the site administrator.', 'caldera-forms-civicrm' ), $event['title'] ), + 'disabled' => true + ]; + + if ( ! $add_filter ) return $notice; + // render notices + add_filter( $filter, $callback ); + return; + } + + // registration start date + if ( isset( $event['registration_start_date'] ) && date( 'Y-m-d H:i:s' ) <= $event['registration_start_date'] ) { + $notice = [ + 'type' => 'warning', + 'note' => sprintf( __( 'Registration for the event %s is not yet opened.', 'caldera-forms-civicrm' ), $event['title'] ), + 'disabled' => true + ]; + + if ( ! $add_filter ) return $notice; + // render notices + add_filter( $filter, $callback ); + return; + } + + // registration end date + if ( isset( $event['registration_end_date'] ) && date( 'Y-m-d H:i:s' ) >= $event['registration_end_date'] ) { + $notice = [ + 'type' => 'warning', + 'note' => sprintf( __( 'Registration for the event %1$s was closed on %2$s.', 'caldera-forms-civicrm' ), $event['title'], date_format( date_create( $event['registration_end_date'] ), 'F d, Y H:i' ) ), + 'disabled' => true + ]; + + if ( ! $add_filter ) return $notice; + // render notices + add_filter( $filter, $callback ); + return; + } + + // is participant approval + if ( $event['requires_approval'] ) { + $notice = [ + 'type' => 'warning', + 'note' => sprintf( __( '%s', 'caldera-forms-civicrm' ), $event['approval_req_text'] ), + 'disabled' => false + ]; + + if ( ! $add_filter ) return $notice; + // render notices + add_filter( $filter, $callback ); + return; + } + + // has waitlist and is full + if ( $this->is_full( $event ) && $event['has_waitlist'] ) { + $notice = [ + 'type' => 'warning', + 'note' => sprintf( __( '%s', 'caldera-forms-civicrm' ), $event['waitlist_text'] ), + 'disabled' => false + ]; + + if ( ! $add_filter ) return $notice; + // render notices + add_filter( $filter, $callback ); + return; + } + + // event full + if ( $this->is_full( $event ) && ! $event['has_waitlist'] ) { + $notice = [ + 'type' => 'warning', + 'note' => sprintf( __( '%s', 'caldera-forms-civicrm' ), $event['event_full_text'] ), + 'disabled' => true + ]; + + if ( ! $add_filter ) return $notice; + // render notices + add_filter( $filter, $callback ); + return; + } + + } + + /** + * Get event. + * + * @since 1.0 + * @param int $id Event id + * @return array|boolean $event The event settings, or false + */ + public function get_event( $id ) { + + try { + $event = civicrm_api3( 'Event', 'getsingle', [ 'id' => $id ] ); + } catch( CiviCRM_API3_Exception $e ) { + $error = $e->getMessage() . '

' . $e->getTraceAsString() . '
'; + return [ 'note' => $error, 'type' => 'error' ]; + } + + if ( is_array( $event ) && ! $event['is_error'] ) { + // get count + $event['participant_count'] = CRM_Event_BAO_Event::getParticipantCount( $id ); + return $event; + } + + return false; + } + + /** + * Event is full. + * + * @since 1.0 + * @param array $event The event config + * @return boolean True if full, false otherwise + */ + public function is_full( $event ) { + if ( isset( $event['participant_count'] ) && isset( $event['max_participants'] ) ) + return $event['participant_count'] >= $event['max_participants']; + } + + /** + * Get default status. + * + * @since 1.0 + * @param array $event The event config + * @param array $config Processor config + * @return string $status The participant status + */ + public function default_status( $event, $config ) { + + if ( $config['status_id'] != 'default_status_id' ) return $config['status_id']; + + if ( $event['requires_approval'] ) return 'Awaiting approval'; + + if ( $event['has_waitlist'] && $this->is_full( $event ) ) return 'On waitlist'; + + return 'Registered'; + } + + /** + * Get contact event registrations. + * + * @since 1.0 + * @param array $form The form config + * @return array $registrations The participant registrations + */ + public function get_participant_registrations( $event_ids, $form ) { + + // participant processors + $processors = $this->plugin->helper->get_processor_by_type( $this->key_name, $form ); + if ( ! $processors ) return false; + + // cfc transient + $transient = $this->plugin->transient->get(); + + return array_map( function( $processor ) use ( $transient, $event_ids ) { + + if ( ! isset( $processor['runtimes'] ) ) return; + + $contact_link = 'cid_' . $processor['config']['contact_link']; + + if ( ! isset( $transient->contacts->{$contact_link} ) || empty( $transient->contacts->{$contact_link} ) ) return; + + try { + $participant = civicrm_api3( 'Participant', 'get', [ + 'sequential' => 1, + 'contact_id' => $transient->contacts->{$contact_link}, + 'event_id' => [ 'IN' => array_values( $event_ids ) ] + ] ); + } catch ( CiviCRM_API3_Exception $e ) { + + } + + if ( ! $participant['count'] ) return; + + $registrations = array_filter( $participant['values'], function( $participant ) use ( $processor, $event_ids ) { + return $participant['event_id'] == $event_ids[$processor['ID']]; + } ); + + return array_pop( $registrations ); + + }, $processors ); + } + + public function send_mail( $participant, $event, $order = false ) { + + if ( $order ) { + + $price_set = ''; + $line_items = array_reduce( $order['line_items'], function( $items, $item ) use ( &$price_set ) { + + $price_field = $this->plugin->helper->get_price_set_column_by_id( $item['price_field_id'], 'price_field' ); + + $price_set = empty( $price_set ) ? $this->plugin->helper->get_price_set_column_by_id( $price_field['price_set_id'], 'price_set' ) : $price_set; + + $line_item = array_merge( $price_field['price_field_values'][$item['price_field_value_id']], $item ); + $line_item['field_title'] = $price_field['label']; + + $items[$price_field['price_set_id']][$item['price_field_value_id']] = $line_item; + + return $items; + + }, [] ); + + $template = CRM_Core_Smarty::singleton(); + + $template->assign( 'amount', $order['total_amount'] ); + $template->assign( 'totalAmount', $order['total_amount'] ); + $template->assign( 'currency', $order['currency'] ); + $template->assign( 'receive_date', $order['receive_date'] ); + $template->assign( 'trxn_id', $order['trxn_id'] ); + + $address_fields = [ 'name', 'street_address', 'supplemental_address_1', 'supplemental_address_2', 'supplemental_address_3', 'city', 'state_province_id.abbreviation', 'postal_code', 'country_id.name' ]; + + try { + $billing_address = civicrm_api3( 'Address', 'get', [ + 'sequential' => 1, + 'contact_id' => $participant['contact_id'], + 'location_type_id' => 'Billing', + 'return' => $address_fields + ] ); + } catch ( CiviCRM_API3_Exception $e ) { + + } + + if ( ! $billing_address['is_error'] && $billing_address['count'] ) { + + $address = $billing_address['values'][0]; + + $billing_name = $address['name'] ? "{$address['name']}
" : ''; + $billing_name .= $address['street_address'] ? "{$address['street_address']}
" : ''; + $billing_name .= $address['supplemental_address_1'] ? "{$address['supplemental_address_1']}
" : ''; + $billing_name .= $address['supplemental_address_2'] ? "{$address['supplemental_address_2']}
" : ''; + $billing_name .= $address['supplemental_address_3'] ? "{$address['supplemental_address_3']}
" : ''; + $billing_name .= $address['city'] ? "{$address['city']}, {$address['state_province_id.abbreviation']} {$address['postal_code']}
" : ''; + $billing_name .= $address['country_id.name'] ? "{$address['country_id.name']}
" : ''; + + $template->assign( 'billingName', $billing_name ); + + } + + if ( isset( $order['card_type_id'] ) ) { + $template->assign( 'contributeMode', 'direct' ); + $template->assign( 'credit_card_type', $order['credit_card_type'] ); + $template->assign( 'credit_card_number', $order['pan_truncation'] ); + $template->assign( 'credit_card_exp_date', $order['credit_card_exp_date'] ); + } + + if ( isset( $order['is_pay_later'] ) ) { + $template->assign( 'is_pay_later', $order['is_pay_later'] ); + } + + $values['contributionId'] = $order['id']; + $values['lineItem'] = $line_items; + $values['fee'] = $price_set['price_fields']; + } + + $values['isPrimary'] = 1; + $values['event'] = $event; + $values['participant'] = $participant; + $values['location'] = CRM_Core_BAO_Location::getValues( [ 'entity_id' => $event['id'], 'entity_table' => 'civicrm_event' ] ); + $value['register_date'] = $participant['participant_register_date']; + + $sent = CRM_Event_BAO_Event::sendMail( $participant['contact_id'], $values, $participant['participant_id'] ); + + } + + /** + * Add Participant to extend custom fields autopopulation/presets. + * + * @since 1.0 + * @param array $extends The entites array + * @return array $extends The filtered entities array + */ + public function custom_fields_extend_participant( $extends ) { + $extends[] = 'Participant'; + return $extends; + } + + /** + * Parse processor id string containing '#'. + * + * @since 1.0 + * @param string $processor_id The processor id + * @return string $processor_id The processor id + */ + public function parse_processor_id( $processor_id ) { + return strpos( $processor_id, '#' ) ? substr( $processor_id, 0, strpos( $processor_id, '#' ) ) : $processor_id; + } + + /** + * Checkes whether a participant is registered and same email address is allowed. + * + * @since 1.0 + * @param bool $is_registered Participant is registered + * @param array $event The event settings + * @return bool $is_allowed + */ + public function is_registered_and_same_email_allowed( $is_registered, $event ) { + return $is_registered && isset( $event['allow_same_participant_emails'] ) && $event['allow_same_participant_emails']; + } + +} diff --git a/processors/participant/config.php b/processors/participant/config.php new file mode 100644 index 0000000..ad9d596 --- /dev/null +++ b/processors/participant/config.php @@ -0,0 +1,186 @@ + 1, + 'action' => 'create', +] ); + +$participant_roles = civicrm_api3( 'Participant', 'getoptions', [ + 'sequential' => 1, + 'field' => 'participant_role_id', +] ); + +$participant_statuses = civicrm_api3( 'Participant', 'getoptions', [ + 'sequential' => 1, + 'field' => 'participant_status_id', +] ); + +$participant_fields = []; +foreach ( $fields['values'] as $key => $value ) { + $participant_fields[$value['name']] = $value['title']; +} + +$ignore = [ 'event_id', 'contact_id', 'is_test', 'discount_amount', 'cart_id', 'must_wait', 'transferred_to_contact_id', 'id', 'status_id', 'role_id', 'register_date', 'fee_level', 'is_pay_later', 'fee_amount', 'register_by_id', 'discount_id', 'fee_currency', 'campaign_id' ]; + +$current_fields = [ 'source' ]; + +$events = civicrm_api3( 'Event', 'get', [ + 'sequential' => 1, + 'is_active' => 1, + 'is_online_registration' => 1, + 'is_template' => 0, + 'options' => [ 'limit' => 0 ], +] ); + +$campaigns = civicrm_api3( 'Campaign', 'get', [ + 'sequential' => 1, + 'is_active' => 1, + 'options' => [ 'limit' => 0 ], +] ); + +?> + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +

+ main event.', 'caldera-forms-civicrm' ) ); ?> +

+
+
+ +

+ + + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+
+ + +

+ $value ) { + if( in_array( $key, $current_fields ) ) { + ?> +
+ +
+ +
+
+ + +

+helper->get_participant_custom_fields() as $key => $custom_field ) { ?> +
+ +
+ +
+
+ + + diff --git a/readme.txt b/readme.txt index 12bdb8d..1b673ad 100755 --- a/readme.txt +++ b/readme.txt @@ -2,8 +2,8 @@ Contributors: mecachisenros, needle Tags: civicrm, caldera, forms, integration Requires at least: 4.5 -Tested up to: 4.9.4 -Stable tag: 0.4.3 +Tested up to: 5.1 +Stable tag: 1.0.2 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -16,7 +16,7 @@ The Caldera Forms CiviCRM plugin contains a set of form processors that interact ### Requirements -This plugin requires a minimum of *CiviCRM 4.6* and *Caldera Forms 1.4.2*. +This plugin requires a minimum of *CiviCRM 4.6* and *Caldera Forms 1.7.3*. ### Plugin Development @@ -34,6 +34,19 @@ This plugin is in active development. For feature requests and bug reports (or i == Changelog == += 1.0.2 = +* Added Participant processor for free and paid events +* Price sets improvements +* Tax support +* CiviDiscount integration for Participant registration and option based discounts (if extension is enabled) +* Discount field for CiviDiscount integration (if extension is enabled) +* Added support for Premium and a Premium field +* Autopopulate options are now available to use a field or processor conditionals +* Line Item processor now supports multiple choices options (ie checkbox type price field) +* Support free entry amounts (ie Civi's Other Amount) for Memberships and Participants +* Other minor improvements + + = 0.4.4 = * Bug fixes * Added option to submit empty/blank values for Address entity diff --git a/templates/notice.php b/templates/notice.php new file mode 100644 index 0000000..808515a --- /dev/null +++ b/templates/notice.php @@ -0,0 +1,5 @@ + +
+ +
+ \ No newline at end of file diff --git a/templates/notices.php b/templates/notices.php new file mode 100644 index 0000000..4a5e1bd --- /dev/null +++ b/templates/notices.php @@ -0,0 +1,25 @@ + +
+ + +
+
+ +
+
+ + +
\ No newline at end of file diff --git a/template/thank-you.php b/templates/thank-you.php similarity index 100% rename from template/thank-you.php rename to templates/thank-you.php