Plugin Directory

Changeset 2683995


Ignore:
Timestamp:
02/23/2022 08:32:26 PM (4 years ago)
Author:
mintunmedia
Message:

v2.1.3

Location:
groups-for-membermouse
Files:
50 added
5 edited

Legend:

Unmodified
Added
Removed
  • groups-for-membermouse/trunk/css/groups-leader-dashboard.css

    r2642052 r2683995  
    4444  margin: 0;
    4545}
     46
     47.search-input-container {
     48  display: flex;
     49}
     50
     51/* Styling for group leader table filters */
     52.filter-header-wrapper.active,
     53.filter-header-wrapper.inactive {
     54  display: flex;
     55  justify-content: center;
     56  align-items: center;
     57}
     58
     59.filter-header {
     60  /* display: flex;
     61  justify-content: center;
     62  align-items: center; */
     63}
     64
     65.filter-header-wrapper.order-asc:after,
     66.filter-header-wrapper.order-desc:after,
     67.filter-header-wrapper.order-none:after {
     68  content: ' ';
     69  position: relative;
     70  margin-left: 13px;
     71  border: 5px solid transparent;
     72}
     73
     74.filter-header-wrapper.order-desc:after {
     75  border-bottom-color: #333;
     76}
     77
     78.filter-header-wrapper.order-asc:after {
     79  border-top-color: #333;
     80}
     81
     82.filter-header-wrapper.order-none:after {
     83  border-bottom-color: transparent;
     84}
     85
     86.filter-header-wrapper.order-none:hover:after {
     87  border-top-color: #333;
     88}
     89
     90.filter-header-wrapper:hover {
     91  cursor: pointer;
     92}
  • groups-for-membermouse/trunk/groups-for-membermouse.php

    r2681090 r2683995  
    44 * Plugin Name: Groups for MemberMouse
    55 * Description: Adds group support to MemberMouse. You can define different types of groups allowing a single customer to pay for multiple seats and members to join existing groups for free or for a price based on how you configure the group type. <strong>Requires MemberMouse to activate and use.</strong>
    6  * Version: 2.1.2
     6 * Version: 2.1.3
    77 * Author: Mintun Media
    88 * Plugin URI:  https://www.mintunmedia.com
  • groups-for-membermouse/trunk/includes/class.shortcodes.php

    r2681090 r2683995  
    4242
    4343    // Ajax
     44    add_action('wp_ajax_groups_load_members', array($this, 'ajax_load_members'));
    4445    add_action('wp_ajax_groups_get_signup_link', array($this, 'ajax_get_signup_link'));
    4546    add_action('wp_ajax_groups_update_group_name', array($this, 'ajax_update_group_name'));
     
    8182  public function generate_group_leader_dashboard($atts) {
    8283    global $wpdb, $current_user;
     84
     85    $search = $_GET["q"];
     86    $filter = $_GET["filter"];
     87    $order = $_GET["order"];
    8388
    8489    $controls = array(
     
    145150    </div>
    146151
    147     <div class="member-count">
    148       <p>Members: <?= $member_count ?>/<?= $group_size ?></p>
     152    <!-- TODO create a JS action that refreshes the page & changes the query param for 'search'. -->
     153    <div class="search-input-container">
     154      <input type="text" id="members-search-input" placeholder="Search Members by Email or Name" aria-placeholder="Search Members by Email or Name" value="<?php echo $search; ?>">
     155      <button id="members-search" class="btn btn-primary">Search</button>
     156      <button id="clear-search" class="btn btn-primary">Clear</button>
    149157    </div>
    150158
     
    152160      <p><em>No members found.</em></p>
    153161    <?php } else { ?>
     162      <?php
     163
     164      $gMemResultsData = array();
     165      foreach ($gMemResults as $gMemRes) {
     166        $userSql      = "SELECT * FROM " . $wpdb->prefix . "users WHERE ID = '" . $gMemRes->member_id . "'";
     167        $userResult    = $wpdb->get_row($userSql);
     168        $registered    = $userResult->user_registered;
     169        $memSql        = "SELECT * FROM mm_user_data WHERE wp_user_id = '" . $gMemRes->member_id . "'";
     170        $memResult    = $wpdb->get_row($memSql);
     171        $firstName     = $memResult->first_name;
     172        $lastName     = $memResult->last_name;
     173        $email         = $userResult->user_email;
     174        $phone         = empty($memResult->phone) ? "&mdash;" : $memResult->phone;
     175        $membershipId  = $memResult->membership_level_id;
     176        $levelSql     = "SELECT name FROM mm_membership_levels WHERE id = '" . $membershipId . "'";
     177        $levelResult  = $wpdb->get_row($levelSql);
     178        $redirecturl      = "";
     179        $crntMemberId     = $gMemRes->member_id;
     180        $member         = new MM_User($crntMemberId);
     181        $url           = "javascript:mmjs.changeMembershipStatus('" . $crntMemberId . "', ";
     182        $url             .= $membershipId . ", " . MM_Status::$CANCELED . ", '" . $redirecturl . "');";
     183        $cancellationHtml   = "<a title=\"Cancel Member\" style=\"cursor: pointer;display: none;\" onclick=\"" . $url . "\"/>" . MM_Utils::getIcon('stop', 'red', '1.2em', '1px') . "</a>";
     184        $statusId = (int) $gMemRes->member_status;
     185
     186        // Get Member's Active Subscriptions - includes overdue subscriptions
     187        $activeSubscriptions = $member->getActiveMembershipSubscriptions(true);
     188
     189        if (empty($activeSubscriptions)) {
     190          // No Subscriptions
     191          $has_subscriptions = false;
     192        } else {
     193          $has_subscriptions = true;
     194        }
     195
     196        switch ($statusId) {
     197          case 1:
     198            $status = "Active";
     199            break;
     200          case 0:
     201            $status = "Deactivated";
     202            break;
     203        }
     204
     205        array_push(
     206          $gMemResultsData,
     207          array(
     208            'member_id' => $gMemRes->member_id,
     209            'membership_id' => $membershipId,
     210            'name' => "$firstName&nbsp;$lastName",
     211            'first_name' => $firstName,
     212            'last_name' => $lastName,
     213            'email' => $email,
     214            'phone' => $phone,
     215            'registered' => $registered,
     216            'status' => $status,
     217            'status_id' => $statusId,
     218            'has_subscriptions' => $has_subscriptions,
     219            'cancellation_html' => $cancellationHtml,
     220            'cancel_url' => $url
     221          )
     222        );
     223      }
     224
     225
     226      // 1. Run search query if there is one.
     227      // 2. Then sort all results by filter and order.
     228      $filteredData = $gMemResultsData;
     229
     230      if (!empty($filter) && !empty($order)) {
     231        $filteredData = $this->filter_member_results($gMemResultsData, $filter, $order);
     232      }
     233
     234      if (!empty($search)) {
     235        $filteredData = $this->search_member_results($filteredData, $search);
     236      }
     237      ?>
     238
     239      <div class="member-count">
     240        <p>Members: <?= sizeof($filteredData) ?>/<?= $group_size ?></p>
     241      </div>
     242
     243      <?php if (!empty($search)) : ?>
     244        <div class="search-result-notif">
     245          <h3>Search Results for <span class="query">"<?php echo $search ?>"</span></h3>
     246        </div>
     247      <?php endif; ?>
     248
    154249      <table class="widefat" id="mm-data-grid" style="width:96%">
    155250        <thead>
    156251          <tr>
    157             <th>Name</th>
    158             <th>Email</th>
     252            <th>
     253              <div class="<?php echo $this->filter_header_class($filter, 'name', $order); ?>" data-filter="name">Name</div>
     254            </th>
     255            <th>
     256              <div class="<?php echo $this->filter_header_class($filter, 'email', $order); ?>" data-filter="email">Email</div>
     257            </th>
    159258            <th>Phone</th>
    160             <th>Registered</th>
    161             <th>Status</th>
     259            <th>
     260              <div class="<?php echo $this->filter_header_class($filter, 'registered', $order); ?>" data-filter="registered">Registered</div>
     261            </th>
     262            <th>
     263              <div class="<?php echo $this->filter_header_class($filter, 'status', $order); ?>" data-filter="status">Status</div>
     264            </th>
    162265            <?php if ($action_control != 'hide') {
    163266              echo $action_header_content;
     
    166269        </thead>
    167270        <tbody>
    168           <?php foreach ($gMemResults as $gMemRes) :
    169             $userSql      = "SELECT * FROM " . $wpdb->prefix . "users WHERE ID = '" . $gMemRes->member_id . "'";
    170             $userResult    = $wpdb->get_row($userSql);
    171             $registered    = $userResult->user_registered;
    172             $memSql        = "SELECT * FROM mm_user_data WHERE wp_user_id = '" . $gMemRes->member_id . "'";
    173             $memResult    = $wpdb->get_row($memSql);
    174             $firstName     = $memResult->first_name;
    175             $lastName     = $memResult->last_name;
    176             $email         = $userResult->user_email;
    177             $phone         = empty($memResult->phone) ? "&mdash;" : $memResult->phone;
    178             $membershipId  = $memResult->membership_level_id;
    179             $levelSql     = "SELECT name FROM mm_membership_levels WHERE id = '" . $membershipId . "'";
    180             $levelResult  = $wpdb->get_row($levelSql);
     271          <?php foreach ($filteredData as $member) :
    181272            $redirecturl      = "";
    182             $crntMemberId     = $gMemRes->member_id;
    183             $member         = new MM_User($crntMemberId);
    184             $url           = "javascript:mmjs.changeMembershipStatus('" . $crntMemberId . "', ";
    185             $url             .= $membershipId . ", " . MM_Status::$CANCELED . ", '" . $redirecturl . "');";
     273            $url           = "javascript:mmjs.changeMembershipStatus('" . $member['member_id'] . "', ";
     274            $url             .= $member['membership_id'] . ", " . MM_Status::$CANCELED . ", '" . $redirecturl . "');";
    186275            $cancellationHtml   = "<a title=\"Cancel Member\" style=\"cursor: pointer;display: none;\" onclick=\"" . $url . "\"/>" . MM_Utils::getIcon('stop', 'red', '1.2em', '1px') . "</a>";
    187             $statusId = (int) $gMemRes->member_status;
    188 
    189             // Get Member's Active Subscriptions - includes overdue subscriptions
    190             $activeSubscriptions = $member->getActiveMembershipSubscriptions(true);
    191 
    192             if (empty($activeSubscriptions)) {
    193               // No Subscriptions
    194               $has_subscriptions = false;
    195             } else {
    196               $has_subscriptions = true;
    197             }
    198 
    199             switch ($statusId) {
    200               case 1:
    201                 $status = "Active";
    202                 break;
    203               case 0:
    204                 $status = "Deactivated";
    205                 break;
    206             }
    207276
    208277          ?>
    209             <tr class="<?= strtolower($status) ?>">
    210               <td><?php echo $firstName . '&nbsp;' . $lastName; ?></td>
    211               <td><?php echo $email; ?></td>
    212               <td><?php echo $phone; ?></td>
    213               <td><?php echo date('F d, Y h:m a', strtotime($registered)); ?></td>
    214               <td><?= $status; ?></td>
     278            <tr class="<?= strtolower($member->status) ?>">
     279              <td><?php echo $member['name']; ?></td>
     280              <td><?php echo $member['email']; ?></td>
     281              <td><?php echo $member['phone']; ?></td>
     282              <td><?php echo date('F d, Y h:m a', strtotime($member['registered'])); ?></td>
     283              <td><?= $member['status']; ?></td>
    215284              <?php if ($action_control != 'hide') { ?>
    216285                <td>
    217286                  <?php
    218                   if ($has_subscriptions) {
     287                  if ($member['has_subscriptions']) {
    219288                    // Member has active subscriptions. Show error
    220289                    echo $cancellationHtml;
    221290                    echo MM_Utils::getDeleteIcon("This member has an active paid membership which must be canceled before they can be removed from the group. Please contact support.", 'margin-left:5px;', '', true);
    222291                  } else if ($statusId === 1) {
    223                     $deleteActionUrl = 'href="#" class="delete-member" data-member-id="' .  $gMemRes->member_id . '" data-name="' . $firstName . ' ' . $lastName . '"';
     292                    $deleteActionUrl = 'href="#" class="delete-member" data-member-id="' .  $member['member_id'] . '" data-name="' . $member['first_name'] . ' ' . $member['last_name'] . '"';
    224293                    echo MM_Utils::getDeleteIcon("Remove the member from this group", 'margin-left:5px;', $deleteActionUrl);
    225294                  }
     
    279348  }
    280349
     350  /**
     351   * Searches through the member results for a relatable match.
     352   */
     353  public function search_member_results($results, $query) {
     354    return array_filter($results, function ($result) use ($query) {
     355      $name_match = strpos(
     356        strtolower($result['name']),
     357        strtolower($query)
     358      );
     359
     360      $email_match = strpos(
     361        strtolower($result['email']),
     362        strtolower($query)
     363      );
     364
     365      return ($name_match !== false || $email_match !== false);
     366    });
     367  }
     368
     369  /**
     370   * Creates a class string for a Filter Header on the Leader Table.
     371   *
     372   * $currentFilter (optional)
     373   *
     374   * @return string
     375   */
     376  public function filter_header_class($currentFilter, $filterHeader, $order) {
     377    $class = "filter-header-wrapper";
     378    if ($currentFilter == $filterHeader) {
     379      $order_lower = strtolower($order);
     380      $class = "$class active order-$order_lower";
     381    } else {
     382      $class = "$class inactive order-none";
     383    }
     384    return $class;
     385  }
     386
     387  /**
     388   * Filter member results based on filter and order given.
     389   *
     390   * @return array
     391   */
     392  public function filter_member_results($data, $filter, $order) {
     393    $sorted_data = $data;
     394
     395    if ($order == 'ASC') {
     396      $sort = usort($sorted_data, function ($item1, $item2) use ($filter) {
     397        return $item1[$filter] <=> $item2[$filter];
     398      });
     399    } else {
     400      $sort = usort($sorted_data, function ($item1, $item2) use ($filter) {
     401        return $item2[$filter] <=> $item1[$filter];
     402      });
     403    }
     404
     405    if ($sort) {
     406      return $sorted_data;
     407    } else {
     408      return $data;
     409    }
     410  }
    281411
    282412  /**
  • groups-for-membermouse/trunk/js/groups-leader-dashboard.js

    r2642052 r2683995  
    99  const $groupSignupLinkTrigger = $('#signup-link');
    1010  const $addMemberTrigger = $('#add-member');
     11  const $clearSearchTrigger = $("#clear-search");
    1112  let $deleteMemberTrigger = $('.delete-member');
     13
     14  let $filterHeaderTrigger = $('.filter-header-wrapper');
     15  let $searchTrigger = $('#members-search');
     16  let $searchInput = $('#members-search-input');
    1217
    1318  $groupSignupLinkTrigger.click(openSignUpLinkPop);
     
    1520  $addMemberTrigger.click(openAddMemberPop);
    1621  $deleteMemberTrigger.click(openDeleteMemberPop);
     22  $filterHeaderTrigger.click(changeMemberFilter);
     23  $searchTrigger.on('click', searchMembers);
     24  $clearSearchTrigger.on('click', clearSearch);
     25
     26  /**
     27   * Refreshes the page and changes the member filter.
     28   * When pressing a filter link, the order is as follows:
     29   *  No filter -> ASC filter -> DESC filter -> Reset to No Filter
     30   */
     31  function changeMemberFilter() {
     32    let selectedFilter = $(this).data('filter');
     33    let params = (new URL(document.location)).searchParams;
     34    let newOrder = null;
     35    let currentFilter = null;
     36
     37    if (params.has('filter')) {
     38      let currentFilter = params.get('filter');
     39      if (currentFilter !== selectedFilter) {
     40        params.delete('order');
     41      }
     42    }
     43
     44    if (params.has('order')) {
     45      let order = params.get('order');
     46      if (order === 'ASC') {
     47        newOrder = 'DESC';
     48      }
     49
     50      if (order === 'DESC') {
     51        newOrder = null;
     52      }
     53    } else {
     54      newOrder = 'ASC';
     55    }
     56
     57    params.set('filter', $(this).data('filter'));
     58
     59    if (newOrder != null) {
     60      params.set('order', newOrder);
     61    } else {
     62      params.delete('order');
     63      params.delete('filter');
     64    }
     65
     66    location.search = params.toString();
     67  }
     68
     69  /**
     70   * Refreshes the page to run a search query.
     71   */
     72  function searchMembers() {
     73    let searchQuery = $searchInput.val();
     74    let params = (new URL(document.location)).searchParams;
     75    params.set('q', searchQuery);
     76    location.search = params.toString();
     77  }
     78
     79  /**
     80   * Clears the search results.
     81   */
     82  function clearSearch() {
     83    let params = (new URL(document.location)).searchParams;
     84    if (params.has('q')) {
     85      params.delete('q');
     86      location.search = params.toString();
     87    }
     88  }
    1789
    1890
  • groups-for-membermouse/trunk/readme.txt

    r2681090 r2683995  
    33Tags: membermouse, member management, membership site, groups, mm groups, groups for membermouse, membermouse groups
    44Requires at least: 4.8
    5 Tested up to: 5.8.2
     5Tested up to: 5.9
    66Requires PHP: 5.6
    77Stable tag: trunk
     
    6969
    7070== Changelog ==
    71 * 2.1.2
     712.1.3
     72- ENHANCEMENT: Added Filtering and sorting functionality to Group Leader Dashboard
     73
     742.1.2
    7275- ENHANCEMENT: Updated group leader dashboard shortcode to allow showing/hiding elements. Updated
    7376- ENHANCEMENT: Added a View Only member dashboard shortcode visible by members
Note: See TracChangeset for help on using the changeset viewer.