Skip to content

Instantly share code, notes, and snippets.

@lizkaraffa
Created February 16, 2016 20:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lizkaraffa/0afa4d02a1dd0f6f0fb6 to your computer and use it in GitHub Desktop.
Save lizkaraffa/0afa4d02a1dd0f6f0fb6 to your computer and use it in GitHub Desktop.
<?php
/**
* Plugin Name: GreekBank Functionality
* Plugin URI: http://zao.is
* Description: Adds all necessary functionality for GreekBank
* Version: 1.0
* Author: Justin Sainton
* Author URI: http://zao.is
*/
/** Dependencies
This plugin depends on the following plugins:
- Gravity Forms (Including User Registration and Braintree Add-Ons)
- Posts 2 Posts
- WP User Groups
- WP Term Meta
It also depends on the user roles of "member" and "treasurer".
Both roles have read and "manage_membership" capabilities.
Treasurers have the additional capability of "manage_orgetganization"
*/
/**
* Registers posts types (Organizations, Terms, Payments)
* @return [type] [description]
*/
function gb_register_post_types() {
$labels = array(
'name' => _x( 'Organizations', 'Post Type General Name', 'text_domain' ),
'singular_name' => _x( 'Organization', 'Post Type Singular Name', 'text_domain' ),
'menu_name' => __( 'Organizations', 'text_domain' ),
'name_admin_bar' => __( 'Organizations', 'text_domain' ),
'parent_item_colon' => __( 'Parent Organization:', 'text_domain' ),
'all_items' => __( 'All Organizations', 'text_domain' ),
'add_new_item' => __( 'Add New Organization', 'text_domain' ),
'add_new' => __( 'Add New', 'text_domain' ),
'new_item' => __( 'New Organization', 'text_domain' ),
'edit_item' => __( 'Edit Organization', 'text_domain' ),
'update_item' => __( 'Update Organization', 'text_domain' ),
'view_item' => __( 'View Organization', 'text_domain' ),
'search_items' => __( 'Search Organizations', 'text_domain' ),
'not_found' => __( 'Not found', 'text_domain' ),
'not_found_in_trash' => __( 'Not found in Trash', 'text_domain' ),
);
$args = array(
'label' => __( 'Organization', 'text_domain' ),
'description' => __( 'Organizations', 'text_domain' ),
'labels' => $labels,
'supports' => array( 'title', 'editor', 'author', 'comments', 'revisions', 'custom-fields' ),
'taxonomies' => array( 'fees', ' dues' ),
'hierarchical' => false,
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'menu_position' => 5,
'menu_icon' => 'dashicons-groups',
'show_in_admin_bar' => true,
'show_in_nav_menus' => false,
'can_export' => true,
'has_archive' => false,
'exclude_from_search' => true,
'publicly_queryable' => true,
'rewrite' => false,
'capability_type' => 'post',
);
register_post_type( 'organization', $args );
$labels = array(
'name' => _x( 'Terms', 'Post Type General Name', 'text_domain' ),
'singular_name' => _x( 'Term', 'Post Type Singular Name', 'text_domain' ),
'menu_name' => __( 'Terms', 'text_domain' ),
'name_admin_bar' => __( 'Terms', 'text_domain' ),
'parent_item_colon' => __( 'Parent Term:', 'text_domain' ),
'all_items' => __( 'All Terms', 'text_domain' ),
'add_new_item' => __( 'Add New Term', 'text_domain' ),
'add_new' => __( 'Add New', 'text_domain' ),
'new_item' => __( 'New Term', 'text_domain' ),
'edit_item' => __( 'Edit Term', 'text_domain' ),
'update_item' => __( 'Update Term', 'text_domain' ),
'view_item' => __( 'View Term', 'text_domain' ),
'search_items' => __( 'Search Terms', 'text_domain' ),
'not_found' => __( 'Not found', 'text_domain' ),
'not_found_in_trash' => __( 'Not found in Trash', 'text_domain' ),
);
$args = array(
'label' => __( 'Term', 'text_domain' ),
'description' => __( 'Terms', 'text_domain' ),
'labels' => $labels,
'supports' => array( 'title', 'editor', 'author', 'comments', 'revisions', ),
'taxonomies' => array( 'fees', ' dues' ),
'hierarchical' => false,
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'menu_position' => 5,
'menu_icon' => 'dashicons-welcome-learn-more',
'show_in_admin_bar' => true,
'show_in_nav_menus' => false,
'can_export' => true,
'has_archive' => false,
'exclude_from_search' => true,
'publicly_queryable' => true,
'rewrite' => false,
'capability_type' => 'post',
);
register_post_type( 'terms', $args );
$labels = array(
'name' => _x( 'Payments', 'Post Type General Name', 'text_domain' ),
'singular_name' => _x( 'Payment', 'Post Type Singular Name', 'text_domain' ),
'menu_name' => __( 'Payments', 'text_domain' ),
'name_admin_bar' => __( 'Payments', 'text_domain' ),
'parent_item_colon' => __( 'Parent Payment:', 'text_domain' ),
'all_items' => __( 'All Payments', 'text_domain' ),
'add_new_item' => __( 'Add New Payment', 'text_domain' ),
'add_new' => __( 'Add New', 'text_domain' ),
'new_item' => __( 'New Payment', 'text_domain' ),
'edit_item' => __( 'Edit Payment', 'text_domain' ),
'update_item' => __( 'Update Payment', 'text_domain' ),
'view_item' => __( 'View Payment', 'text_domain' ),
'search_items' => __( 'Search Payments', 'text_domain' ),
'not_found' => __( 'Not found', 'text_domain' ),
'not_found_in_trash' => __( 'Not found in Trash', 'text_domain' ),
);
$args = array(
'label' => __( 'Payment', 'text_domain' ),
'description' => __( 'Payments', 'text_domain' ),
'labels' => $labels,
'supports' => array( 'title', 'editor', 'author', 'comments', 'revisions', ),
'taxonomies' => array( 'fees', ' dues' ),
'hierarchical' => false,
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'menu_position' => 5,
'menu_icon' => 'dashicons-clipboard',
'show_in_admin_bar' => true,
'show_in_nav_menus' => false,
'can_export' => true,
'has_archive' => false,
'exclude_from_search' => true,
'publicly_queryable' => true,
'rewrite' => false,
'capability_type' => 'post',
);
register_post_type( 'payment', $args );
}
add_action( 'init', 'gb_register_post_types' );
/**
* Registers standard taxonomies (fees/dues).
* @return [type] [description]
*/
function gb_register_taxonomies() {
$labels = array(
'name' => _x( 'Type', 'Taxonomy General Name', 'text_domain' ),
'singular_name' => _x( 'Type', 'Taxonomy Singular Name', 'text_domain' ),
'menu_name' => __( 'Types', 'text_domain' ),
'all_items' => __( 'All Types', 'text_domain' ),
'parent_item' => __( 'Parent Type', 'text_domain' ),
'parent_item_colon' => __( 'Parent Type:', 'text_domain' ),
'new_item_name' => __( 'New Type Name', 'text_domain' ),
'add_new_item' => __( 'Add New Type', 'text_domain' ),
'edit_item' => __( 'Edit Type', 'text_domain' ),
'update_item' => __( 'Update Type', 'text_domain' ),
'view_item' => __( 'View Type', 'text_domain' ),
'separate_items_with_commas' => __( 'Separate types with commas', 'text_domain' ),
'add_or_remove_items' => __( 'Add or remove types', 'text_domain' ),
'choose_from_most_used' => __( 'Choose from the most used', 'text_domain' ),
'popular_items' => __( 'Popular Items', 'text_domain' ),
'search_items' => __( 'Search Items', 'text_domain' ),
'not_found' => __( 'Not Found', 'text_domain' ),
);
$args = array(
'labels' => $labels,
'hierarchical' => false,
'public' => true,
'show_ui' => true,
'show_admin_column' => true,
'show_in_nav_menus' => true,
'show_tagcloud' => true,
'rewrite' => false,
);
register_taxonomy( 'payment_type', array( 'payment' ), $args );
}
add_action( 'init', 'gb_register_taxonomies' );
function gb_register_default_user_taxonomies() {
remove_action( 'init', 'wp_register_default_user_taxonomies' );
new WP_User_Taxonomy( 'member_category', 'members/categories', array( 'public' => true, 'singular' => 'Category', 'plural' => 'Categories' ) );
}
add_action( 'init', 'gb_register_default_user_taxonomies', 8 );
/**
* [gb_modify_payment_total description]
*
* @param [type] $submission_data [description]
* @param [type] $feed [description]
* @param [type] $form [description]
* @param [type] $entry [description]
* @return [type] [description]
*/
function gb_modify_payment_total( $submission_data, $feed, $form, $entry ) {
$surcharge = apply_filters( 'gb_surcharge_percentage', 1.0149 );
$submission_data['payment_amount'] = round( rgar( $entry, '2' ) * $surcharge , 2 ) ;
return $submission_data;
}
add_filter( 'gform_submission_data_pre_process_payment_7', 'gb_modify_payment_total', 10, 4 );
function gb_enqueue_scripts() {
wp_enqueue_script( 'greek-bank', plugins_url( 'greek-bank.js', __FILE__ ), array( 'jquery' ), microtime() );
wp_localize_script( 'greek-bank', 'greekBankGlobal', array(
'logout_url' => wp_logout_url( home_url() )
) );
if ( is_page( 'treasurer-center' ) ) {
wp_enqueue_script( 'greek-bank-tablesorter', plugins_url( 'tablesorter.js', __FILE__ ), array( 'jquery' ) );
wp_enqueue_script( 'greek-bank-chart' , 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js', array( 'jquery' ) );
wp_localize_script( 'greek-bank-chart', 'greekChartVars', array(
'amount_paid' => gb_current_month_amount_paid(),
'amount_due' => gb_current_term_amount_due(),
'amount_past_due' => gb_current_month_amount_past_due(),
'amount_of_fees' => gb_current_month_amount_paid( 'fees' ),
'amount_of_dues' => gb_term_total_dues(),
'current_payments' => gb_members_by_status(),
'thirty_days_late' => gb_members_by_status( 'thirty_days_late' ),
'sixty_days_late' => gb_members_by_status( 'sixty_days_late' ),
'more_than_sixty' => gb_members_by_status( 'more_than_sixty' ),
'ajaxurl' => admin_url( 'admin-ajax.php', 'relative' ),
) );
}
}
function gb_has_fees( $org_id = 0 ) {
$semester = gb_get_current_semester( $org_id );
if ( ! $semester ) {
return false;
}
return 'No' !== -$semester->{'9'};
}
add_filter( 'if_menu_conditions', function( $conditions ) {
$conditions[] = array(
'name' => __( 'User is Member', 'if-menu' ),
'condition' => 'gb_is_user_member'
);
$conditions[] = array(
'name' => __( 'User is Treasurer', 'if-menu' ),
'condition' => 'gb_is_user_treasurer'
);
return $conditions;
} );
function gb_is_user_member() {
return current_user_can( 'manage_membership' ) && ! current_user_can( 'manage_organization' );
}
function gb_is_user_treasurer() {
return current_user_can( 'manage_organization' );
}
add_action( 'wp_enqueue_scripts', 'gb_enqueue_scripts' );
function gb_treasurer_wrapper() {
if ( ! current_user_can( 'manage_organization' ) && ! current_user_can( 'manage_options' ) ) {
?><script>window.location = <?php echo wp_json_encode( home_url( 'member-profile' ) ); ?></script><?php
die;
}
ob_start();
get_template_part( 'treasurer-wrapper' );
return ob_get_clean();
}
add_shortcode( 'member_cp', 'gb_treasurer_wrapper' );
function gb_member_profile() {
ob_start();
get_template_part( 'member-profile' );
return ob_get_clean();
}
add_shortcode( 'member_profile', 'gb_member_profile' );
add_action( 'genesis_after_header', function() {
if ( is_page( 'treasurer-center' ) ) {
get_template_part( 'modals' );
?>
<script>
jQuery(document).ready( function( $ ) {
$.tablesorter.addParser({
// set a unique id
id: 'thousands',
is: function(s) {
// return false so this parser is not auto detected
return false;
},
format: function(s) {
// format your data for normalization
return s.replace('$','').replace(/,/g,'');
},
// set type, either numeric or text
type: 'numeric'
});
$( '.member-roster' ).tablesorter( {
headers: {
0: { sorter: false },
4: { sorter: 'thousands' },
}
} );
});
</script>
<style type="text/css">
th.header::-moz-selection { background:transparent; }
th.header::selection { background:transparent; }
th.header { cursor:pointer; }
table th.header:after {
content:'';
position: relative;
bottom: .9em;
left: .5em;
border-width:0 4px 4px;
border-style:solid;
border-color:#404040 transparent;
visibility:hidden;
}
table th.header:hover:after {
visibility:visible;
}
table th.headerSortUp:after,
table th.headerSortDown:after,
table th.headerSortDown:hover:after {
visibility:visible;
opacity:0.4;
}
table th.headerSortUp:after {
border-bottom:none;
border-width:4px 4px 0;
bottom: 0;
top: .5em;
}
</style>
<?php
} else if ( is_page( 'member-profile' ) ) {
get_template_part( 'make-a-payment-modal' );
}
} );
add_filter( 'body_class', function( $classes ) {
if ( is_singular() ) {
$classes[] = get_post()->post_name;
}
return $classes;
} );
/**
* Auto login to site after GF User Registration Form Submittal
* Also connects gb_get_member_organization()->post_title to organization
*/
function gb_autologin_gfregistration( $user_id, $config, $entry, $password ) {
if ( '4' != $entry['form_id'] ) {
return;
}
gb_connect_treasurer_to_organization( $user_id, $entry['post_id'] );
gb_connect_member_to_organization( $user_id, $entry['post_id'] );
wp_set_auth_cookie( $user_id, false, '' );
}
add_action( 'gform_user_registered','gb_autologin_gfregistration', 10, 4 );
/**
* Connect members to organizations on New Member Form submission.
*/
function gb_new_member_connection( $user_id, $config, $entry ) {
if ( '12' != $entry['form_id'] ) {
return;
}
$organization = gb_get_member_organization();
gb_connect_member_to_organization( $user_id, $organization->ID );
$category = rgar( $entry, '4' );
$category = get_term_by( 'id', $category, 'member_category' );
wp_set_terms_for_user( $user_id, 'member_category', array( $category->slug ) );
}
add_action( 'gform_user_registered','gb_new_member_connection', 9, 3 );
/**
* Returns total number of members per organization.
*
* @param integer $org_id If no parameter passed, uses organization of logged in user.
*
* @return integer Total number of members.
*/
function gb_total_members( $org_id = 0 ) {
return count( _get_members( $org_id ) );
}
function gb_term_start_date() {
return date( get_option( 'date_format' ), strtotime( gb_get_current_semester()->{'13'} ) );
}
function gb_first_payment_date() {
return date( get_option( 'date_format' ), strtotime( gb_get_current_semester()->{'4'} ) );
}
function gb_last_payment_date() {
return date( get_option( 'date_format' ), strtotime( gb_get_current_semester()->{'5'} ) );
}
function gb_late_fee() {
return gb_get_current_semester()->{'11'};
}
function gb_late_fee_days() {
return gb_get_current_semester()->{'10'};
}
function gb_bank_account_last_four( $organization_id = 0 ) {
if ( ! $organization_id ) {
$organization = gb_get_member_organization();
}
return substr( $organization->bank_account, -4, 4 );
}
function gb_routing_number_last_four( $organization_id = 0 ) {
if ( ! $organization_id ) {
$organization = gb_get_member_organization();
}
return substr( $organization->bank_routing, -4, 4 );
}
function gb_payment_plan_info() {
$plans = gb_payment_plan_mapping();
foreach ( $plans as $_plan => $dates ) :
?>
<h4><?php echo esc_html( ucwords( $_plan ) ); ?> Due Dates</h4>
<?php foreach ( $dates as $date ) : ?>
<p><?php echo date( get_option( 'date_format' ), strtotime( $date ) ); ?></p>
<?php endforeach;
endforeach;
}
function gb_user_payment_plan( $user = false ) {
if ( ! $user ) {
$user = wp_get_current_user();
}
return $user->payment_plan;
}
function gb_user_email() {
return wp_get_current_user()->user_email;
}
function gb_user_full_name() {
return wp_get_current_user()->display_name;
}
function gb_user_phone_number() {
return wp_get_current_user()->phone_number;
}
function gb_user_university( $user_id = 0 ) {
$org = gb_get_member_organization( $user_id );
if ( ! $org ) {
return '';
}
return $org->university;
}
function gb_get_member_organization( $user_id = 0 ) {
static $orgs;
if ( ! $user_id ) {
$user_id = get_current_user_id();
}
if ( ! empty( $orgs[ $user_id ] ) ) {
return $orgs[ $user_id ];
}
$orgs[ $user_id ] = get_post( gb_get_organization_id( $user_id ) );
return $orgs[ $user_id ];
}
function gb_connections() {
// Connect Organizations to Members
p2p_register_connection_type( array(
'name' => 'many_members',
'from' => 'organization',
'to' => 'user',
'cardinality' => 'one-to-many',
'title' => array(
'from' => __( 'Organization to Member', 'my-textdomain' ),
'to' => __( 'Members', 'my-textdomain' )
),
'from_labels' => array(
'singular_name' => __( 'Organization', 'my-textdomain' ),
'search_items' => __( 'Search organization', 'my-textdomain' ),
'not_found' => __( 'No organization found.', 'my-textdomain' ),
'create' => __( 'Connect Members', 'my-textdomain' ),
),
'to_labels' => array(
'singular_name' => __( 'Member', 'my-textdomain' ),
'search_items' => __( 'Search members', 'my-textdomain' ),
'not_found' => __( 'No members found.', 'my-textdomain' ),
'create' => __( 'Connect members', 'my-textdomain' ),
)
) );
// Connect Organizations to Treasurers
p2p_register_connection_type( array(
'name' => 'single_treasurer',
'from' => 'organization',
'to' => 'user',
'to_query_vars' => array( 'role' => 'treasurer' ),
'title' => array(
'from' => __( 'Organization to Treasurer', 'my-textdomain' ),
'to' => __( 'Treasurer', 'my-textdomain' )
),
'from_labels' => array(
'singular_name' => __( 'Organization', 'my-textdomain' ),
'search_items' => __( 'Search organization', 'my-textdomain' ),
'not_found' => __( 'No organization found.', 'my-textdomain' ),
'create' => __( 'Create Connections', 'my-textdomain' ),
),
'to_labels' => array(
'singular_name' => __( 'Treasurer', 'my-textdomain' ),
'search_items' => __( 'Search treasurer', 'my-textdomain' ),
'not_found' => __( 'No Treasurer found.', 'my-textdomain' ),
'create' => __( 'Connect treasurer', 'my-textdomain' ),
)
) );
// Connect Many Payments to One User
p2p_register_connection_type( array(
'name' => 'payments_to_user',
'from' => 'payment',
'to' => 'user',
'cardinality' => 'many-to-one',
'title' => array(
'from' => __( 'Payment to User', 'my-textdomain' ),
'to' => __( 'User', 'my-textdomain' )
)
) );
// Connect Many Semester to One User
p2p_register_connection_type( array(
'name' => 'semesters_to_user',
'from' => 'terms',
'to' => 'user',
'cardinality' => 'many-to-one',
'title' => array(
'from' => __( 'Semester to User', 'my-textdomain' ),
'to' => __( 'User', 'my-textdomain' )
)
) );
// Connect Payment to Organization
p2p_register_connection_type( array(
'name' => 'payments_to_organization',
'from' => 'payment',
'to' => 'organization',
'title' => array(
'from' => __( 'Payment to Organization', 'my-textdomain' ),
'to' => __( 'Organization', 'my-textdomain' )
)
) );
// Connect Payments to Organization
p2p_register_connection_type( array(
'name' => 'payments_to_semester',
'from' => 'payment',
'to' => 'terms',
'cardinality' => 'many-to-one',
'title' => array(
'from' => __( 'Payment to Semester', 'my-textdomain' ),
'to' => __( 'Organization', 'my-textdomain' )
)
) );
// Connect Semester to Organization
p2p_register_connection_type( array(
'name' => 'semester_to_organization',
'from' => 'terms',
'to' => 'organization',
'title' => array(
'from' => __( 'Semester to Organization', 'my-textdomain' ),
'to' => __( 'Organization', 'my-textdomain' )
)
) );
}
add_action( 'p2p_init', 'gb_connections' );
/**
* Connects treasurer to organization
* Occurs on Sign-Up Form submission
*
* @return [type] [description]
*/
function gb_connect_treasurer_to_organization( $treasurer_id, $org_id ) {
p2p_type( 'single_treasurer' )->connect( $org_id, $treasurer_id, array( 'date' => current_time( 'mysql' ) ) );
}
/**
* Connects member to organization
* Occurs anytime a member is added.
*
* @return [type] [description]
*/
function gb_connect_member_to_organization( $member_id, $org_id ) {
p2p_type( 'many_members' )->connect( $org_id, $member_id, array( 'date' => current_time( 'mysql' ) ) );
}
/**
* Connects payment to member (or treasurer) who makes the payment
* Occurs anytime a successful payment occurs.
*
* @return [type] [description]
*/
function gb_connect_payment_to_member( $payment_id, $member_id ) {
p2p_type( 'payments_to_user' )->connect( $payment_id, $member_id, array( 'date' => current_time( 'mysql' ) ) );
}
/**
* Connects semester to member (or treasurer).
* Occurs anytime a semester is created or a member is added.
*
* @return [type] [description]
*/
function gb_connect_semester_to_member( $semester_id, $member_id ) {
p2p_type( 'semesters_to_user' )->connect( $semester_id, $member_id, array( 'date' => current_time( 'mysql' ) ) );
}
/**
* Connects payment to organization that receives the payment
* Occurs anytime a successful payment occurs.
*
* @return [type] [description]
*/
function gb_connect_payment_to_organization( $payment_id, $org_id ) {
p2p_type( 'payments_to_organization' )->connect( $payment_id, $org_id, array( 'date' => current_time( 'mysql' ) ) );
}
/**
* Connects payment to semester that the payment is received in.
* Occurs anytime a successful payment occurs.
*
* @return [type] [description]
*/
function gb_connect_payment_to_semester( $payment_id, $semester_id ) {
p2p_type( 'payments_to_semester' )->connect( $payment_id, $semester_id, array( 'date' => current_time( 'mysql' ) ) );
}
/**
* Connects semester to organization.
* Occurs, generally, when treasurer settings form is filled out.
*
* @return [type] [description]
*/
function gb_connect_semester_to_organization( $semester_id, $organization_id ) {
p2p_type( 'semester_to_organization' )->connect( $semester_id, $organization_id, array( 'date' => current_time( 'mysql' ) ) );
}
function gb_get_member_payments( $member = false, $args = array() ) {
if ( ! $member ) {
$member = get_current_user_id();
}
if ( is_a( $member, 'WP_User' ) ) {
$member = $member->ID;
}
$args = wp_parse_args( $args, array(
'connected_type' => 'payments_to_user',
'connected_items' => $member,
'nopaging' => true,
) );
$payments = new WP_Query( $args );
return $payments->have_posts() ? $payments->posts : array();
}
function gb_delete_member( $id ) {
if ( ! current_user_can( 'manage_organization' ) && ! current_user_can( 'delete_users' ) ) {
return false;
}
if ( $id == get_current_user_id() ) {
return false;
}
if ( user_can( $id, 'manage_membership' ) ) {
return wp_delete_user( $id );
}
return false;
}
function gb_ajax_delete_member() {
if ( ! isset( $_POST['bulk'] ) ) {
$members = array( absint( $_POST['id'] ) );
} else {
parse_str( $_REQUEST['id'], $params );
$members = $params['member'];
}
foreach ( $members as $member ) {
$delete = gb_delete_member( $member );
if ( ! $delete ) {
wp_send_json_error( $members );
}
}
wp_send_json_success( $members );
}
add_action( 'wp_ajax_delete_members', 'gb_ajax_delete_member' );
function gb_ajax_bulk_edit_member_categories() {
$params = array();
parse_str( $_REQUEST['ids'], $params );
$members = $params['member'];
$category = $_REQUEST['category'];
$category = get_term_by( 'id', $category, 'member_category' );
foreach ( $members as $member ) {
wp_set_terms_for_user( $member, 'member_category', array( $category->slug ) );
}
wp_send_json_success( $members );
}
add_action( 'wp_ajax_bulk_edit_member_categories', 'gb_ajax_bulk_edit_member_categories' );
function gb_get_terms_for_user( $user, $taxonomy = 'member_category', $args = array() ) {
if ( is_numeric( $user ) ) {
$user = new WP_User( $user );
}
$defaults = array(
'fields' => 'all_with_object_id',
'meta_query' => array(
array(
'key' => 'organization_id',
'value' => gb_get_organization_id( get_current_user_id() )
)
)
);
$args = wp_parse_args( $args, $defaults );
return wp_get_object_terms( $user->ID, $taxonomy, $args );
}
function gb_ajax_get_member() {
$id = $_REQUEST['id'];
$user = get_user_by( 'id', $id );
if ( ! $user ) {
wp_send_json_error();
}
$payments = gb_get_member_payments( $id );
$terms = gb_get_terms_for_user( $user, 'member_category' );
$total_paid = get_member_amount_paid( $user );
$balance_due = get_member_remaining_balance( $user );
ob_start();
get_recent_payments_view( $user );
$recent = ob_get_clean();
ob_start();
gb_member_details_transaction_summary( $user );
$summary = ob_get_clean();
$object = array(
'user' => array(
'first' => $user->first_name,
'last' => $user->last_name,
'email' => $user->user_email,
),
'categories' => $terms,
'payment_plan' => $user->payment_plan,
'financial' => array(
'payments' => $payments,
'total_paid' => $total_paid,
'balance_due' => $balance_due,
'recent_view' => $recent,
'summary' => $summary
)
);
wp_send_json_success( $object );
}
add_action( 'wp_ajax_get_member', 'gb_ajax_get_member' );
function gb_member_details_transaction_summary( $member = false ) {
foreach ( gb_get_member_payments( $member, array( 'posts_per_page' => 3, 'nopaging' => false ) ) as $payment ) :
$due_date = $payment->due_date;
if ( ! empty( $due_date ) ) {
$due_date = date( 'M j', $due_date );
} else {
$due_date = '--';
}
$minus = 'paid' == $payment->post_status ? '- ' : '';
?>
<tr>
<td><?php echo date( 'M j', strtotime( $payment->post_date ) ); ?></td>
<td><?php echo esc_html( $due_date ); ?></td>
<td><?php echo esc_html( $payment->post_title ); ?></td>
<td class="<?php echo esc_html( $payment->post_status ); ?>">
<?php echo $minus; ?>$<?php echo number_format( floatval( $payment->total_amount ), 2, '.', '' ); ?></td>
</tr>
<?php endforeach;
}
function gb_ajax_update_member() {
$id = $_REQUEST['id'];
$user = get_user_by( 'id', $id );
if ( ! $user ) {
wp_send_json_error();
}
$category = get_term_by( 'id', $_POST['term'], 'member_category' );
$terms = wp_set_terms_for_user( $user->ID, 'member_category', array( $category->slug ) );
$first = sanitize_text_field( $_POST['first'] );
$last = sanitize_text_field( $_POST['last'] );
$plan = strtolower( $_POST['plan'] );
if ( ! empty( $plan ) && in_array( $plan, array_keys( gb_payment_plan_mapping( $user ) ) ) ) {
update_user_meta( $user->ID, 'payment_plan', $plan );
}
$update = wp_update_user( array(
'ID' => $user->ID,
'first_name' => $first,
'last_name' => $last,
'user_email' => sanitize_email( $_POST['email'] ),
'display_name' => $first . ' ' . $last
) );
if ( is_wp_error( $update ) ) {
wp_send_json_error( $update );
}
wp_send_json_success(
array(
'balance_due' => '$' . get_member_remaining_balance( $user ),
'terms' => gb_get_terms_for_user( $user )
)
);
}
add_action( 'wp_ajax_update_member', 'gb_ajax_update_member' );
function gb_get_members( $args = array() ) {
$organization_id = gb_get_organization_id( get_current_user_id() );
$members = _get_members( $organization_id );
foreach ( $members as $member ) :
?>
<tr id="member-<?php echo $member->ID; ?>" data-member-id="<?php echo $member->ID; ?>">
<td class="select">
<input id="select-<?php echo $member->ID; ?>" type="checkbox" name="member[]" value="<?php echo $member->ID; ?>" />
</td>
<td>
<span class="display-name"><?php echo esc_html( $member->display_name ); ?></span>
<div class="row-actions">
<span class="add-transaction">
<a href="#">Add Transaction</a> |
</span>
<span class="details">
<a href="#">Details</a> |
</span>
<span class="delete">
<a href="#" data-delete-nonce="<?php echo wp_create_nonce( 'delete-member-' . $member->ID ); ?>">Delete</a>
</span>
</div>
</td>
<td><?php echo get_member_due_date( $member );?></td>
<td><?php echo '$' . get_member_current_balance( $member ); ?></td>
<td><?php echo gb_get_member_category( $member->ID ); ?></td>
<td class="total-paid">$<?php echo get_member_amount_paid( $member ); ?></td>
<td class="remaining-balance">$<?php echo get_member_remaining_balance( $member ); ?></td>
</tr>
<?php
endforeach;
}
function gb_get_member_category( $member ) {
$categories = gb_get_terms_for_user( $member );
if ( empty( $categories ) ) {
return '';
} else {
return $categories[0]->name;
}
}
/**
* Gets the amount a member has paid in the current semester.
*
* @param [type] $member [description]
* @return [type] [description]
*/
function get_member_amount_paid( $member = false, $format = true, $args = array() ) {
if ( ! $member ) {
$member = wp_get_current_user();
}
$payments = new WP_Query( wp_parse_args( $args, array(
'connected_type' => 'payments_to_user',
'connected_items' => $member->ID,
'nopaging' => true,
'payment_type' => 'payment'
) ) );
$total = 0;
$org_id = gb_get_organization_id( $member->ID );
$semester = gb_get_current_semester( $org_id );
if ( ! $semester ) {
return 0.00;
}
$semester_id = $semester->ID;
if ( ! $payments->have_posts() ) {
return $total;
}
foreach ( $payments->posts as $payment ) {
$current = p2p_type( 'payments_to_semester' )->get_p2p_id( $payment->ID, $semester_id );
if ( $current ) {
$total += $payment->total_amount;
}
}
if ( $format ) {
return number_format( $total, 2, '.', '' );
} else {
return $total;
}
}
function gb_get_member_dues( $member = false ) {
if ( ! $member ) {
$member = wp_get_current_user();
}
$categories = gb_get_terms_for_user( $member );
if ( empty( $categories ) ) {
return false;
}
$member_category = $categories[0];
return get_term_meta( $member_category->term_id, 'due_amount', true );
}
/**
* Gets member's remaining balance
* @param [type] $member [description]
* @return [type] [description]
*/
function get_member_remaining_balance( $member = false, $format = true ) {
if ( ! $member ) {
$member = wp_get_current_user();
}
// Get initial amount due from Member Category
$amount = gb_get_member_dues( $member );
// Add all unpaid fees
$amount += get_member_amount_paid( $member, false, array(
'payment_type' => 'fees',
'post_status' => 'unpaid'
)
);
// Add all unpaid dues
$amount += get_member_amount_paid( $member, false, array( 'payment_type' => 'dues', 'post_status' => 'unpaid' ) );
// Subtract all payments made
$amount -= get_member_amount_paid( $member, false );
if ( $format ) {
return number_format( $amount, 2, '.', '' );
} else {
return $amount;
}
}
/**
* Handles redirect scenarios on login.
*
* First scenario: Logging in as a member. Should redirect to member profile.
* Second scenario: Logging in as a treasurer who _has_ filled out all settings info.
* Third scenario: Loggig in as a treasurer who _has not_ filled out all settings info.
*
* @param string $redirect The redirect destination URL.
* @param string $requested_redirect The requested redirect destination URL passed as a parameter.
* @param WP_User|WP_error $user WP_User object if login was successful, WP_Error object otherwise.
*
* @return string $redirect The redirect destination URL.
*/
function gb_handle_login_redirect( $redirect, $requested_redirect, $user ) {
if ( is_wp_error( $user ) ) {
return $redirect;
}
if ( user_can( $user, 'manage_organization' ) ) {
if ( ! gb_completed_treasurer_settings( $user->ID ) ) {
$redirect = home_url( 'treasurer-center/treasurer-settings-form' );
}
} else if ( user_can( $user, 'manage_membership' ) ) {
$redirect = home_url( 'member-profile' );
}
return $redirect;
}
add_filter( 'login_redirect', 'gb_handle_login_redirect', 10, 3 );
/**
* When saving the treasurer form, we create a semester if we're able to, which means all fields are submitted
* Upon creating the semester, we connect it to the organization.
* Therefore, if a semester exists, then the form has been completed.
*
* @return boolean Whether or not the treasurer settings have been completed.
*/
function gb_completed_treasurer_settings( $treasurer_id = 0 ) {
if ( ! $treasurer_id && current_user_can( 'manage_organization' ) ) {
$treasurer_id = get_current_user_id();
}
// Get Organization ID from Treasurer
$organization_id = gb_get_organization_id( $treasurer_id );
$semester = new WP_Query( array(
'connected_type' => 'semester_to_organization',
'connected_items' => $organization_id,
'nopaging' => true,
) );
return $semester->have_posts();
}
/**
* Get organization ID from Member (Treasurer or Member)
*
* @param [type] $id [description]
* @return [type] [description]
*/
function gb_get_organization_id( $id ) {
static $org_ids;
if ( ! empty( $org_ids[ $id ] ) ) {
return $org_ids[ $id ];
}
$connected_type = current_user_can( 'manage_organization' ) ? 'single_treasurer' : 'many_members';
$organization = new WP_Query( array(
'connected_type' => $connected_type,
'connected_items' => $id,
'nopaging' => true,
) );
$org_ids[ $id ] = $organization->have_posts() ? $organization->posts[0]->ID : 0;
return apply_filters( 'gb_get_organization_id', $org_ids[ $id ], $organization, $id );
}
function gb_get_current_semester( $organization_id = 0 ) {
static $semesters;
if ( ! $organization_id ) {
$org = gb_get_member_organization();
if ( ! $org ) {
return null;
}
$organization_id = $org->ID;
}
if ( ! empty( $semesters[ $organization_id ] ) ) {
return $semesters[ $organization_id ];
}
$semester = new WP_Query( array(
'connected_type' => 'semester_to_organization',
'connected_items' => $organization_id,
'nopaging' => true,
) );
$semesters[ $organization_id ] = $semester->have_posts() ? $semester->posts[0] : null;
return $semesters[ $organization_id ];
}
/**
* When treasurer settings are submitted, attach each field as meta to appropriate object.
* Most meta goes to semester. Late fees and bank info go to organization.
*
* Since the lion's share of the meta is for the semester, and we're saving it primarily for
* the purpose of re-displaying in this form, we actually save the keys as the form field IDs.
*
* @return [type] [description]
*/
function gb_create_treasurer_settings_meta( $entry, $form ) {
// Get Organization ID
$treasurer_id = get_current_user_id();
$organization_id = gb_get_organization_id( $treasurer_id );
if ( gb_completed_treasurer_settings() ) {
$semester_id = gb_get_current_semester()->ID;
} else {
// Create Semester, if there isn't one active
$semester_id = gb_create_semester_for_organization( $organization_id );
}
// Organization meta
update_post_meta( $organization_id, 'bank_routing', rgar( $entry, '2' ) );
update_post_meta( $organization_id, 'bank_account', rgar( $entry, '3' ) );
// Semester meta
update_post_meta( $semester_id, 'entry_id', $entry['id'] );
foreach ( $entry as $form_field_id => $value ) {
if ( is_numeric( $form_field_id ) ) {
update_post_meta( $semester_id, $form_field_id, $value );
}
}
}
add_action( 'gform_after_submission_6', 'gb_create_treasurer_settings_meta', 10, 2 );
/**
* Creates a new semester and attaches it to the provided organization ID.
*
* @param int $id Organization ID.
* @return int Semester ID.
*/
function gb_create_semester_for_organization( $id ) {
$semester_id = wp_insert_post( apply_filters( 'semester_post_args', array(
'post_type' => 'terms',
'post_title' => 'Semester for Organization ID#: ' . $id,
'post_content' => 'Semester created at ' . current_time( 'mysql' ),
'post_status' => 'publish'
), get_current_user_id(), $id ) );
// Connect Semester to Organization
gb_connect_semester_to_organization( $semester_id, $id );
return $semester_id;
}
function gb_generate_transaction( $entry ) {
if ( is_numeric( $_POST['member_id'] ) ) {
$members = array( $_POST['member_id'] );
} else {
parse_str( $_POST['member_id'], $params );
$members = $params['member'];
}
$due_date = rgar( $entry, '5' );
$args = array(
'ID' => $entry['post_id'],
'amount' => rgar( $entry, '4' ),
'description' => rgar( $entry, '6' ),
'type' => intval( rgar( $entry, '1' ) )
);
foreach ( (array) $members as $member_id ) {
$args['member_id'] = $member_id;
if ( $args['type'] != 11 ) {
$args['status'] = 'unpaid';
}
if ( ! empty( $due_date ) ) {
$args['due_date'] = $due_date;
} else {
$args['due_date'] = false;
}
$payment_id = gb_insert_transaction( $args );
if ( $payment_id && $args['due_date'] ) {
gb_send_email( 'new-fee', $member_id, 'A New Fee Has Been Assessed to Your Account.' );
}
}
}
add_action( 'gform_after_submission_10', 'gb_generate_transaction', 10 );
/**
* Creates new member categories
*
* @todo Tie member categories to specific organizations
*
* @param [type] $entry [description]
* @param [type] $form [description]
* @return [type] [description]
*/
function gb_create_new_category( $entry, $form ) {
$categories = maybe_unserialize( rgar( $entry, '1' ) );
foreach ( $categories as $cat ) {
$category = $cat['Member Category'];
$dues = $cat['Due Amount'];
$term = wp_insert_term( $category, 'member_category', array( 'slug' => $category . '_' . gb_get_organization_id( get_current_user_id() ) ) );
if ( ! is_wp_error( $term ) ) {
update_term_meta( $term['term_id'], 'due_amount' , $dues );
update_term_meta( $term['term_id'], 'organization_id', gb_get_organization_id( get_current_user_id() ) );
}
}
}
add_action( 'gform_after_submission_9', 'gb_create_new_category', 10, 2 );
function gb_member_categories_dropdown() {
echo '<select class="member_category_dropdown medium gfield_select">option value="">Select a Category</option>';
foreach ( get_terms( 'member_category', array(
'hide_empty' => 0,
'meta_query' => array(
array(
'key' => 'organization_id',
'value' => gb_get_organization_id( get_current_user_id() )
)
)
) ) as $term ) {
?>
<option value="<?php echo $term->term_id; ?>"><?php echo esc_html( $term->name ); ?></option>
<?php
}
echo '</select>';
}
/**
* Sends email, based on template, with placeholders replaced with dynamic data.
*
* @param string $template Filename of template in /emails folder in plugin
* @param string $user User message is being sent to.
* @param string $subject Subject
*
* @return boolean True if successfully sent, false if not.
*/
function gb_send_email( $template, $user, $subject, $extra = '' ) {
add_filter( 'wp_mail_content_type', function() {
return 'text/html';
} );
$template_file = plugin_dir_path( __FILE__ ) . 'emails/' . $template . '.php';
if ( ! file_exists( $template_file ) ) {
return false;
}
if ( is_numeric( $user ) ) {
$user = new WP_User( $user );
}
ob_start();
include $template_file;
$text = ob_get_clean();
$text = gb_merge_email_tags( $text, $user );
$text .= var_export( $user, 1 );
// $email = // 'missed-payment' == $template ? 'justinsainton@gmail.com' : $user->user_email;
return wp_mail( 'justinsainton@gmail.com', $subject, $text );
}
/**
* [gb_merge_email_tags description]
*
* @param [type] $text [description]
* @param [type] $user [description]
* @return [type] [description]
*/
function gb_merge_email_tags( $text, $user ) {
$amount_due = get_member_current_balance( $user );
$org = gb_get_member_organization( $user->ID );
$last_fees = gb_get_member_payments( $user,
array(
'payment_type' => 'fees',
'post_status' => 'unpaid',
'posts_per_page' => 1,
'nopaging' => false,
)
);
$last_fee = array_pop( $last_fees );
$args = array(
'{amount_due}' => $amount_due,
'{due_date}' => get_member_due_date( $user ),
'{days_overdue}' => get_member_days_over_due( $user ),
'{current_year}' => date( 'Y' ),
'{organization}' => $org->post_title,
'{member_name}' => $user->display_name,
'{treasurer_name}' => gb_get_treasurer( $user )->display_name,
'{treasurer_email}' => gb_get_treasurer( $user )->user_email,
'{payment_url}' => gb_get_payment_url( $amount_due, $user ),
'{total_dues}' => gb_current_month_amount_paid( 'all', $org ),
'{total_past_dues}' => gb_current_month_amount_past_due( 'all', $org ),
'{university}' => gb_user_university( $user->ID ),
'{members_paid}' => gb_members_by_status( 'current', $org ),
'{members_late}' => gb_members_by_status( 'late', $org ),
'{fee_description}' => $last_fee->post_title,
'{fee_amount}' => '$' . number_format( $last_fee->total_amount, 2, '.', '' ),
'{fee_due_date}' => date( 'F j, Y', $last_fee->due_date ),
);
return str_replace( array_keys( $args ), $args, $text );
}
function gb_get_payment_url( $amount, $user ) {
if ( user_can( $user, 'manage_organization' ) ) {
$url = home_url( 'treasurer-center' );
} else {
$url = home_url( 'member-profile' );
}
$url = add_query_arg( array( 'amount' => str_replace( ',', '', $amount ), 'payment_hash' => gb_generate_payment_hash( $user, $amount ) ), $url ) . '#profile-panel-payment';
return $url;
}
function gb_generate_payment_hash( $user, $amount ) {
$hash = get_user_meta( $user->ID, '_make_a_payment_hash', true );
if ( empty( $hash ) ) {
$hash = hash_hmac( 'sha256', $amount, 'greek-bank-super-secret' );
update_user_meta( $user->ID, '_make_a_payment_hash', $hash );
}
return $hash;
}
function gb_check_payment_hash() {
if ( ! isset( $_REQUEST['amount'] ) || ! isset( $_REQUEST['payment_hash'] ) ) {
return;
}
$amount = $_REQUEST['amount'];
$hash = $_REQUEST['payment_hash'];
$check = hash_hmac( 'sha256', $amount, 'greek-bank-super-secret' );
if ( $check !== $hash ) {
return;
}
global $wpdb;
$user_id = $wpdb->get_var( $wpdb->prepare( 'SELECT user_id FROM ' . $wpdb->usermeta . ' WHERE meta_key = "_make_a_payment_hash" AND meta_value = %s', $hash ) );
if ( ! $user_id ) {
return;
}
delete_user_meta( $user_id, '_make_a_payment_hash' );
wp_clear_auth_cookie();
wp_set_auth_cookie( $user_id, false, '' );
wp_set_current_user( $user_id );
}
add_action( 'init', 'gb_check_payment_hash' );
/**
* Gets next due date.
*
* @param boolean $user [description]
* @return [type] [description]
*/
function get_member_due_date( $user = false ) {
$due_date = false;
foreach ( gb_due_dates( $user ) as $date ) {
if ( current_time( 'timestamp' ) < strtotime( $date['date'] ) ) {
$due_date = $date['date'];
break;
}
}
return $due_date ? date( 'M jS', strtotime( $due_date ) ) : false;
}
/**
* Returns due dates for a specific user.
*
* Date objects have the following format:
*
* array(
* array(
* 'date' => 'September 1st',
* 'amount_due' => '1500', // Amount due. Balance, including fees due on or before due date.
* 'status' => 'Paid' // Returns "Paid" if paid in full, or remaining balance if not.
* )
* )
*
* @param WP_User $user [description]
* @return [type] [description]
*/
function gb_due_dates( $user = false ) {
$plan = strtolower( gb_user_payment_plan( $user ) );
$plans = gb_payment_plan_mapping( $user );
$dates = gb_get_member_payment_plans( $user );
$fees = gb_get_member_payments( $user, array(
'payment_type' => 'fees',
'post_status' => 'unpaid'
)
);
$due_dates = array();
$term = 1;
foreach ( (array) $dates as $date ) {
$amount_due = gb_get_amount_due_by_date( $user, $date, $term );
$due_dates[] = array(
'date' => date( 'F jS, Y', strtotime( $date ) ),
'amount_due' => $amount_due,
'paid' => $amount_due <= 0 ? 'Paid' : '$' . number_format( $amount_due, 2, '.', '' ),
);
$term++;
}
return $due_dates;
}
function gb_get_member_payment_plans( $user = false ) {
$plan = strtolower( gb_user_payment_plan( $user ) );
$plans = gb_payment_plan_mapping( $user );
if ( isset( $plans[ $plan ] ) && is_array( $plans[ $plan ] ) ) {
return $plans[ $plan ];
}
$semester = gb_get_current_semester();
if ( ! $semester ) {
return array();
}
$payment_date = strtotime( gb_get_current_semester()->{'4'} );
// If we're closer than 24 hours to the due date, set a fallback.
$set_plan_by = date( 'm-d-Y', strtotime( '-1 day', $payment_date ) );
if ( current_time( 'timestamp' ) > $set_plan_by ) {
return array_pop( $plans );
}
return array();
}
/**
* Returns amount owed by a member, on (or before) a specific date.
*
* Inclusive of fees and dues.
*
* @param [type] $user [description]
* @param [type] $date [description]
* @return [type] [description]
*/
function gb_get_amount_due_by_date( $user, $date, $payment_plan = 1 ) {
static $amount_paid = null;
static $initial_dues = null;
if ( is_null( $amount_paid ) ) {
$amount_paid = get_member_amount_paid( $user, false );
}
if ( is_null( $initial_dues ) ) {
$initial_dues = gb_get_member_dues( $user ) + get_member_amount_paid( $user, false, array(
'payment_type' => 'dues',
'post_status' => 'unpaid'
)
);
}
$plans = gb_get_member_payment_plans( $user );
$dues = $initial_dues / count( $plans );
if ( $amount_paid > $dues ) {
$amount_paid -= $initial_dues / count( $plans );
} else {
$amount_paid = 0;
}
$dues -= $amount_paid;
return $dues;
}
function gb_get_treasurer( $user ) {
if ( user_can( $user, 'manage_organization' ) ) {
return $user;
}
$treasurer = get_users( array(
'connected_type' => 'single_treasurer',
'connected_items' => gb_get_organization_id( $user->ID ),
'fields' => 'id'
) );
if ( empty( $treasurer ) ) {
return false;
} else {
return new WP_User( $treasurer[0] );
}
}
/**
* Add Sign-Up URL query string URL as a merge tag
* @param [type] $form [description]
*/
function gb_add_merge_tags( $form ) {
?>
<script type="text/javascript">
gform.addFilter('gform_merge_tags', 'add_merge_tags');
function add_merge_tags(mergeTags, elementId, hideAllFields, excludeFieldTypes, isPrepop, option){
if (elementId == "gform_notification_message"){
//add merge tag only to the confirmation message field on the confirmation page
mergeTags["custom"].tags.push({ tag: '{sign_up_url}', label: 'Sign-Up URL' });
mergeTags["custom"].tags.push({ tag: '{current_year}', label: 'Current Year' });
mergeTags["custom"].tags.push({ tag: '{organization}', label: 'Organization' });
mergeTags["custom"].tags.push({ tag: '{university}', label: 'University' });
}
return mergeTags;
}
</script>
<?php
return $form;
}
add_action( 'gform_admin_pre_render', 'gb_add_merge_tags' );
/**
* [replace_download_link description]
*
* @param [type] $text [description]
* @param [type] $form [description]
* @param [type] $entry [description]
*
* @return [type] [description]
*/
function gb_replace_signup_merge_tag( $text, $form, $entry ) {
$custom_merge_tag = '{sign_up_url}';
if ( strpos( $text, $custom_merge_tag ) === false ) {
return $text;
}
$first = rgar( $entry, '1.3' );
$last = rgar( $entry, '1.6' );
$email = rgar( $entry, '2' );
$phone = rgar( $entry, '3' );
$organization = rgar( $entry, '4' );
$args = array(
'first_name' => $first,
'last_name' => $last,
'email' => urlencode( $email ),
'phone' => $phone,
'org' => $organization,
);
$sign_up_url = add_query_arg( $args, home_url( 'treasurer-sign-up' ) );
$text = str_replace( $custom_merge_tag, $sign_up_url, $text );
return $text;
}
add_filter( 'gform_replace_merge_tags', 'gb_replace_signup_merge_tag', 10, 3 );
function gb_payment_confirmation_tags( $text, $form, $entry ) {
$amount = floatval( rgar( $entry, 'payment_amount' ) );
$org = gb_get_member_organization();
$title = $org ? $org->post_title : '';
$args = array(
'{term_balance}' => '$' . get_member_remaining_balance( wp_get_current_user() ),
'{payment_amount}' => '$' . number_format( $amount , 2, '.', '' ),
'{organization}' => $title,
'{university}' => gb_user_university(),
'{confirmation_number}' => rgar( $entry, 'transaction_id' ),
);
$text = str_replace( array_keys( $args ), $args, $text );
return $text;
}
add_filter( 'gform_replace_merge_tags', 'gb_payment_confirmation_tags', 10, 3 );
/**
* [replace_download_link description]
*
* @param [type] $text [description]
* @param [type] $form [description]
* @param [type] $entry [description]
*
* @return [type] [description]
*/
function gb_replace_current_year_merge_tag( $text, $form, $entry ) {
$custom_merge_tag = '{current_year}';
if ( strpos( $text, $custom_merge_tag ) === false ) {
return $text;
}
$text = str_replace( $custom_merge_tag, date( 'Y' ), $text );
return $text;
}
add_filter( 'gform_replace_merge_tags', 'gb_replace_current_year_merge_tag', 10, 3 );
/**
*
* Pre-fills treasurer form with meta.
*
* To note: on new terms, where the new-term query string is set, we check if the
* field is a date field, and if it is, we set it to empty.
*
* @param [type] $form [description]
* @return [type] [description]
*/
function gb_prepopulate_treasurer_settings( $form ) {
if ( ! gb_completed_treasurer_settings() || '6' != $form['id'] ) {
return $form;
}
$organization = gb_get_member_organization();
$semester = gb_get_current_semester();
// Organization meta
$routing = $organization->bank_routing;
$account = $organization->bank_account;
// Semester meta
$semester_meta = get_post_custom( $semester->ID );
if ( ! is_array( $semester_meta ) ) {
return $form;
}
$mapped_fields = array();
foreach ( $semester_meta as $key => $value ) {
if ( is_numeric( $key ) && ! empty( $value[0] ) ) {
$mapped_fields[ $key ] = $value[0];
}
}
$mapped_field_ids = array_map( 'intval', array_keys( $mapped_fields ) );
$is_new_term = isset( $_GET['new-term'] ) && 'true' == $_GET['new-term'];
$term_start_date = GFCommon::date_display( rgar( $mapped_fields, 13 ) , '', false );
$datepicker_ids = array();
foreach ( $form['fields'] as &$field ) {
if ( ! in_array( $field['id'], $mapped_field_ids ) ) {
continue;
}
switch( RGFormsModel::get_input_type( $field ) ) {
case 'fileupload':
$value = rgar($mapped_fields, $field['id']);
$path_info = pathinfo($value);
// check if file has been "deleted" via form UI
$upload_files = json_decode( rgpost('gform_uploaded_files'), ARRAY_A );
$input_name = "input_{$field['id']}";
if( is_array( $upload_files ) && array_key_exists( $input_name, $upload_files ) && !$upload_files[$input_name] )
continue;
// if $uploaded_files array is not set for this form at all, init as array
if( !isset( RGFormsModel::$uploaded_files[$form['id']] ) )
RGFormsModel::$uploaded_files[$form['id']] = array();
// check if this field's key has been set in the $uploaded_files array, if not add this file (otherwise, a new image may have been uploaded so don't overwrite)
if( !isset( RGFormsModel::$uploaded_files[$form['id']]["input_{$field['id']}"] ) )
RGFormsModel::$uploaded_files[$form['id']]["input_{$field['id']}"] = $path_info['basename'];
break;
case 'checkbox':
$value = rgar($mapped_fields, $field['id']);
$cb_values = array();
if(is_array($value)) {
$cb_values = $value;
} else {
$inputs = $field['inputs'];
foreach($inputs as &$input) {
$cb_values[] = rgar($mapped_fields, (string)$input['id']);
}
$field['inputs'] = $inputs;
}
$value = implode(',', $cb_values );
break;
case 'list':
$value = maybe_unserialize(rgar($mapped_fields, $field['id']));
$list_values = array();
if(is_array($value)) {
foreach($value as $vals) {
if( ! is_array( $vals ) )
$vals = array( $vals );
$list_values = array_merge($list_values, array_values($vals));
}
$value = $list_values;
}
break;
case 'date':
$value = GFCommon::date_display( rgar($mapped_fields, $field['id']) , $field['dateFormat'], false);
break;
default:
// handle complex fields
$inputs = $field instanceof GF_Field ? $field->get_entry_inputs() : rgar( $field, 'inputs' );
if(is_array($inputs)) {
foreach($inputs as &$input) {
$filter_name = GFUser::prepopulate_input( $input['id'], rgar($mapped_fields, (string)$input['id']));
$field['allowsPrepopulate'] = true;
$input['name'] = $filter_name;
}
$field['inputs'] = $inputs;
} else {
$value = is_array(rgar($mapped_fields, $field['id'])) ? implode(',', rgar($mapped_fields, $field['id'])) : rgar($mapped_fields, $field['id']);
}
}
if ( 'date' == $field['type'] && $is_new_term ) {
$value = false;
}
if (rgblank($value)){
continue;
}
// If the term date has already passed, no date fields should be changeable.
/* if ( 'date' == $field->type && strtotime( $term_start_date ) < current_time( 'timestamp' ) ) {
if ( is_numeric( strtotime( $term_start_date ) ) ) {
$datepicker_ids[] = $field->id;
$field->isRequired = false;
}
} else
*/
if ( 'date' == $field->type && strtotime( $value ) < current_time( 'timestamp' ) ) {
if ( is_numeric( strtotime( $value ) ) ) {
// If the term date has not passI ed, but the specific payment plan date has, it should not be changeable.
$datepicker_ids[] = $field->id;
$field->isRequired = false;
}
} else if ( strtotime( $term_start_date ) < current_time( 'timestamp' ) && 12 == $field->id ) {
if ( is_numeric( strtotime( $term_start_date ) ) ) {
$datepicker_ids[] = $field->id;
$field->isRequired = false;
}
}
$value = GFUser::maybe_get_category_id($field, $value);
$filter_name = GFUser::prepopulate_input($field['id'], $value);
$field->allowsPrepopulate = true;
$field->inputName = $filter_name;
}
if ( ! empty( $datepicker_ids ) && ! $is_new_term ) {
add_action( 'wp_footer', function () use ( $datepicker_ids ) {
?>
<script>
jQuery( document ).ready( function( $ ) {
var ids = <?php echo wp_json_encode( $datepicker_ids ); ?>;
$( ids ).each( function( i,v ) {
if ( 12 == v ) {
$( 'input', '#input_6_12' ).attr( 'disabled', true );
} else {
$( 'input[name="input_' + v +'"]' ).datepicker( "option", { disabled: true } );
}
} );
$( '#ui-datepicker-div:visible' ).hide();
$( 'form#gform_6' ).submit( function() {
$( 'input' ).attr( 'disabled', false );
} );
} );
</script>
<?php
}, 999 );
}
return $form;
}
add_action( 'gform_pre_render', 'gb_prepopulate_treasurer_settings' );
function gb_set_email_to_address( $notification ) {
if ( $notification['name'] == 'Confirmation of Payment' ) {
$notification['to'] = wp_get_current_user()->user_email;
}
return $notification;
}
add_filter( 'gform_notification_7', 'gb_set_email_to_address', 10 );
function gb_send_attachment_to_admin( $notification, $form, $entry ) {
$file = $entry[28];
// Send a notification every time a file is uploaded, but never otherwise.
if ( empty( $file ) ) {
$notification['isActive'] = false;
$notification['to'] = false;
}
$fileupload_fields = GFCommon::get_fields_by_type( $form, array( 'fileupload' ) );
if ( ! is_array( $fileupload_fields ) ) {
return $notification;
}
$attachments = array();
$upload_root = RGFormsModel::get_upload_root();
foreach( $fileupload_fields as $field ) {
$url = $entry[ $field['id'] ];
$attachment = preg_replace( '|^(.*?)/gravity_forms/|', $upload_root, $url );
if ( $attachment ) {
$attachments[] = $attachment;
}
}
$notification['attachments'] = $attachments;
return $notification;
}
add_filter( 'gform_notification_6', 'gb_send_attachment_to_admin', 10, 3 );
add_filter( 'gform_notification_12', function( $n, $f, $l ) {
return $n;
}, 10, 3 );
function gb_redirect_profile_updates( $confirmation ) {
if ( gb_is_user_treasurer() ) {
$confirmation = array( 'redirect' => home_url( 'treasurer-center/?#profile-panel' ) );
}
return $confirmation;
}
add_filter( 'gform_confirmation_5', 'gb_redirect_profile_updates', 10 );
add_filter( 'gform_confirmation_7', 'gb_redirect_profile_updates', 10 );
function gb_payment_plan_mapping( $user = false ) {
$plan_map = array(
'12.1' => array(
'18'
),
'12.2' => array(
'19', '17'
),
'12.3' => array(
'16', '15', '24'
),
'12.4' => array(
'23', '22', '21', '20'
),
);
if ( $user ) {
$semester = gb_get_current_semester( gb_get_organization_id( $user->ID ) );
} else {
$semester = gb_get_current_semester();
}
$plans = array();
foreach ( $plan_map as $plan_option_key => $date_keys ) {
if ( ! empty( $semester->{ $plan_option_key } ) ) {
$plan = strtolower( $semester->{ $plan_option_key } );
$plans[ $plan ] = array();
foreach ( $date_keys as $key ) {
$plans[ $plan ][] = $semester->{$key};
}
}
}
return $plans;
}
function gb_map_meta( $caps, $cap, $user_id ) {
if ( ! did_action( 'init' ) ) {
return $caps;
}
$tax = get_taxonomy( 'member_category' );
if ( $cap !== 'edit_user' && $cap !== $tax->cap->assign_terms ) {
return $caps;
}
remove_filter( 'map_meta_cap', 'gb_map_meta', 10 );
$organization_id = gb_get_organization_id( get_current_user_id() );
$members = get_users( array(
'connected_type' => 'many_members',
'connected_items' => $organization_id,
'fields' => 'id'
) );
add_filter( 'map_meta_cap', 'gb_map_meta', 10, 3 );
if ( in_array( $user_id, $members ) ) {
$caps[] = 'edit_user';
$caps[] = $tax->cap->assign_terms;
}
return $caps;
}
add_filter( 'map_meta_cap', 'gb_map_meta', 10, 3 );
/**
* Removes payment plan from member profile once it's set for a member.
*
* @param [type] $form [description]
*/
function gb_member_profile_payment_plan_removal( $form ) {
$plan = gb_user_payment_plan();
$plans = array_map( function( $var ) {
return array( 'value' => ucwords( $var ), 'text' => ucwords( $var ) );
}, array_keys( gb_payment_plan_mapping() ) );
foreach ( $form['fields'] as &$field ) {
if ( ( $field->id == 5 || $field->id == 8 ) && ! empty( $plan ) ) {
$field->adminOnly = true;
} else if ( $field->id == 8 ) {
array_unshift( $plans, array( 'value' => 'Choose a Payment Plan', 'text' => 'Choose a Payment Plan' ) );
$field->choices = $plans;
}
}
return $form;
}
add_action( 'gform_pre_render_5', 'gb_member_profile_payment_plan_removal' );
function gb_filter_member_categories( $form ) {
foreach ( $form['fields'] as &$field ) {
if ( 'Member Category' === $field->label ) {
foreach ( $field->choices as $index => $choice ) {
if ( is_numeric( $choice['value'] ) ) {
$org_id = get_term_meta( $choice['value'], 'organization_id', true );
if ( $org_id != gb_get_member_organization()->ID ) {
unset( $field->choices[ $index ] );
}
}
}
}
}
return $form;
}
add_action( 'gform_pre_render_8' , 'gb_filter_member_categories' );
add_action( 'gform_pre_render_12', 'gb_filter_member_categories' );
function gb_filter_member_payment_plans( $form ) {
foreach ( $form['fields'] as &$field ) {
if ( 'Payment Plan' === $field->label ) {
foreach ( $field->choices as $index => $choice ) {
if ( empty( $choice['value'] ) ) {
continue;
}
$plans = array_map( 'strtolower', array_keys( gb_payment_plan_mapping() ) );
if ( ! in_array( strtolower( $choice['value'] ), $plans ) ) {
unset( $field->choices[ $index ] );
}
}
}
}
return $form;
}
add_action( 'gform_pre_render_8' , 'gb_filter_member_payment_plans' );
function gb_payments_post_status(){
register_post_status( 'paid', array(
'label' => _x( 'Paid', 'post' ),
'public' => true,
'exclude_from_search' => false,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop( 'Paid <span class="count">(%s)</span>', 'Paid <span class="count">(%s)</span>' ),
) );
register_post_status( 'unpaid', array(
'label' => _x( 'Unpaid', 'post' ),
'public' => true,
'exclude_from_search' => false,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop( 'Unpaid <span class="count">(%s)</span>', 'Unpaid <span class="count">(%s)</span>' ),
) );
}
add_action( 'init', 'gb_payments_post_status' );
/**
* Inserts payment entry, connecting them to semesters/members/organizations, after a payment is made.
*
* Payments have a payment_type taxonomy. Terms are either payment (a credit) or fees/dues (debits).
* - This API specifically handles payments, so the selected term will always be payment.
*
* Payments have statuses of unpaid/paid.
* - This API handles payments, so the specific status will always be paid.
* - For fees and dues, they are entered as unpaid. When a payment against them is made, they are marked paid.
*
* In addition, payments have metadata of when it was added, when it was paid, how much was paid in fees and when it is due.
* - Date added - post_date
* - When it was paid - 'paid_date' - in this case, same as above
* - Due Date - 'due_date' - in this case, empty
* - Total amount - 'total_amount'
* - Fees amount - 'fees_amount'
*
* With all of this relational data, we have enough information to build the rest of our financial panel
* showing the status of payments paid, membership health, etc.
*
* @param array $entry The Entry Object
* @param array $action The Action Object
*
* $action = array(
* 'type' => 'complete_payment',
* 'transaction_id' => '', // What is the ID of the transaction made?
* 'amount' => '0.00', // Amount to charge?
* 'entry_id' => 1, // What entry to check?
* 'transaction_type' => '',
* 'payment_status' => '',
* 'note' => ''
* );
*
* @return boolean True if payment insertion was successful, false if not.
*/
function gb_insert_payment( $entry, $action ) {
$total_amount = rgar( $entry, '2' );
$processed = rgar( $entry, 'payment_amount' );
$total_fees = $processed - $total_amount;
$payment_id = gb_insert_transaction(
array(
'description' => 'Payment ID: ' . $action['transaction_id'],
'amount' => $total_amount,
'due_date' => false,
'paid_date' => current_time( 'mysql' )
)
);
return $payment_id;
}
/**
* Inserts payment entry, connecting them to semesters/members/organizations, after a payment is made.
*
* Payments have a payment_type taxonomy. Terms are either payment (a credit) or fees/dues (debits).
*
* Payments have statuses of unpaid/paid.
* - For fees and dues, they are entered as unpaid. When a payment against them is made, they are marked paid.
*
* In addition, payments have metadata of when it was added, when it was paid, how much was paid in fees and when it is due.
* - Date added - post_date
* - When it was paid - 'paid_date'
* - Due Date - 'due_date'
* - Total amount - 'total_amount'
* - Fees amount - 'fees_amount'
*
* With all of this relational data, we have enough information to build the rest of our financial panel
* showing the status of payments paid, membership health, etc.
*
* @param array $args Arguments passed
*
* @return boolean True if payment insertion was successful, false if not.
*/
function gb_insert_transaction( $args ) {
$args = wp_parse_args( $args, array(
'type' => 'payment',
'status' => 'paid',
'description' => 'Payment',
'amount' => 0,
'due_date' => date( 'Y-m-d H:i:s' ),
'paid_date' => false,
'member_id' => get_current_user_id(),
'org_id' => false,
'semester_id' => false,
) );
trigger_error( __FUNCTION__ . ' : ' . var_export($percent_due, 1 ) );
if ( ! $args['org_id'] ) {
$args['org_id'] = gb_get_organization_id( $args['member_id'] );
}
if ( ! $args['semester_id'] ) {
$args['semester_id'] = gb_get_current_semester( $args['org_id'] )->ID;
}
$payment_id = wp_insert_post( array(
'post_status' => $args['status'],
'post_title' => $args['description'],
'post_type' => 'payment'
) );
wp_set_object_terms( $payment_id, $args['type'], 'payment_type' );
update_post_meta( $payment_id, 'paid_date' , strtotime( $args['paid_date'] ) );
update_post_meta( $payment_id, 'due_date' , strtotime( $args['due_date'] ) );
update_post_meta( $payment_id, 'total_amount', $args['amount'] );
gb_connect_payment_to_member( $payment_id , $args['member_id'] );
gb_connect_payment_to_semester( $payment_id , $args['semester_id'] );
gb_connect_payment_to_organization( $payment_id , $args['org_id'] );
return $payment_id;
}
add_action( 'gform_post_payment_completed', 'gb_insert_payment', 10, 2 );
function gb_set_up_cron() {
if ( ! wp_next_scheduled( 'gb_upcoming_payment_due' ) ) {
wp_schedule_event( current_time( 'timestamp' ), 'twicedaily', 'gb_upcoming_payment_due' );
}
if ( ! wp_next_scheduled( 'gb_missed_payment' ) ) {
wp_schedule_event( current_time( 'timestamp' ), 'twicedaily', 'gb_missed_payment' );
}
if ( ! wp_next_scheduled( 'gb_monthly_summary' ) ) {
wp_schedule_event( current_time( 'timestamp' ), 'twicedaily', 'gb_monthly_summary' );
}
if ( ! wp_next_scheduled( 'gb_late_fees' ) ) {
wp_schedule_event( current_time( 'timestamp' ), 'twicedaily', 'gb_late_fees' );
}
}
add_action( 'wp', 'gb_set_up_cron' );
function get_member_overdue_balance( $member = false, $format = true, $fees = true ) {
if ( ! $member ) {
$member = wp_get_current_user();
}
$date = date( 'Y-m-d', strtotime( get_member_due_date() ) );
$dates = gb_get_member_payment_plans();
if ( is_null( $dates ) ) {
return 0.00;
}
$due = array_search( $date, $dates );
$plans = gb_get_member_payment_plans( $member );
if ( ! count( $plans ) ) {
return false;
}
$percent_due = $due / count( $plans );
// Get initial amount due from Member Category (divided by amount currently due)
$amount = gb_get_member_dues( $member ) * $percent_due;
trigger_error( __FUNCTION__ . ' : ' . var_export($amount, 1 ) );
/* In some cases, we don't want to get the fees - we only want the past dues. */
if ( $fees ) {
// Add all unpaid fees that are due by yesterday
$amount += get_member_amount_paid( $member, false, array(
'payment_type' => 'fees',
'post_status' => 'unpaid',
'meta_compare' => '<=',
'meta_key' => 'due_date',
'meta_value' => strtotime( '23:59:59 -1 day', current_time( 'timestamp' ) )
)
);
}
// Add all unpaid dues (after dividing)
trigger_error( __FUNCTION__ . ' : ' . var_export($amount, 1 ) );
$amount += ( get_member_amount_paid( $member, false, array(
'payment_type' => 'dues',
'post_status' => 'unpaid'
)
) * $percent_due );
// Subtract all payments made
$amount -= get_member_amount_paid( $member, false );
$amount = $amount < 0 ? 0.00 : $amount;
trigger_error( __FUNCTION__ . ' : ' . var_export($amount, 1 ) );
if ( $format ) {
return number_format( $amount, 2, '.', '' );
} else {
return $amount;
}
}
/**
* Slightly different than get_member_remaining_balance
* While the former gets the remaining balance for the semester, this function gets
* only the overdue balance
*
* @param [type] $member [description]
* @return [type] [description]
*/
function get_member_overdue_balance_disabled( $member = false, $format = true, $fees = true ) {
if ( ! $member ) {
$member = wp_get_current_user();
}
$date = date( 'Y-m-d', strtotime( get_member_due_date() ) );
trigger_error( __FUNCTION__ . ' : ' . var_export($date, 1 ) );
$dates = gb_get_member_payment_plans();
trigger_error( __FUNCTION__ . ' : ' . var_export($dates, 1 ) );
if ( is_null( $dates ) ) {
return 0.00;
}
$due = array_search( $date, $dates ) + 1;
trigger_error( __FUNCTION__ . ' : ' . var_export($due, 1 ) );
if ( ! ( $count = count( $dates ) ) ) {
trigger_error( __FUNCTION__ . ' : ' . var_export($count, 1 ) );
return 0.00;
}
$percent_due = $due / count( $count );
trigger_error( __FUNCTION__ . ' : ' . var_export($percent_due, 1 ) );
// Get initial amount due from Member Category (divided by amount currently due)
$amount = gb_get_member_dues( $member ) * $percent_due;
trigger_error( __FUNCTION__ . ' : ' . var_export($amount, 1 ) );
/* In some cases, we don't want to get the fees - we only want the past dues. */
if ( $fees ) {
trigger_error( __FUNCTION__ . ' : ' . var_export($fees, 1 ) );
// Add all unpaid fees that are due by yesterday
$amount += get_member_amount_paid( $member, false, array(
'payment_type' => 'fees',
'post_status' => 'unpaid',
'meta_query' => array(
array(
'meta_key' => 'due_date',
'meta_value' => strtotime( '23:59:59 -1 day', current_time( 'timestamp' ) ),
'compare' => '<=',
'type' => 'NUMERIC',
)
)
)
);
trigger_error( __FUNCTION__ . ' : ' . var_export($amount, 1 ) );
}
// Add all unpaid dues (after dividing)
$amount += ( get_member_amount_paid( $member, false, array(
'payment_type' => 'dues',
'post_status' => 'unpaid'
)
) * $percent_due );
trigger_error( __FUNCTION__ . ' : ' . var_export($amount, 1 ) );
// Subtract all payments made
$amount -= get_member_amount_paid( $member, false );
trigger_error( __FUNCTION__ . ' : ' . var_export($amount, 1 ) );
$amount = $amount < 0 ? 0.00 : $amount;
trigger_error( __FUNCTION__ . ' : ' . var_export($amount, 1 ) );
if ( $format ) {
return number_format( $amount, 2, '.', '' );
} else {
trigger_error( __FUNCTION__ . ' : ' . var_export($amount, 1 ) );
return $amount;
}
}
/**
* Slightly different than get_member_remaining_balance
* While the former gets the remaining balance for the semester, this function gets
* the current balance due, which only includes the amount due through the next payment date.
*
* @param [type] $member [description]
* @return [type] [description]
*/
function get_member_current_balance_has_bug( $member = false, $format = true ) {
if ( ! $member ) {
$member = wp_get_current_user();
}
$date = date( 'Y-m-d', strtotime( get_member_due_date() ) );
$dates = gb_get_member_payment_plans();
if ( is_null( $dates ) ) {
return 0.00;
}
$due = array_search( $date, $dates ) + 1;
if ( ! ( $count = count( $dates ) ) ) {
return 0.00;
}
$percent_due = $due / count( $count );
// Get initial amount due from Member Category (divided by amount currently due)
$amount = gb_get_member_dues( $member ) * $percent_due;
// Add all unpaid fees that are due by yesterday
$amount += get_member_amount_paid( $member, false, array(
'payment_type' => 'fees',
'post_status' => 'unpaid',
'meta_query' => array(
array(
'meta_key' => 'due_date',
'meta_value' => strtotime( $date ),
'compare' => '<=',
)
)
)
);
// Add all unpaid dues (after dividing)
$amount += ( get_member_amount_paid( $member, false, array(
'payment_type' => 'dues',
'post_status' => 'unpaid'
)
) * $percent_due );
// Subtract all payments made
$amount -= get_member_amount_paid( $member, false );
$amount = $amount < 0 ? 0.00 : $amount;
if ( $format ) {
return number_format( $amount, 2, '.', '' );
} else {
return $amount;
}
}
function gb_get_next_payment_amount( $member = false ) {
if ( ! $member ) {
$member = wp_get_current_user();
}
$paid = get_member_amount_paid( $member, false );
$initial_dues = gb_get_member_dues( $member );
if ( $initial_dues <= $paid ) {
return false;
}
$plans = gb_get_member_payment_plans( $member );
if ( ! count( $plans ) ) {
return false;
}
$dues = $initial_dues / count( $plans );
return $dues;
}
function get_member_current_balance( $member = false, $format = true ) {
if ( ! $member ) {
$member = wp_get_current_user();
}
$date = date( 'Y-m-d', strtotime( get_member_due_date() ) );
if ( get_member_overdue_balance() > 0.01 ) {
$amount = gb_get_next_payment_amount( $member );
// Add all unpaid fees that are due by yesterday
$amount += get_member_amount_paid( $member, false, array(
'payment_type' => 'fees',
'post_status' => 'unpaid',
'meta_query' => array(
array(
'meta_key' => 'due_date',
'meta_value' => strtotime( $date ),
'compare' => '<='
)
)
)
);
$amount = $amount < 0 ? 0.00 : $amount;
} else {
$amount = gb_get_next_payment_amount( $member );
// Add all unpaid fees that are due by yesterday
$amount += get_member_amount_paid( $member, false, array(
'payment_type' => 'fees',
'post_status' => 'unpaid',
'meta_query' => array(
array(
'meta_key' => 'due_date',
'meta_value' => strtotime( $date ),
'compare' => '<='
)
)
)
);
// Add all unpaid dues (after dividing)
$amount += ( get_member_amount_paid( $member, false, array(
'payment_type' => 'dues',
'post_status' => 'unpaid'
)
) );
// Subtract all payments made
$amount -= get_member_amount_paid( $member, false );
$amount = $amount < 0 ? 0.00 : $amount;
}
if ( $format ) {
return number_format( $amount, 2, '.', '' );
} else {
return $amount;
}
}
function get_recent_payments_view( $member = false ) {
if ( ! $member ) {
$member = wp_get_current_user();
}
$dues = gb_get_next_payment_amount( $member );
if ( ! $dues ) {
return;
}
$payments = gb_get_member_payments( $member->ID, array( 'nopaging' => false, 'posts_per_page' => 1, 'payment_type' => 'payment', 'post_status' => 'paid' ) );
$payment = array_pop( $payments );
if ( $payment ) :
?>
<p>Previous payment was on <span class=""><?php echo date( 'M j', strtotime( $payment->post_date ) ); ?></span> for <span>$<?php echo number_format( $payment->total_amount, 2, '.', '' ); ?></span>.</p>
<?php endif; ?>
<p>Next payment is on <span><?php echo get_member_due_date( $member ); ?></span> for <span>$<?php echo number_format( $dues, 2, '.', '' ); ?></span>.</p>
<?php
}
function gb_download_payments_csv() {
if ( isset( $_GET['generate_payments'] ) && $_GET['generate_payments'] == 'true' && current_user_can( 'manage_organization' ) ) {
gb_download_send_headers( 'payment_export_' . date( 'Y-m-d' ) . '.csv' );
echo gb_export_payments( gb_get_organization_payments() );
die();
}
}
add_action( 'template_redirect', 'gb_download_payments_csv', 50 );
function gb_export_payments( array $payments ) {
if ( count( $payments ) == 0 ) {
return null;
}
ob_start();
$df = fopen( "php://output", 'w' );
fputcsv( $df, array_keys( reset( $payments ) ) );
foreach ( $payments as $row ) {
fputcsv( $df, $row );
}
fclose( $df );
return ob_get_clean();
}
function gb_download_send_headers( $filename ) {
// disable caching
$now = gmdate("D, d M Y H:i:s");
header("Expires: Tue, 03 Jul 2001 06:00:00 GMT");
header("Cache-Control: max-age=0, no-cache, must-revalidate, proxy-revalidate");
header("Last-Modified: {$now} GMT");
// force download
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
// disposition / encoding on response body
header("Content-Disposition: attachment;filename={$filename}");
header("Content-Transfer-Encoding: binary");
}
function gb_get_organization_payments( $org_id = false ) {
if ( ! $org_id ) {
$org_id = gb_get_organization_id( get_current_user_id() );
}
$args = array(
'connected_type' => 'payments_to_organization',
'connected_items' => $org_id,
'nopaging' => true,
);
$payments = new WP_Query( $args );
$payment_objects = $payments->have_posts() ? $payments->posts : array();
$payments = array();
foreach ( $payment_objects as $payment ) {
$payer = get_users( array(
'connected_type' => 'payments_to_user',
'connected_items' => $payment->ID,
'fields' => 'id'
) );
$payer = array_pop( $payer );
$payer = new WP_User( $payer );
$payments[] = array(
'Member Name' => $payer->display_name,
'Payment Date' => $payment->post_date,
'Amount' => $payment->total_amount,
'Description' => $payment->post_title
);
}
return $payments;
}
/**
* Sends upcoming payment emails to all users who have payments due in the near future
*
* @return [type] [description]
*/
function gb_send_upcoming_payment_due_email() {
// Loop through all members
$members = get_users( array( 'role' => 'Member' ) );
$five_days = strtotime( '+5 days', current_time( 'timestamp') );
// Determine if next due date is within the next five days AND if an email has been sent for that date.
foreach ( $members as $member ) {
// If the member is paid up, early exit.
$balance = get_member_current_balance( $member, false );
if ( ! $balance ) {
continue;
}
$next_due_date = get_member_due_date( $member );
$due_date = strtotime( $next_due_date );
// If there is no upcoming due date - either all in the past, or more than five days out.
if ( ! $next_due_date || ( $due_date > $five_days ) ) {
continue;
}
// send email if w/in 5 days
if ( ! $member->{"upcoming_reminder_for_{$due_date}_sent"} ) {
$mail = gb_send_email( 'upcoming-payment', $member, 'Upcoming Payment Information' );
if ( $mail ) {
update_user_meta( $member->ID, "upcoming_reminder_for_{$due_date}_sent", 'sent' );
}
}
}
}
add_action( 'gb_upcoming_payment_due', 'gb_send_upcoming_payment_due_email' );
/**
* Sends missed payment emails the day after they're missed.
* @return [type] [description]
*/
function gb_send_missed_payment_email() {
// Loop through all members
$members = get_users( array( 'role' => 'Member' ) );
// Determine if next due date is within the next five days AND if an email has been sent for that date.
foreach ( $members as $member ) {
// If the member is paid up, early exit.
$balance = get_member_overdue_balance( $member, false );
if ( ! $balance ) {
continue;
}
$recently_sent = $member->recently_sent_missed_payment_email;
if ( $recently_sent && current_time( 'timestamp' ) < strtotime( '+3 days', $recently_sent ) ) {
continue;
}
$append = var_export( array(
'balance' => $balance,
'recently_sent' => $recently_sent,
'current_time' => current_time( 'timestamp' ),
'three_days' => strtotime( '+3 days', $recently_sent )
), 1 );
$mail = gb_send_email( 'missed-payment', $member, 'Missed Payment', $append );
if ( $mail ) {
update_user_meta( $member->ID, 'recently_sent_missed_payment_email', current_time( 'timestamp' ) );
}
}
}
add_action( 'gb_missed_payment', 'gb_send_missed_payment_email' );
function gb_send_monthly_summary_email() {
$treasurers = get_users( array( 'role' => 'Treasurer' ) );
$after_the_25th = date( 'j' ) >= 25;
if ( ! $after_the_25th ) {
return false;
}
$this_month = date( 'MY' );
foreach ( $treasurers as $member ) {
$sent_this_month = get_user_meta( $member->ID, "summary_email_for_{$this_month}", true );
if ( $sent_this_month ) {
continue;
}
$mail = gb_send_email( 'monthly-summary', $member, 'Monthly Payment Report' );
if ( $mail ) {
update_user_meta( $member->ID, "summary_email_for_{$this_month}", current_time( 'timestamp' ) );
}
}
}
add_action( 'gb_monthly_summary', 'gb_send_monthly_summary_email' );
function gb_email_new_member( $args ) {
$form = GFAPI::get_form( 12 );
$notification = end( $form['notifications'] );
$lead = array(
'id' => wp_generate_password( 64, true, true ),
'form_id' => '12',
'source_url' => 'https://greekbank.org/treasurer-center/',
'status' => 'active',
'1.3' => $args['first_name'],
'1.6' => $args['last_name'],
'2' => $args['user_email'],
'4' => $args['term_id'],
'5' => $args['user_pass'],
'1.2' => '',
'1.4' => '',
'1.8' => ''
);
GFCommon::send_notification( $notification, $form, $lead );
}
/**
* Adds member to organization
* Called via AJAX and via CSV import
*
* @return [type] [description]
*/
function gb_add_member( $member ) {
$has_dues = count( $member ) > 3;
$first_name = $member[0];
$last_name = $member[1];
$email = $member[2];
if ( $has_dues ) {
$category = sanitize_title( $member[3] );
$dues = floatval( $member[4] );
}
$args = array(
'user_login' => sanitize_email( $email ),
'user_email' => sanitize_email( $email ),
'user_pass' => wp_generate_password( 32, true, true ),
'role' => 'member',
'first_name' => sanitize_text_field( $first_name ),
'last_name' => sanitize_text_field( $last_name ),
);
$user_id = wp_insert_user( $args );
if ( is_wp_error( $user_id ) ) {
return;
}
$organization = gb_get_member_organization();
gb_connect_member_to_organization( $user_id, $organization->ID );
gb_email_new_member( $args );
if ( $has_dues ) {
$_category = get_term_by( 'slug', sanitize_title( $member[3] ) . '_' . $organization->ID, 'member_category' );
if ( ! $_category ) {
$_category = wp_insert_term( $member[3], 'member_category', array( 'slug' => $member[3] . '_' . $organization->ID ) );
if ( ! is_wp_error( $_category ) ) {
update_term_meta( $_category['term_id'], 'due_amount', $dues );
update_term_meta( $_category['term_id'], 'organization_id', $organization->ID );
} else {
return;
}
$_category = get_term_by( 'id', $_category['term_id'], 'member_category' );
}
$args['term_id'] = $_category->term_id;
wp_set_terms_for_user( $user_id, 'member_category', array( $_category->slug ) );
}
}
function gb_import_members( $form ) {
$upload = new GF_Field_FileUpload();
$file_info = $upload->get_single_file_value( '6', 'input_28' );
$value = str_replace( home_url(), untrailingslashit( ABSPATH ), set_url_scheme( $file_info, 'https' ) );
if ( empty( $value ) ) {
return;
}
if ( 'csv' !== pathinfo( $value, PATHINFO_EXTENSION ) ) {
return;
}
$members = gb_csv_to_array( $value );
// necessary if a large csv file
set_time_limit( 0 );
foreach ( $members as $member ) {
$member = array_values( $member );
if ( 'first name' == strtolower( $member[0] ) ) {
continue;
}
$member = array_filter( $member );
gb_add_member( $member );
}
}
function gb_csv_to_array($filename='', $delimiter=',') {
ini_set('auto_detect_line_endings',true );
if(!file_exists($filename) || !is_readable($filename))
return $filename . ' is not readable';
$header = null ;
$data = array();
if (($handle = fopen($filename, 'r')) !== false )
{
while (($row = fgetcsv($handle, 1000, $delimiter)) !== false )
{
if (!$header) {
$header = $row;
}
else {
if (count($header) > count($row)) {
$difference = count($header) - count($row);
for ($i = 1; $i <= $difference; $i++) {
$row[count($row) + 1] = $delimiter;
}
}
$data[] = array_combine($header, $row);
}
}
fclose($handle);
}
return $data;
}
add_action( 'gform_after_submission_6', 'gb_import_members', 99 );
/**
* When new semester is created via new_term qv, do the following:
* - connect new semester to existing semester's organization and all members/treasurer
* - confirm all meta is pre-set, except for dates.
* - confirm that all existing unpaid fees are migrated and past due
* - generate new dues.
*
* @return [type] [description]
*/
function gb_create_new_semseter( $entry, $form ) {
if ( ! ( isset( $_GET['new-term'] ) && 'true' == $_GET['new-term'] ) ) {
return;
}
// Create new semester and connect new semester to existing organization
$treasurer_id = get_current_user_id();
$organization_id = gb_get_organization_id( $treasurer_id );
$semester_id = gb_create_semester_for_organization( $organization_id );
// Generate new dues
$members = get_users( array(
'connected_type' => 'many_members',
'connected_items' => $organization_id,
) );
$args = array(
'status' => 'unpaid',
'description' => 'New Semester Dues',
'type' => 'dues',
'due_date' => strtotime( $_POST['input_4'] )
);
foreach ( $members as $member ) {
gb_connect_semester_to_member( $semester_id, $member->ID );
$args['member_id'] = $member->ID;
$args['amount'] = gb_get_member_dues( $member );
$dues = gb_insert_transaction( $args );
$overdue = get_member_overdue_balance( $member, false, false );
if ( $overdue > 0 ) {
$new_args = $args;
$new_args['amount'] = get_member_overdue_balance( $member, false, false );
$new_args['description'] = 'Past Dues from Previous Semester';
$new_args['due_date'] = $five_days = strtotime( '-2 days', current_time( 'timestamp' ) );
$dues_id = gb_insert_transaction( $new_args );
}
}
}
add_action( 'gform_after_submission_6', 'gb_create_new_semseter', 8, 2 );
function gb_remove_admin_bar() {
if ( ! current_user_can( 'manage_options' ) ) {
show_admin_bar( false );
}
}
add_action( 'after_setup_theme', 'gb_remove_admin_bar' );
/**
* Applies late fees
*
* @return [type] [description]
*/
function gb_apply_late_fees() {
// Loop through all members
$members = get_users( array( 'role' => 'Member' ) );
foreach ( $members as $member ) {
trigger_error( __FUNCTION__ . ' : ' . var_export($member, 1 ) );
$org_id = gb_get_member_organization( $member->ID )->ID;
$fees = gb_has_fees( $org_id );
trigger_error( __FUNCTION__ . ' : ' . var_export($fees, 1 ) );
if ( ! $fees ) {
trigger_error( 'organization apparently has no fees in their settings' );
continue;
}
// If the member is paid up, early exit.
$balance = get_member_overdue_balance( $member, false );
trigger_error( __FUNCTION__ . ' : ' . var_export($balance, 1 ) );
if ( ! $balance ) {
continue;
}
$days_late = gb_get_current_semester( $org_id )->{'10'};
trigger_error( __FUNCTION__ . ' : ' . var_export($days_late, 1 ) );
$due_date = get_member_due_date( $member );
trigger_error( __FUNCTION__ . ' : ' . var_export($due_date, 1 ) );
// If we're not currently outside of the "Days Late" threshold, continue
if ( current_time( 'timestamp' ) < strtotime( '+ ' . $days_late . ' days', strtotime( $due_date ) ) ) {
continue;
}
$fee = gb_get_current_semester( $org_id )->{'11'};
$sanitized_due_date = sanitize_title( $due_date );
trigger_error( __FUNCTION__ . ' : ' . var_export($sanitized_due_date, 1 ) );
// If we've already charged a late fee for this date, continue.
if ( $member->{"charged_late_fee_for_$sanitized_due_date"} ) {
continue;
}
$args = array(
'type' => 'fees',
'status' => 'unpaid',
'description' => 'Late Fee',
'amount' => $fee,
'member_id' => $member->ID,
);
// Now, we're safely able to charge a late fee
$fees_id = gb_insert_transaction( $args );
trigger_error( __FUNCTION__ . ' : ' . var_export($fees_id, 1 ) );
if ( $fees_id ) {
update_user_meta( $member->ID, "charged_late_fee_for_$sanitized_due_date", current_time( 'timestamp' ) );
}
}
}
add_action( 'gb_late_fees', 'gb_apply_late_fees' );
function _get_members( $org ) {
static $members = array();
if ( ! $org ) {
$organization_id = gb_get_organization_id( get_current_user_id() );
} else {
if ( is_object ( $org ) ) {
$organization_id = $org->ID;
} else {
$organization_id = $org;
}
}
if ( empty( $members[ $organization_id ] ) ) {
$members[ $organization_id ] = get_users( array(
'connected_type' => 'many_members',
'connected_items' => $organization_id,
) );
}
return $members[ $organization_id ];
}
/**
* Total amount due this month that is not currently paid.
* @return [type] [description]
*/
function gb_current_month_amount_due( $org = false ) {
$members = _get_members( $org );
$current_due = 0;
$formatter = new NumberFormatter( 'en_US', NumberFormatter::DECIMAL );
$currency = 'USD';
foreach ( $members as $member ) {
$current_due += $formatter->parseCurrency( get_member_current_balance( $member ), $currency );
}
return $current_due;
}
/**
* Total amount due this term that is not currently paid.
* @return [type] [description]
*/
function gb_current_term_amount_due( $organization = false ) {
$members = _get_members( $organization );
$current_due = 0;
$formatter = new NumberFormatter( 'en_US', NumberFormatter::DECIMAL );
$currency = 'USD';
foreach ( $members as $member ) {
$current_due += $formatter->parseCurrency( get_member_remaining_balance( $member ), $currency );
}
return $current_due;
}
/**
* The total amount currently past due, as of this month.
*
* @return [type] [description]
*/
function gb_current_month_amount_past_due( $type = 'all', $org = false ) {
$members = _get_members( $org );
$past_due = 0;
$formatter = new NumberFormatter( 'en_US', NumberFormatter::DECIMAL );
$currency = 'USD';
foreach ( $members as $member ) {
$past_due += $formatter->parseCurrency( get_member_overdue_balance( $member ), $currency );
}
return $past_due;
}
/**
* The total amount of payments received in the current month.
* Defaults to all payments, but can be limited to just fees, or just dues.
*
* @param string $payment_type [description]
* @return [type] [description]
*/
function gb_current_month_amount_paid( $payment_type = 'all', $organization = false ) {
$members = _get_members( $organization );
$paid = 0;
$formatter = new NumberFormatter( 'en_US', NumberFormatter::DECIMAL );
$currency = 'USD';
// Potential todo: add meta_query boundary for semester start date
$payment_args = array();
if ( 'fees' == $payment_type ) {
$payment_args['payment_type'] = 'fees';
} else if ( 'dues' == $payment_type ) {
$payment_args['payment_type'] = 'dues';
}
foreach ( $members as $member ) {
$paid += $formatter->parseCurrency( get_member_amount_paid( $member, false, $payment_args ), $currency );
}
return $paid;
}
/**
* Total amount of dues this term.
* @return [type] [description]
*/
function gb_term_total_dues( $organization = false ) {
$members = _get_members( $organization );
$total_dues = 0;
$formatter = new NumberFormatter( 'en_US', NumberFormatter::DECIMAL );
$currency = 'USD';
foreach ( $members as $member ) {
$total_dues += $formatter->parseCurrency( gb_get_member_dues( $member ), $currency );
}
return $total_dues;
}
function gb_payment_modal_payment_options() {
$today = date( 'm/d/Y' );
$due_dates = gb_due_dates();
$due_date = array_pop( $due_dates );
$last_date = date( 'm/d/Y', strtotime( $due_date['date'] ) );
?>
<?php if ( 0.00 != get_member_overdue_balance() ) : ?>
<label class="payment-plan-option">
<input type="radio" name="_amount" data-amount="<?php echo get_member_overdue_balance( false, false ); ?>" /> Past Due: <strong>$<?php echo get_member_overdue_balance(); ?></strong>
</label>
<?php endif; ?>
<label class="payment-plan-option">
<input type="radio" name="_amount" data-amount="<?php echo get_member_current_balance( false, false ); ?>" /> Current Due as of <?php echo $today; ?>: <strong>$<?php echo get_member_current_balance(); ?></strong>
</label>
<label class="payment-plan-option">
<input type="radio" name="_amount" data-amount="<?php echo get_member_remaining_balance( false, false ); ?>" /> Total Due <?php echo $last_date; ?>: <strong>$<?php echo get_member_remaining_balance(); ?></strong>
</label>
<label class="payment-plan-option custom-amount">
<input type="radio" name="_amount" /> Other Amount
</label>
<div id="surcharge" style="display: none; padding-bottom: 20px">
Total Charge:<br />
<em class="base-amount"></em> + <em class="surcharge"></em> surcharge = <strong class="total-charge"></strong>
</div>
<?php
}
class GWRequireListColumns {
private $field_ids;
public static $fields_with_req_cols = array();
function __construct($form_id = '', $field_ids = array(), $required_cols = array()) {
$this->field_ids = ! is_array( $field_ids ) ? array( $field_ids ) : $field_ids;
$this->required_cols = ! is_array( $required_cols ) ? array( $required_cols ) : $required_cols;
if( ! empty( $this->required_cols ) ) {
// convert values from 1-based index to 0-based index, allows users to enter "1" for column "0"
$this->required_cols = array_map( create_function( '$val', 'return $val - 1;' ), $this->required_cols );
if( ! isset( self::$fields_with_req_cols[$form_id] ) )
self::$fields_with_req_cols[$form_id] = array();
// keep track of which forms/fields have special require columns so we can still apply GWRequireListColumns
// to all list fields and then override with require columns for specific fields as well
self::$fields_with_req_cols[$form_id] = array_merge( self::$fields_with_req_cols[$form_id], $this->field_ids );
}
$form_filter = $form_id ? "_{$form_id}" : $form_id;
add_filter("gform_validation{$form_filter}", array(&$this, 'require_list_columns'));
}
function require_list_columns($validation_result) {
$form = $validation_result['form'];
$new_validation_error = false;
foreach($form['fields'] as &$field) {
if(!$this->is_applicable_field($field, $form))
continue;
$values = rgpost("input_{$field['id']}");
// If we got specific fields, loop through those only
if( count( $this->required_cols ) ) {
$rows = $this->convert_values_to_rows( $values, $field );
foreach( $rows as $row ) {
foreach($this->required_cols as $required_col) {
if(empty($row[$required_col])) {
$new_validation_error = true;
$field['failed_validation'] = true;
$field['validation_message'] = $field['errorMessage'] ? $field['errorMessage'] : 'All inputs must be filled out.';
}
}
}
} else {
// skip fields that have req cols specified by another GWRequireListColumns instance
$fields_with_req_cols = rgar( self::$fields_with_req_cols, $form['id'] );
if( is_array( $fields_with_req_cols ) && in_array( $field['id'], $fields_with_req_cols ) )
continue;
foreach($values as $value) {
if(empty($value)) {
$new_validation_error = true;
$field['failed_validation'] = true;
$field['validation_message'] = $field['errorMessage'] ? $field['errorMessage'] : 'All inputs must be filled out.';
}
}
}
}
$validation_result['form'] = $form;
$validation_result['is_valid'] = $new_validation_error ? false : $validation_result['is_valid'];
return $validation_result;
}
function is_applicable_field($field, $form) {
if($field['pageNumber'] != GFFormDisplay::get_source_page($form['id']))
return false;
if( GFFormsModel::get_input_type( $field ) != 'list' || RGFormsModel::is_field_hidden($form, $field, array()))
return false;
// if the field has already failed validation, we don't need to fail it again
if(!$field['isRequired'] || $field['failed_validation'])
return false;
if(empty($this->field_ids))
return true;
return in_array($field['id'], $this->field_ids);
}
function convert_values_to_rows( $values, $field ) {
$column_count = count( $field['choices'] );
$rows = array();
while( count( $values ) > 0 ) {
$rows[] = array_splice( $values, 0, $column_count );
}
return $rows;
}
}
new GWRequireListColumns(9);
function gb_prevent_add_term( $term, $taxonomy ) {
if ( $term === 'dues' && 'payment_type' == $taxonomy ) {
$term = new WP_Error( 'invalid_term', 'You are attempting to insert "dues" as a payment type. No bueno.' );
}
return $term;
}
add_filter( 'pre_insert_term', 'gb_prevent_add_term', 20, 2 );
//////// DONE
//////// DONE
//////// DONE
/**
* Gets number of members who have payments that are in specific stages of delinquency.
*
* @param string $delinquency [description]
* @return [type] [description]
*/
function gb_members_by_status( $delinquency = 'current', $org = false ) {
if ( 'current' == $delinquency ) {
return 50;
} else if ( 'thirty_days_late' == $delinquency ) {
return 100;
} else if ( 'sixty_days_late' == $delinquency ) {
return 300;
} else {
return 10; //more_than_sixty
}
}
function get_member_days_over_due( $user ) {
return '10';
}
function __old_csv_upload_description() {
?>
CSV files are automatically uploaded in the member roster. For it to work right, please submit either of these two formats with the following Headers: <br>
- First name, Last Name, Email<br>
- First name, Last Name, Email, Member Category, Due Amount for Term<br>
<strong>Do NOT submit a member category without a due amount</strong>
<?php
}
if ( !function_exists('set_magic_quotes_runtime') ) {
function set_magic_quotes_runtime($value) { return true;}
}
function gb_force_ssl_in_treasurer_center() {
$secure_pages = is_page( 'member-profile' ) || is_page( 'treasurer-center' );
if ( $secure_pages && ! is_ssl() ) {
if ( 0 === strpos($_SERVER['REQUEST_URI'], 'http') ) {
wp_safe_redirect(preg_replace('|^http://|', 'https://', $_SERVER['REQUEST_URI']), 301 );
exit();
} else {
wp_safe_redirect('https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], 301 );
exit();
}
}
}
add_action( 'template_redirect', 'gb_force_ssl_in_treasurer_center', 1 );
<div class="panel" id="profile-panel">
<div class="table-container clearfix" id="profile-large">
<div class="five-sixths first">
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Payment Plan</th>
</tr>
</thead>
<tbody>
<tr>
<td><?php echo gb_user_full_name(); ?></td>
<td><?php echo gb_user_email(); ?></td>
<td><?php echo gb_user_payment_plan(); ?></td>
</tr>
</tbody>
<thead>
<tr>
<th>University</th>
<th>Organization</th>
<th>Member Category</th>
</tr>
</thead>
<tbody>
<tr>
<td><?php echo gb_user_university(); ?></td>
<td><?php echo gb_get_member_organization()->post_title; ?></td>
<td><?php echo gb_get_member_category( get_current_user_id() ); ?></td>
</tr>
</tbody>
</table>
</div>
<div class="one-sixth"><a class="button" href="<?php echo esc_url( home_url( 'update-profile' ) ); ?>">Edit</a></div>
<?php if ( current_user_can( 'manage_organization' ) ) : ?>
<div class="one-sixth"><a class="button payment-file" href="?generate_payments=true">Export Payments</a></div>
<?php endif; ?>
</div>
<div class="table-container" id="profile-mobile">
<ul class="profile">
<li class="label">Name</li>
<li><?php echo gb_user_full_name(); ?></li>
<li class="label">Email</li>
<li><?php echo gb_user_email(); ?></li>
<li class="label">Payment Plan</li>
<li><?php echo gb_user_payment_plan(); ?></li>
<li class="label">University</li>
<li><?php echo gb_user_university(); ?></li>
<li class="label">Organization</li>
<li><?php echo gb_get_member_organization()->post_title; ?></li>
<li class="label">Member Category</li>
<li><?php echo gb_get_member_category( get_current_user_id() ); ?></li>
</ul>
<a class="button" href="<?php echo esc_url( home_url( 'update-profile' ) ); ?>">Edit</a>
</div>
<div class="table-container one-fourth first">
<div class="payment">
<a class="button" href="#">Make Payment</a>
</div>
<p class="plan-details">Payment Plan Details</p>
<table class="payment-period">
<thead>
<tr>
<th>Due Date</th>
</tr>
</thead>
<tbody>
<?php
foreach ( gb_due_dates() as $plan => $date ) :
?>
<tr>
<td><?php echo date( 'F jS, Y', strtotime( $date['date'] ) ); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="table-container three-fourths">
<div class="balances clearfix">
<div class="running one-third first">
<p class="amount">$<?php echo get_member_remaining_balance(); ?></p>
<p class="label">Term Balance</p>
</div>
<div class="current one-third">
<p class="amount">$<?php echo get_member_current_balance(); ?></p>
<p class="label">Currently Due</p>
</div>
<div class="overdue one-third">
<p class="amount">$<?php echo get_member_overdue_balance(); ?></p>
<p class="label">Overdue</p>
</div>
</div>
<div class="history">
<table>
<thead>
<tr>
<th>Date Added</th>
<th>Due Date</th>
<th>Title</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
<?php
foreach ( gb_get_member_payments() as $payment ) :
$due_date = $payment->due_date;
if ( ! empty( $due_date ) ) {
$due_date = date( 'M j', $due_date );
} else {
$due_date = '--';
}
$minus = 'paid' == $payment->post_status ? '- ' : '';
?>
<tr>
<td><?php echo date( 'M j', strtotime( $payment->post_date ) ); ?></td>
<td><?php echo esc_html( $due_date ); ?></td>
<td><?php echo esc_html( $payment->post_title ); ?></td>
<td class="<?php echo esc_html( $payment->post_status ); ?>">
<?php echo $minus; ?>$<?php echo number_format( floatval( $payment->total_amount ), 2 ); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<div id="payment-options" style="display:none">
<?php gb_payment_modal_payment_options(); ?>
</div>
<div class="table-container clearfix panel" id="roster-panel">
<table class="member-roster">
<div class="tablenav clearfix">
<div class="bulk-actions">
<select>
<option selected="selected">Bulk Actions</option>
<option value="edit">Set Category</option>
<option value="add-transaction">Add Transaction</option>
<option value="delete">Delete</option>
</select>
<input type="submit" id="doaction" class="button" value="Apply" />
</div>
<div class="new-member"><a class="button" href="#">New Member</a></div>
<div class="new-category"><a class="button" href="#">New Category</a></div>
</div>
<thead>
<tr>
<th class="select"><input id="select" type="checkbox" /></th>
<th class="member-name">Member Name</th>
<th class="payment-date">Next Payment Due</th>
<th class="payment-amount">Next Payment Amount</th>
<th class="member-category">Member Category</th>
<th class="total-paid">Total Paid</th>
<th class="remaining-balance">Balance Due</th>
</tr>
</thead>
<tbody>
<?php gb_get_members(); ?>
</tbody>
</table>
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment