Plugin Directory

Changeset 3306218


Ignore:
Timestamp:
06/04/2025 08:12:28 AM (10 months ago)
Author:
wanotifier
Message:

v2.7.7

Location:
notifier
Files:
236 added
7 edited

Legend:

Unmodified
Added
Removed
  • notifier/trunk/README.txt

    r3300503 r3306218  
    1 === Notifier - Send Message Notifications Using Business API ===
     1=== Notifier - Send Notifications from Woocommerce, Form Plugins and More! ===
    22Contributors: wanotifier
    33Donate link: https://wanotifier.com
    44Tags: whatsapp api, whatsapp integration, whatsapp notification, woocommerce whatsapp, whatsapp
    55Requires at least: 5.0
    6 Tested up to: 6.7
    7 Stable tag: 2.7.6
     6Tested up to: 6.8
     7Stable tag: 2.7.7
    88Requires PHP: 7.4
    99License: GPLv2 or later
     
    1313
    1414== Description ==
    15 **Integrate WhatsApp API with WordPress** to send WhatsApp notifications for Woocommerce orders, abadoned cart recovery, form submissions from _Contact Form 7_, _Gravity Forms_, _WPForms_ etc and more using the **official WhatsApp Business API**.
     15**Integrate WhatsApp API with WordPress** to send WhatsApp notifications for Woocommerce orders, abadoned cart recovery, form submissions from _Contact Form 7_, _Gravity Forms_, _WPForms_ etc and more using the official [WhatsApp Business API](https://wanotifier.com/whatsapp-business-api/).
    1616
    1717**NOTE:** This plugin requires you to have an account at [WANotifier.com](https://wanotifier.com/). Create your **FREE** account now by [clicking here](https://app.wanotifier.com/create-account/).
     
    135135
    136136== Changelog ==
     137= 2.7.7 - 2025-06-04 =
     138mod: Tested upto version bump and README.txt file update
     139mod: minor UI updates
     140
    137141= 2.7.6 - 2025-05-26 =
    138142add: added order item fields – name, price, total of the first product
  • notifier/trunk/assets/css/admin.css

    r3064698 r3306218  
    3737.select2-container--default .select2-selection--multiple.select2-selection--clearable { padding-right: 25px; }
    3838.select2-container--default .select2-selection--multiple .select2-selection__clear { cursor: pointer; font-weight: bold; height: 20px; margin-right: 10px; margin-top: 5px; position: absolute; right: 0; padding: 1px; }
    39 .select2-container--default .select2-selection--multiple .select2-selection__choice { background-color: #e4e4e4; border: 1px solid #aaa; border-radius: 4px; box-sizing: border-box; display: inline-block; margin-right: 8px; margin-bottom: 8px; padding: 0; padding-left: 20px; position: relative; max-width: 100%; overflow: hidden; text-overflow: ellipsis; vertical-align: bottom; white-space: nowrap; }
    40 .select2-container--default .select2-selection--multiple .select2-selection__choice__display { cursor: default; padding-left: 2px; padding-right: 5px; }
    41 .select2-container--default .select2-selection--multiple .select2-selection__choice__remove { background-color: transparent; border: none; border-right: 1px solid #aaa; border-top-left-radius: 4px; border-bottom-left-radius: 4px; color: #999; cursor: pointer; font-size: 1em; font-weight: bold; padding: 0 4px; position: absolute; left: 0; top: 0; }
     39.select2-container--default .select2-selection--multiple .select2-selection__choice { background-color: #e4e4e4; border: 1px solid #aaa; border-radius: 4px; box-sizing: border-box; display: inline-flex ; margin-right: 8px; margin-bottom: 8px; padding: 0; position: relative; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; align-items: center; }
     40.select2-container--default .select2-selection--multiple .select2-selection__choice__display { cursor: default; padding: 3px 8px 4px 8px; line-height: 1em; }
     41.select2-container--default .select2-selection--multiple .select2-selection__choice__remove { background-color: transparent; border: none; border-right: 1px solid #aaa; border-top-left-radius: 4px; border-bottom-left-radius: 4px; color: #999; cursor: pointer; font-size: 1em; font-weight: bold; padding: 2px 6px 4px 6px; }
    4242
    4343.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover,
     
    10371037  background: #f7f7f7;
    10381038  padding: 10px;
    1039   max-height: 150px;
     1039  max-height: 200px;
    10401040  overflow: auto;
    10411041  border: 1px solid #ddd;
     
    10431043  margin-top: 10px;
    10441044}
     1045.notifier-tool-activity-wrap .activity-log-preview-wrap table {
     1046  width: 100px;
     1047}
     1048.notifier-tool-activity-wrap .activity-log-preview-wrap table .activity-record td {
     1049  vertical-align: top;
     1050}
     1051.notifier-tool-activity-wrap .activity-log-preview-wrap table .activity-record td:first-child {
     1052  padding-right: 10px;
     1053  white-space: nowrap;
     1054}
  • notifier/trunk/assets/js/admin.js

    r3064698 r3306218  
    1414
    1515    // Show / hide fields as per conditional logic
    16   function conditionallyShowFields() {
    17     if ($('.meta-fields').length == 0) {
    18       return;
    19     }
    20 
    21     $('.form-field').each(function() {
    22       var thisField = $(this).find(':input');
    23 
    24       // Conditionally show/hide fields
    25       var conditions = $(this).attr('data-conditions') || '';
    26       var conditionsOperator = $(this).attr('data-conditions-operator') || 'OR';
    27       if (conditions !== '') {
    28         var fieldElem = $(this);
    29         var conditionsArray = JSON.parse(conditions);
    30         var fieldConditionResults = [];
    31 
    32         conditionsArray.forEach(function(condition) {
    33           var fieldClass = '.' + condition.field + '_field';
    34           var fieldInput = $(fieldClass + ' :input');
    35           var fieldInputType = fieldInput.prop('type');
    36 
    37           // Do not fetch value of hidden fields.
    38           if(!$(fieldClass).is(':visible')){
    39             return;
    40           }
    41 
    42           // Get field value
    43           var fieldVal = fieldInput.val();
    44 
    45           // Get field value if it's radio or checkbox
    46           if($.inArray(fieldInputType, ['radio', 'checkbox']) !== -1) {
    47             fieldVal = $(fieldClass + ' :input:checked').val();
    48           }
    49 
    50           var showThis = false;
    51           if(condition.operator == '==') {
    52             showThis = (fieldVal == condition.value) ? true : false;
    53           }
    54           else if(condition.operator == '!=') {
    55             showThis = (fieldVal != condition.value) ? true : false;
    56           }
    57 
    58           fieldConditionResults.push(showThis);
    59         });
    60 
    61         if('OR' == conditionsOperator){
    62           var showField = false;
    63         }
    64         else if('AND' == conditionsOperator){
    65           var showField = fieldConditionResults[0];
    66         }
    67 
    68         fieldConditionResults.forEach(function(showThis){
    69           if('OR' == conditionsOperator){
    70             showField = showField || showThis;
    71           }
    72           else if('AND' == conditionsOperator){
    73             showField = showField && showThis;
    74           }
    75         });
    76 
    77         if(showField){
    78           fieldElem.show();
    79           var disabled = fieldElem.find(':input').attr('data-disabled') || 'no';
    80           if('no' == disabled){
    81             fieldElem.find(':input').removeAttr('disabled');
    82           }
    83         }
    84         else{
    85           fieldElem.hide();
    86           fieldElem.find(':input').attr('disabled', 'disabled');
    87         }
    88       }
    89 
    90       // Apply data limit on fields
    91       if (thisField.hasClass('force-text-limit')) {
    92         var content = thisField.val();
    93         var contentLength = content.length;
    94         var limit = thisField.attr('data-limit');
    95         if (contentLength >= limit) {
    96           thisField.siblings('label').find('.limit-used').text(limit);
    97           thisField.val(content.substr(0, limit));
    98         } else {
    99           thisField.siblings('label').find('.limit-used').text(contentLength);
    100         }
    101       }
    102 
    103     });
    104   }
     16    function conditionallyShowFields() {
     17      if ($('.meta-fields').length == 0) {
     18        return;
     19      }
     20
     21      $('.form-field').each(function() {
     22        var thisField = $(this).find(':input');
     23
     24        // Conditionally show/hide fields
     25        var conditions = $(this).attr('data-conditions') || '';
     26        var conditionsOperator = $(this).attr('data-conditions-operator') || 'OR';
     27        if (conditions !== '') {
     28          var fieldElem = $(this);
     29          var conditionsArray = JSON.parse(conditions);
     30          var fieldConditionResults = [];
     31
     32          conditionsArray.forEach(function(condition) {
     33            var fieldClass = '.' + condition.field + '_field';
     34            var fieldInput = $(fieldClass + ' :input');
     35            var fieldInputType = fieldInput.prop('type');
     36
     37            // Do not fetch value of hidden fields.
     38            if(!$(fieldClass).is(':visible')){
     39              return;
     40            }
     41
     42            // Get field value
     43            var fieldVal = fieldInput.val();
     44
     45            // Get field value if it's radio or checkbox
     46            if($.inArray(fieldInputType, ['radio', 'checkbox']) !== -1) {
     47              fieldVal = $(fieldClass + ' :input:checked').val();
     48            }
     49
     50            var showThis = false;
     51            if(condition.operator == '==') {
     52              showThis = (fieldVal == condition.value) ? true : false;
     53            }
     54            else if(condition.operator == '!=') {
     55              showThis = (fieldVal != condition.value) ? true : false;
     56            }
     57
     58            fieldConditionResults.push(showThis);
     59          });
     60
     61          if('OR' == conditionsOperator){
     62            var showField = false;
     63          }
     64          else if('AND' == conditionsOperator){
     65            var showField = fieldConditionResults[0];
     66          }
     67
     68          fieldConditionResults.forEach(function(showThis){
     69            if('OR' == conditionsOperator){
     70              showField = showField || showThis;
     71            }
     72            else if('AND' == conditionsOperator){
     73              showField = showField && showThis;
     74            }
     75          });
     76
     77          if(showField){
     78            fieldElem.show();
     79            var disabled = fieldElem.find(':input').attr('data-disabled') || 'no';
     80            if('no' == disabled){
     81              fieldElem.find(':input').removeAttr('disabled');
     82            }
     83          }
     84          else{
     85            fieldElem.hide();
     86            fieldElem.find(':input').attr('disabled', 'disabled');
     87          }
     88        }
     89
     90        // Apply data limit on fields
     91        if (thisField.hasClass('force-text-limit')) {
     92          var content = thisField.val();
     93          var contentLength = content.length;
     94          var limit = thisField.attr('data-limit');
     95          if (contentLength >= limit) {
     96            thisField.siblings('label').find('.limit-used').text(limit);
     97            thisField.val(content.substr(0, limit));
     98          } else {
     99            thisField.siblings('label').find('.limit-used').text(contentLength);
     100          }
     101        }
     102
     103      });
     104    }
    105105
    106106    // Fetch and display trigger fields
    107   function fetchAndDisplayTriggerFields(){
     107    function fetchAndDisplayTriggerFields(){
    108108        const post_id = $('#post_ID').val() || 0;
    109109        const trigger = $('#notifier_trigger').val() || '';
     
    133133        });
    134134
    135   }
     135    }
    136136
    137137    // Uploading media to WP using wp.media
    138   var file_frame;
    139   function uploadMediaFile( button, preview_media ) {
    140       var button_id = button.attr('id');
    141       var field_id = button_id.replace( '_button', '' );
    142       var preview_id = button_id.replace( '_button', '_preview' );
    143 
    144       // Create the media frame.
    145       file_frame = wp.media.frames.file_frame = wp.media({
    146         title: button.data( 'uploader_title' ),
    147         button: {
    148           text: button.data( 'uploader_button_text' ),
    149         },
    150         library: {
    151             type: button.data( 'uploader_supported_file_types' ).split(',')
    152         },
    153         multiple: false
    154       });
    155 
    156       // When an image is selected, run a callback.
    157       file_frame.on( 'select', function() {
    158         attachment = file_frame.state().get('selection').first().toJSON();
    159         jQuery("#"+field_id).attr('data-type', attachment.type);
    160         jQuery("#"+field_id).attr('data-subtype', attachment.subtype);
    161         jQuery("#"+field_id).siblings('.notifier-media-preview').find('.notifier-media-preview-item').addClass('hide');
    162         if( preview_media ) {
    163         if('image' == attachment.type || ('application' == attachment.type && 'pdf' == attachment.subtype) ) {
     138    var file_frame;
     139    function uploadMediaFile( button, preview_media ) {
     140        var button_id = button.attr('id');
     141        var field_id = button_id.replace( '_button', '' );
     142        var preview_id = button_id.replace( '_button', '_preview' );
     143
     144        // Create the media frame.
     145        file_frame = wp.media.frames.file_frame = wp.media({
     146          title: button.data( 'uploader_title' ),
     147          button: {
     148            text: button.data( 'uploader_button_text' ),
     149          },
     150          library: {
     151            type: button.data( 'uploader_supported_file_types' ).split(',')
     152          },
     153          multiple: false
     154        });
     155
     156        // When an image is selected, run a callback.
     157        file_frame.on( 'select', function() {
     158          attachment = file_frame.state().get('selection').first().toJSON();
     159          jQuery("#"+field_id).attr('data-type', attachment.type);
     160          jQuery("#"+field_id).attr('data-subtype', attachment.subtype);
     161          jQuery("#"+field_id).siblings('.notifier-media-preview').find('.notifier-media-preview-item').addClass('hide');
     162          if( preview_media ) {
     163          if('image' == attachment.type || ('application' == attachment.type && 'pdf' == attachment.subtype) ) {
    164164            jQuery("#"+field_id).attr('data-url', attachment.sizes.full.url);
    165165            jQuery("#"+preview_id+'_image').removeClass('hide').attr('src', attachment.sizes.thumbnail.url);
    166         }
    167         else if ('video' == attachment.type) {
    168             jQuery("#"+field_id).attr('data-url', attachment.url);
    169             jQuery("#"+preview_id+'_video').removeClass('hide').find('source').attr('src', attachment.url);
    170             jQuery("#"+preview_id+'_video')[0].load();
    171         }
    172       }
    173       jQuery("#"+field_id).val(attachment.id).change();
    174       });
    175 
    176       // Finally, open the modal
    177       file_frame.open();
    178   }
     166          }
     167          else if ('video' == attachment.type) {
     168            jQuery("#"+field_id).attr('data-url', attachment.url);
     169            jQuery("#"+preview_id+'_video').removeClass('hide').find('source').attr('src', attachment.url);
     170            jQuery("#"+preview_id+'_video')[0].load();
     171          }
     172        }
     173        jQuery("#"+field_id).val(attachment.id).change();
     174        });
     175
     176        // Finally, open the modal
     177        file_frame.open();
     178    }
     179
     180    // Fetch activity logs on Tools page
     181    function fetchActivityLogs(){
     182        $('.activity-log-preview-wrap').html('');
     183        var currenEle = $('#notifier_activity_date');
     184        if(currenEle.val() === ''){
     185            return false;
     186        }
     187        currenEle.addClass('disabled-field');
     188        data = {
     189            'action': 'fetch_activity_logs_by_date',
     190            'notifier_activity_date': currenEle.val(),
     191        }
     192
     193        notifierAjax(data, function(response){
     194            currenEle.removeClass('disabled-field');
     195            $('.activity-log-preview-wrap').html(response.preview);
     196        });
     197    }
    179198
    180199    $(document).on('ready', function() {
     
    297316        });
    298317
    299         // Do stuff if on the trigger edit page
     318            // Do stuff if on the trigger edit page
    300319        if ($('#notifier-trigger-data').length > 0) {
    301320            fetchAndDisplayTriggerFields(); // Fetch and display data and recipient fields
     
    324343         * Tools page
    325344         ****************/     
     345        if($('#notifier_activity_date').length > 0){
     346            fetchActivityLogs();
     347        }
     348
    326349        $(document).on('change', '#notifier_activity_date', function(){
    327             $('.activity-log-preview-wrap').html('');
    328             var currenEle = $(this);
    329             if(currenEle.val() === ''){
    330                 return false;
    331             }
    332             currenEle.addClass('disabled-field');
    333             data = {
    334                 'action': 'fetch_activity_logs_by_date',
    335                 'notifier_activity_date': currenEle.val(),
    336             }
    337 
    338             notifierAjax(data, function(response){
    339                 currenEle.removeClass('disabled-field');
    340                 $('.activity-log-preview-wrap').html(response.preview);
    341             });
     350            fetchActivityLogs();
    342351        });
    343352
  • notifier/trunk/includes/classes/class-notifier-tools.php

    r3231402 r3306218  
    205205            foreach ($logs as $log){
    206206                $logs_preview_htm .= '<tr class="activity-record">';
    207                 $logs_preview_htm .= '<td style="width:10%; vertical-align: top;"><strong>'.esc_html($log->timestamp).'</strong> </td>';
    208                 $logs_preview_htm .= '<td style="width:90%; vertical-align: top;">'.esc_html($log->message).'</td>';
     207                $logs_preview_htm .= '<td><strong>'.esc_html($log->timestamp).'</strong> </td>';
     208                $logs_preview_htm .= '<td>'.esc_html($log->message).'</td>';
    209209                $logs_preview_htm .= '</tr>';
    210210            }
  • notifier/trunk/includes/classes/integrations/class-notifier-woocommerce.php

    r3300503 r3306218  
    233233                'label'     => 'Order first item featured image',
    234234                'return_type'   => 'image',
    235                 'value'     => function ($order, $field_function) {
    236                     $image_id = false;
    237                     $image_url = '';
    238                     foreach($order->get_items() as $item){
    239                         $first_product_id = $item->get_product_id();
    240                         $product = wc_get_product( $first_product_id );
    241                         $image_id = $product->get_image_id();
    242                         if($image_id){
    243                             break;
     235                'value'       => function ( $order, $field_function ) {
     236                    foreach ( $order->get_items() as $item ) {
     237                        $product = $item->get_product();
     238                        if ( $product ) {
     239                            $image_id = $product->get_image_id();
     240                            if ( $image_id ) {
     241                                return wp_get_attachment_url( $image_id );
     242                            } else {
     243                                return wc_placeholder_img_src();
     244                            }
    244245                        }
    245                     }
    246 
    247                     if($image_id){
    248                         $image_url = wp_get_attachment_url( $product->get_image_id() );
    249                     }
    250                     else{
    251                         $image_url = wc_placeholder_img_src();
    252                     }
    253                     return $image_url;
     246                        break;
     247                    }
     248                    return wc_placeholder_img_src();
    254249                }
    255250            ),
     
    12841279        }
    12851280    }
    1286 
    12871281}
  • notifier/trunk/notifier.php

    r3300503 r3306218  
    11<?php
    22/**
    3  * Plugin Name: Notifier - Send Message Notifications Using Business API
     3 * Plugin Name: Notifier - Send Notifications from Woocommerce, Form Plugins and More!
    44 * Plugin URI: https://wordpress.org/plugins/notifier/
    55 * Description: Integrate WhatsApp API with WordPress to send WhatsApp notifications from Woocommerce, Contact Form 7, Gravity Forms, WPForms & more.
    6  * Version: 2.7.6
     6 * Version: 2.7.7
    77 * Author: WANotifier.com
    88 * Author URI: https://wanotifier.com
  • notifier/trunk/views/admin-tools.php

    r3231402 r3306218  
    4646                            <select name="notifier_activity_date" id="notifier_activity_date">
    4747                                <option value="">Select date</option>
    48                                 <?php foreach ($dates as $date){ ?>
    49                                     <option value="<?php echo esc_attr($date); ?>"><?php echo esc_html($date); ?></option>
     48                                <?php foreach ($dates as $key => $date){ ?>
     49                                    <option value="<?php echo esc_attr($date); ?>" <?php echo ($key == 0) ? 'selected' : ''; ?>><?php echo esc_html($date); ?></option>
    5050                                <?php } ?>
    5151                            </select>
Note: See TracChangeset for help on using the changeset viewer.