Plugin Directory

Changeset 1558838


Ignore:
Timestamp:
12/21/2016 08:07:54 AM (9 years ago)
Author:
pinpointe
Message:

Update v1.2 includes usage of radio buttons, checkboxes, Latin characters, and assigning a single list tag to a form

Location:
pinpointe-form-integration/trunk
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • pinpointe-form-integration/trunk/assets/css/style-admin.css

    r1340221 r1558838  
    354354    left: 0;
    355355    z-index: 99999;
     356}
     357
     358.ui-tooltip {
     359    max-width: 350px;
     360    padding: 0px 8px;
    356361}
    357362
  • pinpointe-form-integration/trunk/assets/forms/js/sky-forms-ie8.js

    r1340221 r1558838  
    11jQuery(function()
    22{
    3     jQuery('input[type="checkbox"]:checked, input[type="radio"]:checked').addClass('checked');
     3    jQuery('.pinpointe-signup-form input[type="checkbox"]:checked, input[type="radio"]:checked').addClass('checked');
    44   
    5     jQuery('.sky-form').on('change', 'input[type="radio"]', function()
     5    jQuery('.pinpointe-signup-form .sky-form').on('change', 'input[type="radio"]', function()
    66    {
    7         jQuery(this).closest('.sky-form').find('input[name="' + jQuery(this).attr('name') + '"]').removeClass('checked');
     7        jQuery(this).closest('.pinpointe-signup-form .sky-form').find('input[name="' + jQuery(this).attr('name') + '"]').removeClass('checked');
    88        jQuery(this).addClass('checked');
    99    });
    1010   
    11     jQuery('.sky-form').on('change', 'input[type="checkbox"]', function()
     11    jQuery('.pinpointe-signup-form .sky-form').on('change', 'input[type="checkbox"]', function()
    1212    {
    1313        jQuery(this).toggleClass('checked');
  • pinpointe-form-integration/trunk/assets/js/pinpointe-admin.js

    r1340221 r1558838  
    190190        jQuery('#submit').prop('disabled', true);
    191191        jQuery('#submit').prop('title', pinpointe_label_still_connecting_to_pinpointe);
     192
     193        jQuery.post(
     194            ajaxurl,
     195            {
     196                'action': 'pinpointe_get_tags_with_multiple_groups_and_fields',
     197                'data': pinpointe_selected_tags
     198            },
     199
     200            function(response) {
     201                try {
     202                    var result = jQuery.parseJSON(response);
     203                }
     204                catch (err) {
     205                    jQuery('.pinpointe_forms_field_tag_groups').html(pinpointe_label_bad_ajax_response);
     206                    jQuery('.pinpointe_forms_field_fields').html(pinpointe_label_bad_ajax_response);
     207                }
     208
     209                if (result && typeof result['message'] === 'object') {
     210                    /**
     211                     * Render lists and groups selection // TODO: Prepopulate form with fields from active list
     212                     */
     213                    var current_field_id = 0;
     214
     215                    jQuery('.pinpointe_forms_field_tag_groups').each(function() {
     216
     217                        current_field_id++;
     218
     219                        var current_selected_tag = (typeof pinpointe_selected_tags[current_field_id] !== 'undefined' && typeof pinpointe_selected_tags[current_field_id]['tag'] !== 'undefined' ? pinpointe_selected_tags[current_field_id]['tag'] : null);
     220
     221                        // List selection
     222                        if (typeof result['message']['tags'] === 'object') {
     223                            // var fields = '';
     224                            var fields = '<option value="">&nbsp;</option>'; // SDL
     225                            for (var prop in result['message']['tags']) {
     226                                fields += '<option value="' + prop + '" ' + (current_selected_tag !== null && current_selected_tag === prop ? 'selected="selected"' : '') + '>' + result['message']['tags'][prop] + '</option>';
     227                            }
     228
     229                            var field_field = '<select id="pinpointe_forms_tag_field_' + current_field_id + '" name="pinpointe_options[forms][' + current_field_id + '][tag_field]" class="pinpointe-field pinpointe_forms_mailing_tag">' + fields + '</select>';
     230                            var field_html = '<table class="form-table" style="margin-bottom: 0px;"><tbody><tr valign="top"><th scope="row">' + pinpointe_label_mailing_tag + '</th><td>' + field_field + '</td></tr></tbody></table>';
     231
     232                            jQuery(this).replaceWith(field_html);
     233
     234                            // Make it chosen!
     235                            jQuery('#pinpointe_forms_tag_field_' + current_field_id).chosen({
     236                                no_results_text: pinpointe_label_no_results_match_tag,
     237                                placeholder_text_single: pinpointe_label_select_mailing_tag,
     238                                width: '400px'
     239                            });
     240
     241                         }
     242
     243                         pinpointe_forms_page_hints();
     244
     245                    });
     246                }
     247            }
     248        );
    192249
    193250        jQuery.post(
  • pinpointe-form-integration/trunk/assets/js/pinpointe-frontend.js

    r1340221 r1558838  
    3636    });
    3737
     38    function pinpointe_process_botbust(form)
     39    {
     40        if (form.find('#addressStarfall').length < 1 || form.find('#addressStarfall').val() != 'Please replace') {
     41            return false;
     42        }
     43
     44        if (form.find('#contactStarfall').length < 1 || form.find('#contactStarfall').val() != '') {
     45            return false;
     46        }
     47
     48        if (form.find('#commentStarfall').length < 1 || form.find('#commentStarfall').val() != '') {
     49            return false;
     50        }
     51
     52        if (form.find('#timerStarfall').length < 1 || form.find('#timerStarfall').val() < 2) {
     53            return false;
     54        }
     55
     56        return true;
     57    }
     58
    3859    /**
    3960     * Pinpointe signup
     
    4465
    4566            button.closest('.pinpointe_signup_form').find('fieldset').fadeOut(function() {
     67                var  this_form = jQuery(this).closest('.pinpointe_signup_form');
     68                var passBots = pinpointe_process_botbust(button.closest('.pinpointe_signup_form'));
    4669
    47                 var  this_form = jQuery(this).closest('.pinpointe_signup_form');
     70                if (!passBots)
     71                {
     72                    this_form.find('#pinpointe_signup_'+context+'_success').children().html('Thank you for subscribing!');
     73                    this_form.find('#pinpointe_signup_'+context+'_success').fadeIn();
     74                    return;
     75                }
     76
    4877                this_form.find('#pinpointe_signup_'+context+'_processing').fadeIn();
    4978                button.prop('disabled', true);
  • pinpointe-form-integration/trunk/includes/pinpointe-prepare-form.inc.php

    r1340221 r1558838  
    9494                    break;
    9595
    96                 // Radio
    97                 case 'radio':
     96                // Radiobutton
     97                case 'radiobutton':
     98                    $field_rules['digits'] = true;
     99                    $field_rules['minlength'] = 1;
     100                    $field_rules['maxlength'] = 3;
     101                    break;
     102
     103                // Checkbox
     104                case 'checkbox':
    98105                    $field_rules['digits'] = true;
    99106                    $field_rules['minlength'] = 1;
     
    221228
    222229        // Start form
    223         $html .= '<form id="pinpointe_' . $context . '_' . $global_form_id . '" class="pinpointe_signup_form sky-form ' . $custom_classes . '">';
     230        $html .= '<form id="pinpointe_' . $context . '_' . $global_form_id . '" class="pinpointe_signup_form sky-form ' . $custom_classes . '" method="post">';
    224231
    225232        // Form ID
     
    253260
    254261            // Radio
    255             if ($field['type'] == 'radio') {
     262            if ($field['type'] == 'radiobutton') {
    256263
    257264                $html .= '<label class="label">' . $field['name'] . '</label>';
     
    292299            }
    293300
     301            else if ($field['type'] == 'checkbox') {
     302
     303                $html .= '<label class="label">' . $field['name'] . '</label>';
     304
     305                foreach ($field['choices'] as $choice_key => $choice) {
     306
     307                    $html .= '<label class="checkbox">';
     308
     309                    $html .= '<input type="checkbox" '
     310                           . 'id="pinpointe_' . $context . '_field_' . $field['tag'] . '_' . $choice_key . '" '
     311                           . 'name="pinpointe_' . $context . '_subscribe[custom][' . $field['tag'] . '][]" '
     312                           . 'value="' . $choice . '" >';
     313
     314                    $html .= '<i></i>' . $choice . '</label>';
     315
     316                }
     317            }
     318
    294319            // Any other field (basic text input)
    295320            else {
     
    299324                }
    300325
    301                 $html .= '<label class="input">';
     326                $html .= '<label class="input" data-type="'.$field['type'].'">';
    302327
    303328                if (isset($field['icon']) && $field['icon']) {
     
    422447        // End footer
    423448        $html .= '</footer>';
     449
     450       // Add botbusting features
     451        $html .= "<div style='display: none'><div>
     452            If the below fields are visible, ignore them.<br>
     453
     454            <input type='text' name='addressStarfall' id='addressStarfall' value='Please replace'><br>
     455            <input type='text' name='contactStarfall' id='contactStarfall' value=''><br>
     456            <textarea cols='40' rows='6' name='commentStarfall' id='commentStarfall'></textarea>
     457            <input type='checkbox' name='termsStarfall' value='Accepted' id='termsStarfall'> Accept Terms?<br>
     458            <input type='text' name='timerStarfall' id='timerStarfall' value='0'>
     459            </div></div>
     460
     461            <script>
     462            var counterStarfall=setInterval(timerStarfallCounter, 1000);
     463            var countStarfall = 0;
     464            function timerStarfallCounter()
     465            {
     466                countStarfall=countStarfall+1;
     467                document.getElementById('timerStarfall').value=countStarfall;
     468            }
     469            timerStarfallCounter();
     470            </script>";
    424471
    425472        // End form
  • pinpointe-form-integration/trunk/includes/pinpointe-service.class.php

    r1340221 r1558838  
    108108            $xmlStr = $dom_output->saveXML($dom_output, LIBXML_NOEMPTYTAG);
    109109
     110            $file = WP_PLUGIN_DIR."/pinpointe-form-integration/test.xml";
     111            $dom_output->save($file);
     112
    110113            curl_setopt($this->curl, CURLOPT_URL, $this->apiUrl);
    111114            curl_setopt($this->curl, CURLOPT_HTTPHEADER, array('Content-Type: application/xml'));
     
    144147            foreach ($arr as $key => $value) {
    145148                if (is_array($value)) {
    146                     $child = $root->addChild($key);
    147 
    148                     $this->arrayToXml($value, $child);
     149                    if ($key != 'value') {
     150                        $child = $root->addChild($key);
     151
     152                        $this->arrayToXml($value, $child);
     153                    } else {
     154                        foreach ($value as $k => $v) {
     155                            $root->addChild($key, $v);
     156                        }
     157                    }
    149158                } elseif (is_a($value, 'SimpleXMLElement')) {
    150159                    $r = $root->addChild($value->getName());
     
    190199
    191200            return array_slice($lists, min($start, count($lists) - 1), max($limit, count($lists) - $limit));
     201        }
     202
     203        /**
     204         * Get the tags
     205         * @param array $filters
     206         * @param int $start
     207         * @param int $limit
     208         */
     209        public function tags_get_tag($start = 0, $limit = 25)
     210        {
     211            $tags = $this->call('tags', 'GetTags');
     212            if(isset($tags['item'])) {
     213                $tags = $tags['item'];
     214            }
     215
     216            if(!is_array($tags) || $this->isAssociativeArray($tags)) {
     217                $tags = [$tags];
     218            }
     219
     220            return array_slice($tags, min($start, count($tags) - 1), max($limit, count($tags) - $limit));
    192221        }
    193222
     
    221250
    222251                foreach($mergeVarForList as $mergeVar) {
    223                     $settings = unserialize($mergeVar['fieldsettings']);
     252                    $settings = $this->mb_unserialize($mergeVar['fieldsettings']);
    224253
    225254                    $mergeVars[$mergeVar['fieldid']] = [
     
    229258                    ];
    230259
    231                     if($mergeVar['fieldtype'] == 'dropdown') {
     260                    if($mergeVar['fieldtype'] == 'dropdown' || $mergeVar['fieldtype'] == 'checkbox' || $mergeVar['fieldtype'] == 'radiobutton') {
    232261                        $choices = [];
    233262                        foreach($settings['Key'] as $index => $key) {
     
    243272
    244273            return $allMergeVars;
     274        }
     275
     276        /**
     277         * Mulit-byte Unserialize
     278         *
     279         * UTF-8 will screw up a serialized string
     280         *
     281         * @access private
     282         * @param string
     283         * @return string
     284         */
     285        function mb_unserialize($string) {
     286            $string = preg_replace('!s:(\d+):"(.*?)";!se', "'s:'.strlen('$2').':\"$2\";'", $string);
     287            return unserialize($string);
    245288        }
    246289
     
    255298         */
    256299        public function lists_subscribe($id, $email, $merge_vars = array(), $email_type = 'html',
    257                                         $add_to_autoresponders = false, $update_existing = false) {
     300                                        $add_to_autoresponders = false, $update_existing = false, $tagid) {
    258301            if(!is_string($email)) {
    259302                $email = $email['email'];
     
    264307                'mailinglist' => $id,
    265308                'format' => $email_type,
    266                 'customfields' => []
     309                'customfields' => [],
     310                'tag' => $tagid
    267311            );
    268312
     
    278322                    $val = array_keys($choices)[array_search($val, array_values($choices))];
    279323                }
     324
     325                // if ($list_merge_vars[$key]['field_type'] == 'checkbox') {
     326                //     $val = '[' . implode(',', $val) . ']';
     327                // }
    280328
    281329                $data['customfields'][] = $this->arrayToXml([
  • pinpointe-form-integration/trunk/pinpointe-signup-form.php

    r1340221 r1558838  
    2727define('PINPOINTE_PLUGIN_PATH', untrailingslashit(plugin_dir_path(__FILE__)));
    2828define('PINPOINTE_PLUGIN_URL', plugins_url(basename(plugin_dir_path(__FILE__)), basename(__FILE__)));
    29 define('PINPOINTE_VERSION', '2.1.3');
     29define('PINPOINTE_VERSION', '2.2.0'); // Increment this to force cached JS to reload
    3030
    3131if (!class_exists('PinpointeSignupForm')) {
     
    9898                add_action('wp_ajax_pinpointe_get_lists_with_multiple_groups_and_fields', array($this, 'ajax_get_lists_groups_fields'));
    9999                add_action('wp_ajax_pinpointe_update_groups_and_tags', array($this, 'ajax_groups_and_tags_in_array'));
     100                add_action('wp_ajax_pinpointe_get_tags', array($this, 'ajax_get_tags'));
     101                add_action('wp_ajax_pinpointe_get_tags_with_multiple_groups_and_fields', array($this, 'ajax_get_tags_groups_fields'));
    100102            }
    101103
     
    529531
    530532                    $pinpointe_selected_lists = array();
     533                    $pinpointe_selected_tags = array();
    531534
    532535                    foreach ($this->opt['forms'] as $form_key => $form) {
     
    536539                            'merge'     => $form['fields']
    537540                        );
     541
     542                        $pinpointe_selected_tags[$form_key] = array(
     543                            'tag'       => $form['tag']
     544                        );
    538545                    }
    539546                }
    540547                else {
    541548                    $pinpointe_selected_lists = array();
     549                    $pinpointe_selected_tags = array();
    542550                }
    543551
     
    553561                    'pinpointe_forms_redirect_url'             => __('<p>Optionaly provide an URL where subscribers should be redirected to after successful signup.</p> <p>Leave this field empty to simply display a thank you message.</p>', 'pinpointe'),
    554562                    'pinpointe_forms_mailing_list'             => __('<p>Select one of your Pinpointe mailing lists for users to be subscribed to.</p>', 'pinpointe'),
     563                    'pinpointe_forms_mailing_tag'             => __('<p>Select one of your Pinpointe list tags for users to be added to.</p>', 'pinpointe'),
    555564                    'pinpointe_forms_groups'                   => __('<p>Select interest groups that you want to add automatically or allow users to choose in the form.</p> <p>If no interest groups are available, either mailing list has not been selected yet in the field above or you have no interest groups created for this list.</p>', 'pinpointe'),
    556565                    'pinpointe_form_group_method'              => __('<p>Select how you would like interest groups to work with this form - you can either add all selected interest groups to subscribers profile by default or allow your visitors to manually select some.</p>', 'pinpointe'),
     
    570579                    var pinpointe_label_still_connecting_to_pinpointe = '<?php _e('Still connecting to Pinpointe...', 'pinpointe'); ?>';
    571580                    var pinpointe_label_mailing_list = '<?php _e('Mailing list', 'pinpointe'); ?>';
     581                    var pinpointe_label_mailing_tag = '<?php _e('Mailing tag', 'pinpointe'); ?>';
    572582                    var pinpointe_label_no_results_match_list = '<?php _e('There are no lists named', 'pinpointe'); ?>';
     583                    var pinpointe_label_no_results_match_tag = '<?php _e('There are no tags named', 'pinpointe'); ?>';
    573584                    var pinpointe_label_select_mailing_list = '<?php _e('Select a mailing list', 'pinpointe'); ?>';
     585                    var pinpointe_label_select_mailing_tag = '<?php _e('Select a mailing tag', 'pinpointe'); ?>';
    574586                    var pinpointe_label_no_results_match_groups = '<?php _e('Selected list does not have groups named', 'pinpointe'); ?>';
    575587                    var pinpointe_label_select_some_groups = '<?php _e('Select some groups (optional)', 'pinpointe'); ?>';
     
    599611                    <?php if ($current_tab == 'forms'): ?>
    600612                        var pinpointe_selected_lists = <?php echo json_encode($pinpointe_selected_lists); ?>;
     613                        var pinpointe_selected_tags = <?php echo json_encode($pinpointe_selected_tags); ?>;
    601614                    <?php endif; ?>
    602615
     
    830843                        // Pass selected properties to Javascript
    831844                        $pinpointe_selected_lists = array();
     845                        $pinpointe_selected_tags = array();
    832846
    833847                        foreach ($saved_forms as $form_key => $form) {
     
    836850                                'groups'    => $form['groups'],
    837851                                'merge'     => $form['fields']
     852                            );
     853                            $pinpointe_selected_tags[$form_key] = array(
     854                                'tag'       => $form['tag']
    838855                            );
    839856                        }
     
    858875                        // Pass selected properties to Javascript
    859876                        $pinpointe_selected_lists = array();
     877                        $pinpointe_selected_tags = array();
    860878                    }
    861879
     
    910928                                    <div class="pinpointe_forms_section">List</div>
    911929                                    <p id="pinpointe_forms_list_<?php echo $form_key; ?>" class="pinpointe_loading_list pinpointe_forms_field_list_groups">
     930                                        <span class="pinpointe_loading_icon"></span>
     931                                        <?php _e('Connecting to Pinpointe...', 'pinpointe'); ?>
     932                                    </p>
     933
     934                                    <div class="pinpointe_forms_section">Tag</div>
     935                                    <p id="pinpointe_forms_tag_<?php echo $form_key; ?>" class="pinpointe_loading_tag pinpointe_forms_field_tag_groups">
    912936                                        <span class="pinpointe_loading_icon"></span>
    913937                                        <?php _e('Connecting to Pinpointe...', 'pinpointe'); ?>
     
    12391263                        $new_forms[$form_number]['list'] = (isset($form['list_field']) && !empty($form['list_field'])) ? $form['list_field']: '';
    12401264
     1265                        // Tag
     1266                        $new_forms[$form_number]['tag'] = (isset($form['tag_field']) && !empty($form['tag_field'])) ? $form['tag_field']: '';
     1267
    12411268                        // Groups
    12421269                        $new_forms[$form_number]['groups'] = array();
     
    18041831
    18051832        /**
     1833         * Get tags from Pinpointe
     1834         *
     1835         * @access public
     1836         * @return boid
     1837         */
     1838        public function ajax_get_tags()
     1839        {
     1840            // Get lists
     1841            $tags = $this->get_tags();
     1842            echo json_encode(array('message' => array('tags' => $tags)));
     1843            die();
     1844        }
     1845
     1846        /**
    18061847         * Get all lists plus groups and fields for selected lists in array
    18071848         *
     
    18651906                foreach ($lists as $list) {
    18661907                    $results[$list['listid']] = $list['name'];
     1908                }
     1909
     1910                return $results;
     1911            }
     1912            catch (Exception $e) {
     1913                return array('' => '');
     1914            }
     1915        }
     1916
     1917        /**
     1918         * Get all tags plus groups and fields for selected lists in array
     1919         *
     1920         * @access public
     1921         * @return void
     1922         */
     1923        public function ajax_get_tags_groups_fields()
     1924        {
     1925            if (isset($_POST['data'])) {
     1926                $data = $_POST['data'];
     1927            }
     1928            else {
     1929                $data = array();
     1930            }
     1931
     1932            // Get tags
     1933            $tags = $this->get_tags();
     1934
     1935            echo json_encode(array('message' => array('tags' => $tags)));
     1936            die();
     1937        }
     1938
     1939        /**
     1940         * Return all tags from Pinpointe to be used in select fields
     1941         *
     1942         * @access public
     1943         * @return array
     1944         */
     1945        public function get_tags()
     1946        {
     1947            $this->load_pinpointe();
     1948
     1949            try {
     1950                if (!$this->pinpointe) {
     1951                    throw new Exception(__('Unable to load tags', 'pinpointe'));
     1952                }
     1953
     1954                $tags = $this->pinpointe->tags_get_tag();
     1955
     1956                if (count($tags) < 1) {
     1957                    throw new Exception(__('No tags found', 'pinpointe'));
     1958                }
     1959
     1960                $results = array('' => '');
     1961
     1962                foreach ($tags as $tag) {
     1963                    $results[$tag['tagid']] = $tag['name'];
    18671964                }
    18681965
     
    25492646         * @return mixed
    25502647         */
    2551         public function subscribe($list_id, $email, $groups, $custom_fields, $is_backend = false)
     2648        public function subscribe($list_id, $email, $groups, $custom_fields, $is_backend = false, $tag_id = 0)
    25522649        {
    25532650            // Load Pinpointe
     
    26042701                    'html',
    26052702                    $this->opt['pinpointe_add_to_autoresponders'],
    2606                     $this->opt['pinpointe_update_existing']
     2703                    false,
     2704                    $tag_id
    26072705                );
    26082706
     
    28202918
    28212919                    // If it's dropdown or radio - extract actual value
    2822                     else if (in_array($field['type'], array('dropdown', 'radio'))) {
     2920                    else if (in_array($field['type'], array('dropdown', 'radiobutton'))) {
    28232921                        if (isset($field['choices'][$data['custom'][$field['tag']]])) {
    28242922                            $custom_fields[$field['tag']] = $field['choices'][$data['custom'][$field['tag']]];
     2923                        }
     2924                        else {
     2925                            if ($field['reg']) {
     2926                                $this->respond_with_error();
     2927                            }
     2928                            else {
     2929                                continue;
     2930                            }
     2931                        }
     2932                    }
     2933
     2934                    else if ($field['type'] == 'checkbox') {
     2935                        if (isset($data['custom'][$field['tag']])) {
     2936                            $custom_fields[$field['tag']] = $data['custom'][$field['tag']];
    28252937                        }
    28262938                        else {
     
    29253037
    29263038            // Subscribe user
    2927             $subscribe_result = $this->subscribe($form['list'], $email, $subscribe_groups, $custom_fields);
     3039            $subscribe_result = $this->subscribe($form['list'], $email, $subscribe_groups, $custom_fields, false, $form['tag']);
    29283040
    29293041            if (is_bool($subscribe_result)) {
  • pinpointe-form-integration/trunk/readme.txt

    r1340221 r1558838  
    33Tags: email marketing, forms, popups, opt-in, mailing list, subscription
    44Requires at least: 3.5
    5 Tested up to: 4.4.1
     5Tested up to: 4.7
     6Stable tag: 4.4.1
    67License: GPLv2 or later
    78License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    4243
    4344== Changelog ==
    44 1.1 - Streamlined workflow for so new users can create forms more intuitively
    45 1.0 - Initial release
     451.2
     46* Tag lists can now be added to a form so when someone subscribes, they are added to that List in your Pinpointe account
     47* Improved the display of tooltips within the settings pages
     48* Added basic bot/spam deterrents to form processing
     49* Further isolated custom JQuery and CSS code
     50* Updated form submissions to use POST rather than GET
     51* Custom fields now support special characters, such as Latin characters
     52* Custom fields now support radio and checkboxes
     53
     541.1
     55* Streamlined workflow for so new users can create forms more intuitively
     56
     571.0
     58* Initial release
Note: See TracChangeset for help on using the changeset viewer.