SendGrid - Version 1.9.0

Version Description

  • Added the SendGrid Subscription Widget
  • The settings page now has tabs to separate the configuration of general settings from the widget settings
  • Fixed an issue where a 'gzinflate()' warning was displayed in Query Monitor for each plugin request
  • Fixed an issue where the API Key would be deleted from the db if it was set in wp-config
Download this release

Release Info

Developer team-rs
Plugin Icon 128x128 SendGrid
Version 1.9.0
Comparing to
See all releases

Code changes from version 1.8.2 to 1.9.0

lib/class-sendgrid-mc-optin.php ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once plugin_dir_path( __FILE__ ) . 'class-sendgrid-tools.php';
4
+ require_once plugin_dir_path( __FILE__ ) . 'class-sendgrid-nlvx.php';
5
+ require_once plugin_dir_path( __FILE__ ) . 'class-sendgrid-virtual-pages.php';
6
+
7
+ class Sendgrid_OptIn_API_Endpoint{
8
+ /**
9
+ * Hook WordPress
10
+ *
11
+ * @return void
12
+ */
13
+ public function __construct(){
14
+ add_filter( 'query_vars', array( $this, 'add_query_vars' ), 0 );
15
+ add_action( 'parse_request', array( $this, 'sniff_requests' ), 0 );
16
+ }
17
+
18
+ /**
19
+ * Add public query vars
20
+ *
21
+ * @param array $vars List of current public query vars
22
+ * @return array $vars
23
+ */
24
+ public function add_query_vars( $vars ){
25
+ $vars[] = '__sg_api';
26
+ $vars[] = 'token';
27
+
28
+ return $vars;
29
+ }
30
+
31
+ /**
32
+ * Sniff Requests
33
+ * This is where we hijack all API requests
34
+ *
35
+ * @return die if API request
36
+ */
37
+ public function sniff_requests(){
38
+ global $wp;
39
+
40
+ if( isset( $wp->query_vars['__sg_api'] ) )
41
+ {
42
+ $this->handle_request();
43
+ exit;
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Handle Requests
49
+ * This is where compute the email from the token and subscribe the user_error()
50
+ *
51
+ * @return void
52
+ */
53
+ protected function handle_request(){
54
+ global $wp;
55
+
56
+ $token = $wp->query_vars['token'];
57
+ if ( !$token )
58
+ {
59
+ wp_redirect( 'sg-subscription-missing-token' );
60
+
61
+ exit();
62
+ }
63
+
64
+ $transient = get_transient( $token );
65
+
66
+ if ( !$transient ||
67
+ !is_array( $transient ) ||
68
+ !isset( $transient['email'] ) ||
69
+ !isset( $transient['first_name'] ) ||
70
+ !isset( $transient['last_name'] ) )
71
+ {
72
+ wp_redirect( 'sg-subscription-invalid-token' );
73
+
74
+ exit();
75
+ }
76
+
77
+ $subscribed = Sendgrid_NLVX::create_and_add_recipient_to_list(
78
+ $transient['email'],
79
+ $transient['first_name'],
80
+ $transient['last_name'] );
81
+
82
+ if ( $subscribed )
83
+ {
84
+ set_transient( $token, null );
85
+ $page = Sendgrid_Tools::get_mc_signup_confirmation_page_url();
86
+ if ( $page == false ) {
87
+ wp_redirect( 'sg-subscription-success' );
88
+
89
+ exit();
90
+ }
91
+ else
92
+ {
93
+ wp_redirect( $page );
94
+
95
+ exit();
96
+ }
97
+
98
+ return;
99
+ }
100
+ else
101
+ {
102
+ wp_redirect( 'sg-error' );
103
+
104
+ exit();
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Send OptIn email
110
+ *
111
+ * @param string $email Email of subscribed user
112
+ * @param string $first_name First Name of subscribed user
113
+ * @param string $last_name Last Name of subscribed user
114
+ * @return bool
115
+ */
116
+ public static function send_confirmation_email( $email, $first_name = '', $last_name = '', $from_settings = false ) {
117
+ $subject = Sendgrid_Tools::get_mc_signup_email_subject();
118
+ $content = Sendgrid_Tools::get_mc_signup_email_content();
119
+
120
+ if ( false == $subject or false == $content ) {
121
+ return false;
122
+ }
123
+
124
+ $subject = stripslashes( $subject );
125
+ $content = stripslashes( $content );
126
+ $to = array( $email );
127
+
128
+ $token = Sendgrid_OptIn_API_Endpoint::generate_email_token( $email, $first_name, $last_name );
129
+
130
+ $transient = get_transient($token);
131
+
132
+ if ( $transient and isset( $transient['email'] ) and ! $from_settings ) {
133
+ return false;
134
+ }
135
+
136
+ if( false == set_transient( $token,
137
+ array(
138
+ 'email' => $email,
139
+ 'first_name' => $first_name,
140
+ 'last_name' => $last_name ),
141
+ 24 * 60 * 60 ) and ! $from_settings and $transient ) {
142
+ return false;
143
+ }
144
+
145
+ $confirmation_link = site_url() . '?__sg_api=1&token=' . $token;
146
+ $headers = new SendGrid\Email();
147
+ $headers->addSubstitution( '%confirmation_link%', array( $confirmation_link ) )
148
+ ->addCategory( 'wp_sendgrid_subscription_widget' );
149
+
150
+ add_filter( 'wp_mail_content_type', 'set_html_content_type' );
151
+ $result = wp_mail( $to, $subject, $content, $headers );
152
+ remove_filter( 'wp_mail_content_type', 'set_html_content_type' );
153
+
154
+ return $result;
155
+ }
156
+
157
+ /**
158
+ * Generates a hash from an email address using sha1
159
+ *
160
+ * @return string hash from email address
161
+ */
162
+ private static function generate_email_token( $email ){
163
+ return hash( "sha1", $email );
164
+ }
165
+ }
166
+
167
+ // Initialize OptIn Endopint
168
+ new Sendgrid_OptIn_API_Endpoint();
169
+
170
+ add_action( 'init', 'sg_create_subscribe_general_error_page' );
171
+ add_action( 'init', 'sg_create_subscribe_missing_token_error_page' );
172
+ add_action( 'init', 'sg_create_subscribe_invalid_token_error_page' );
173
+ add_action( 'init', 'sg_create_subscribe_success_page' );
lib/class-sendgrid-nlvx-widget.php ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once plugin_dir_path( __FILE__ ) . 'class-sendgrid-tools.php';
4
+ require_once plugin_dir_path( __FILE__ ) . 'class-sendgrid-nlvx.php';
5
+
6
+ class SendGrid_NLVX_Widget extends WP_Widget {
7
+ const DEFAULT_TITLE = 'Newsletter Subscription';
8
+ const DEFAULT_MESSAGE = 'If you want to subscribe to our monthly newsletter, please submit the form below.';
9
+ const DEFAULT_ERROR_MESSAGE = 'An error occured when processing your details. Please try again.';
10
+ const DEFAULT_SUBSCRIBE_MESSAGE = 'An email has been sent to your address. Please check your inbox in order to confirm your subscription.';
11
+
12
+ /**
13
+ * Widget class constructor
14
+ *
15
+ * @return void
16
+ */
17
+ function __construct() {
18
+ parent::__construct(
19
+ 'sendgrid_nlvx_widget',
20
+ 'SendGrid Subscription Widget',
21
+ array(
22
+ 'description' => 'SendGrid Marketing Campaigns Subscription Widget'
23
+ )
24
+ );
25
+ }
26
+
27
+ /**
28
+ * Method called to render the back-end form (dashboard form)
29
+ *
30
+ * @param mixed $instance the widget instance
31
+ *
32
+ * @return void
33
+ */
34
+ public function form( $instance ) {
35
+ if ( isset( $instance['title'] ) ) {
36
+ $title = $instance['title'];
37
+ } else {
38
+ $title = self::DEFAULT_TITLE;
39
+ }
40
+
41
+ if ( isset( $instance['text'] ) ) {
42
+ $text = $instance['text'];
43
+ } else {
44
+ $text = self::DEFAULT_MESSAGE;
45
+ }
46
+
47
+ if ( isset( $instance['error_text'] ) ) {
48
+ $error_text = $instance['error_text'];
49
+ } else {
50
+ $error_text = self::DEFAULT_ERROR_MESSAGE;
51
+ }
52
+
53
+ if ( isset( $instance['success_text'] ) ) {
54
+ $success_text = $instance['success_text'];
55
+ } else {
56
+ $success_text = self::DEFAULT_SUBSCRIBE_MESSAGE;
57
+ }
58
+
59
+ // Widget title input
60
+ echo '<p>';
61
+ echo '<label for="' . $this->get_field_id( 'title' ) . '">' . _e( 'Title:' ) . '</label>';
62
+ echo '<input class="widefat" id="'. $this->get_field_id( 'title' ) . '" name="' . $this->get_field_name( 'title' ) . ' type="text" value="' . esc_attr( $title ) . '" />';
63
+ echo '</p>';
64
+
65
+ // Widget text input
66
+ echo '<p>';
67
+ echo '<label for="' . $this->get_field_id( 'text' ) . '">' . _e( 'Message to display before subscription form:' ) . '</label>';
68
+ echo '<input class="widefat" id="' . $this->get_field_id( 'text' ) . '" name="' . $this->get_field_name( 'text' ). '" type="text" value="' . esc_attr( $text ) . '" />';
69
+ echo '</p>';
70
+
71
+ // Widget error text input
72
+ echo '<p>';
73
+ echo '<label for="' . $this->get_field_id( 'error_text' ) . '">' . _e( 'Message to display for errors:' ) . '</label>';
74
+ echo '<input class="widefat" id="' . $this->get_field_id( 'error_text' ) . '" name="' . $this->get_field_name( 'error_text' ). '" type="text" value="' . esc_attr( $error_text ) . '" />';
75
+ echo '</p>';
76
+
77
+ // Widget success text input
78
+ echo '<p>';
79
+ echo '<label for="' . $this->get_field_id( 'success_text' ) . '">' . _e( 'Message to display for success:' ) . '</label>';
80
+ echo '<input class="widefat" id="' . $this->get_field_id( 'success_text' ) . '" name="' . $this->get_field_name( 'success_text' ). '" type="text" value="' . esc_attr( $success_text ) . '" />';
81
+ echo '</p>';
82
+ }
83
+
84
+ /**
85
+ * Method called to update the widget parameters in the back-end
86
+ *
87
+ * @param mixed $new_instance the new widget instance
88
+ * @param mixed $old_instance the old widget instance
89
+ *
90
+ * @return mixed the widget instace to save
91
+ */
92
+ public function update( $new_instance, $old_instance ) {
93
+ $instance = array();
94
+ $instance['title'] = ( ! empty( $new_instance['title'] ) ) ? strip_tags( $new_instance['title'] ) : '';
95
+ $instance['text'] = ( ! empty( $new_instance['text'] ) ) ? $new_instance['text'] : '';
96
+ $instance['error_text'] = ( ! empty( $new_instance['error_text'] ) ) ? $new_instance['error_text'] : '';
97
+ $instance['success_text'] = ( ! empty( $new_instance['success_text'] ) ) ? $new_instance['success_text'] : '';
98
+
99
+ return $instance;
100
+ }
101
+
102
+ /**
103
+ * Method called to render the front-end of the widget
104
+ *
105
+ * @param mixed $args wordpress provided arguments
106
+ * @param mixed $instance the widget instance
107
+ *
108
+ * @return void
109
+ */
110
+ public function widget( $args, $instance ) {
111
+ $title = self::DEFAULT_TITLE;
112
+ if ( isset( $instance['title'] ) ) {
113
+ $title = apply_filters( 'widget_title', $instance['title'] );
114
+ }
115
+
116
+ $text = self::DEFAULT_MESSAGE;
117
+ if ( isset( $instance['text'] ) ) {
118
+ $text = apply_filters( 'widget_text', $instance['text'] );
119
+ }
120
+
121
+ $error_text = self::DEFAULT_ERROR_MESSAGE;
122
+ if ( isset( $instance['error_text'] ) ) {
123
+ $error_text = apply_filters( 'widget_text', $instance['error_text'] );
124
+ }
125
+
126
+ $success_text = self::DEFAULT_SUBSCRIBE_MESSAGE;
127
+ if ( isset( $instance['success_text'] ) ) {
128
+ $success_text = apply_filters( 'widget_text', $instance['success_text'] );
129
+ }
130
+
131
+ // Theme style
132
+ echo $args['before_widget'];
133
+
134
+ if ( ! empty( $title ) ) {
135
+ echo $args['before_title'] . $title . $args['after_title'];
136
+ }
137
+
138
+ // Form was submitted
139
+ if ( isset( $_POST['sendgrid_mc_email'] ) ) {
140
+ if ( $this->process_subscription( $_POST ) ) {
141
+ echo '<p class="sendgrid_widget_text"> ' . $success_text . ' </p>';
142
+ } else {
143
+ echo '<p class="sendgrid_widget_text"> ' . $error_text . ' </p>';
144
+ $this->display_form();
145
+ }
146
+ } else {
147
+ // Display form
148
+ if ( ! empty( $text ) ) {
149
+ echo '<p class="sendgrid_widget_text">' . $text . '</p>';
150
+ }
151
+
152
+ $this->display_form();
153
+ }
154
+
155
+ // Theme style
156
+ echo $args['after_widget'];
157
+ }
158
+
159
+ /**
160
+ * Method that processes the subscription params
161
+ *
162
+ * @param mixed $params array of parameters from $_POST
163
+ *
164
+ * @return void
165
+ */
166
+ private function process_subscription( $params ) {
167
+ // Bad call
168
+ if ( ! isset( $_POST['sendgrid_mc_email'] ) or ! Sendgrid_Tools::is_valid_email( $_POST['sendgrid_mc_email'] ) ) {
169
+ return false;
170
+ }
171
+
172
+ if ( 'true' == Sendgrid_Tools::get_mc_opt_req_fname_lname() and 'true' == Sendgrid_Tools::get_mc_opt_incl_fname_lname() ) {
173
+ if ( ! isset( $_POST['sendgrid_mc_first_name'] ) or empty( $_POST['sendgrid_mc_first_name'] ) ) {
174
+ return false;
175
+ }
176
+ if ( ! isset( $_POST['sendgrid_mc_last_name'] ) or empty( $_POST['sendgrid_mc_last_name'] ) ) {
177
+ return false;
178
+ }
179
+ }
180
+
181
+ if ( isset( $_POST['sendgrid_mc_first_name'] ) and isset( $_POST['sendgrid_mc_last_name'] ) ) {
182
+ Sendgrid_OptIn_API_Endpoint::send_confirmation_email( $_POST['sendgrid_mc_email'], $_POST['sendgrid_mc_first_name'], $_POST['sendgrid_mc_last_name'] );
183
+ } else {
184
+ Sendgrid_OptIn_API_Endpoint::send_confirmation_email( $_POST['sendgrid_mc_email'] );
185
+ }
186
+
187
+ return true;
188
+ }
189
+
190
+ /**
191
+ * Method that displays the subscription form
192
+ *
193
+ * @return void
194
+ */
195
+ private function display_form() {
196
+ echo '<form method="post" id="sendgrid_mc_email_form" class="mc_email_form" action="#sendgrid_mc_email_subscribe">';
197
+
198
+ if ( 'true' == Sendgrid_Tools::get_mc_opt_incl_fname_lname() ) {
199
+ if ( 'true' == Sendgrid_Tools::get_mc_opt_req_fname_lname() ) {
200
+ echo '<div class="sendgrid-mc-field">';
201
+ echo '<label for="sendgrid_mc_first_name">First Name<sup>*</sup> : </label>';
202
+ echo '<input id="sendgrid_mc_first_name" name="sendgrid_mc_first_name" type="text" value="" required/>';
203
+ echo '</div>';
204
+ echo '<div class="sendgrid-mc-field">';
205
+ echo '<label for="sendgrid_mc_last_name">Last Name<sup>*</sup> : </label>';
206
+ echo '<input id="sendgrid_mc_last_name" name="sendgrid_mc_last_name" type="text" value="" required/>';
207
+ echo '</div>';
208
+ } else {
209
+ echo '<div class="sendgrid-mc-field">';
210
+ echo '<label for="sendgrid_mc_first_name">First Name : </label>';
211
+ echo '<input id="sendgrid_mc_first_name" name="sendgrid_mc_first_name" type="text" value=""/>';
212
+ echo '</div>';
213
+ echo '<div class="sendgrid-mc-field">';
214
+ echo '<label for="sendgrid_mc_last_name">Last Name : </label>';
215
+ echo '<input id="sendgrid_mc_last_name" name="sendgrid_mc_last_name" type="text" value=""/>';
216
+ echo '</div>';
217
+ }
218
+ }
219
+
220
+ echo '<div class="sendgrid-mc-field">';
221
+ echo '<label for="sendgrid_mc_email">Email<sup>*</sup> :&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </label>';
222
+ echo '<input id="sendgrid_mc_email" name="sendgrid_mc_email" type="email" value="" required/>';
223
+ echo '</div>';
224
+
225
+ echo '<div class="sendgrid-mc-button">';
226
+ echo '<input type="submit" id="sendgrid_mc_email_submit" value="Subscribe" />';
227
+ echo '</div>';
228
+ echo '</form>';
229
+ }
230
+ }
lib/class-sendgrid-nlvx.php ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once plugin_dir_path( __FILE__ ) . 'class-sendgrid-tools.php';
4
+
5
+ class Sendgrid_NLVX
6
+ {
7
+ const NLVX_API_URL = 'https://api.sendgrid.com/v3/contactdb';
8
+
9
+ /**
10
+ * Returns the appropriate header value of authorization depending on the available credentials.
11
+ *
12
+ * @return mixed string of the header value if successful, false otherwise.
13
+ */
14
+ private static function get_auth_header_value()
15
+ {
16
+ if ( "false" == Sendgrid_Tools::get_mc_opt_use_transactional() ) {
17
+ $mc_api_key = Sendgrid_Tools::get_mc_api_key();
18
+
19
+ if ( false != $mc_api_key ) {
20
+ return 'Bearer ' . $mc_api_key;
21
+ }
22
+ }
23
+
24
+ $auth_method = Sendgrid_Tools::get_auth_method();
25
+
26
+ if ( 'credentials' == $auth_method ) {
27
+ $creds = base64_encode( Sendgrid_Tools::get_username() . ':' . Sendgrid_Tools::get_password() );
28
+
29
+ return 'Basic ' . $creds;
30
+ } else {
31
+ $api_key = Sendgrid_Tools::get_api_key();
32
+ if ( false == $api_key ) {
33
+ return false;
34
+ }
35
+
36
+ return 'Bearer ' . $api_key;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Returns the contact lists from SendGrid
42
+ *
43
+ * @return mixed an array of lists if the request is successful, false otherwise.
44
+ */
45
+ public static function get_all_lists()
46
+ {
47
+ $auth = Sendgrid_NLVX::get_auth_header_value();
48
+
49
+ if ( false == $auth ) {
50
+ return false;
51
+ }
52
+
53
+ $args = array(
54
+ 'headers' => array(
55
+ 'Authorization' => $auth
56
+ ),
57
+ 'decompress' => false
58
+ );
59
+
60
+ $url = Sendgrid_NLVX::NLVX_API_URL . '/lists';
61
+
62
+ $response = wp_remote_get( $url, $args );
63
+
64
+ if ( ! is_array( $response ) or ! isset( $response['body'] ) ) {
65
+ return false;
66
+ }
67
+
68
+ $lists_response = json_decode($response['body'], true);
69
+ if ( isset( $lists_response['lists'] ) ) {
70
+ return $lists_response['lists'];
71
+ }
72
+
73
+ return false;
74
+ }
75
+
76
+ /**
77
+ * Adds a recipient in the SendGrid MC contact db
78
+ *
79
+ * @param string $email The email of the recipient
80
+ * @param string $first_name The first name of the recipient
81
+ * @param string $last_name The last name of the recipient
82
+ *
83
+ * @return mixed The recipient ID if successful, false otherwise.
84
+ */
85
+ public static function add_recipient($email, $first_name = '', $last_name = '')
86
+ {
87
+ $auth = Sendgrid_NLVX::get_auth_header_value();
88
+
89
+ if ( false == $auth ) {
90
+ return false;
91
+ }
92
+
93
+ $args = array(
94
+ 'headers' => array(
95
+ 'Authorization' => $auth
96
+ ),
97
+ 'decompress' => false
98
+ );
99
+
100
+ $url = Sendgrid_NLVX::NLVX_API_URL . '/recipients';
101
+
102
+ $contact = array('email' => $email);
103
+
104
+ if ( '' != $first_name ) {
105
+ $contact['first_name'] = $first_name;
106
+ }
107
+
108
+ if ( '' != $last_name ) {
109
+ $contact['last_name'] = $last_name;
110
+ }
111
+
112
+ $req_body = json_encode(array($contact));
113
+ $args['body'] = $req_body;
114
+
115
+ $response = wp_remote_post( $url, $args );
116
+
117
+ if ( ! is_array( $response ) or ! isset( $response['body'] ) ) {
118
+ return false;
119
+ }
120
+
121
+ $recipient_response = json_decode($response['body'], true);
122
+ if ( isset( $recipient_response['error_count'] ) and 0 != $recipient_response['error_count'] ) {
123
+ return false;
124
+ }
125
+
126
+ if ( ! isset( $recipient_response['persisted_recipients'] ) or ! isset( $recipient_response['persisted_recipients'][0] ) ) {
127
+ return false;
128
+ }
129
+
130
+ return $recipient_response['persisted_recipients'][0];
131
+ }
132
+
133
+ /**
134
+ * Adds a recipient in the specified list
135
+ *
136
+ * @param string $recipient_id the ID of the recipient.
137
+ * @param string $list_id the ID of the list.
138
+ *
139
+ * @return bool True if successful, false otherwise.
140
+ */
141
+ public static function add_recipient_to_list($recipient_id, $list_id)
142
+ {
143
+ $auth = Sendgrid_NLVX::get_auth_header_value();
144
+
145
+ if ( false == $auth ) {
146
+ return false;
147
+ }
148
+
149
+ $args = array(
150
+ 'headers' => array(
151
+ 'Authorization' => $auth
152
+ ),
153
+ 'decompress' => false
154
+ );
155
+
156
+ $url = Sendgrid_NLVX::NLVX_API_URL . '/lists/'. $list_id . '/recipients/' . $recipient_id;
157
+
158
+ $response = wp_remote_post( $url, $args );
159
+
160
+ if ( ! is_array( $response ) or ! isset( $response['body'] ) ) {
161
+ return false;
162
+ }
163
+
164
+ if ( isset( $response['response']['code'] ) && 201 == $response['response']['code'] ) {
165
+ return true;
166
+ }
167
+
168
+ return false;
169
+ }
170
+
171
+ /**
172
+ * Adds a recipient in the SendGrid MC contact db and adds it to the list
173
+ *
174
+ * @param string $email The email of the recipient
175
+ * @param string $first_name The first name of the recipient
176
+ * @param string $last_name The last name of the recipient
177
+ *
178
+ * @return bool True if successful, false otherwise.
179
+ */
180
+ public static function create_and_add_recipient_to_list($email, $first_name = '', $last_name = '')
181
+ {
182
+ $list_id = Sendgrid_Tools::get_mc_list_id();
183
+ if ( false == $list_id ) {
184
+ return false;
185
+ }
186
+
187
+ $recipient_id = Sendgrid_NLVX::add_recipient($email, $first_name, $last_name);
188
+ if ( false == $recipient_id ) {
189
+ return false;
190
+ }
191
+
192
+ return Sendgrid_NLVX::add_recipient_to_list($recipient_id, $list_id);
193
+ }
194
+ }
lib/class-sendgrid-settings.php CHANGED
@@ -2,8 +2,21 @@
2
 
3
  require_once plugin_dir_path( __FILE__ ) . 'sendgrid/class-sendgrid-smtp.php';
4
  require_once plugin_dir_path( __FILE__ ) . 'class-sendgrid-tools.php';
 
 
 
5
 
6
  class Sendgrid_Settings {
 
 
 
 
 
 
 
 
 
 
7
  public function __construct( $plugin_directory )
8
  {
9
  // Add SendGrid settings page in the menu
@@ -21,6 +34,8 @@ class Sendgrid_Settings {
21
 
22
  /**
23
  * Add SendGrid settings page in the menu
 
 
24
  */
25
  public static function add_settings_menu() {
26
  add_options_page( __( 'SendGrid' ), __( 'SendGrid' ), 'manage_options', 'sendgrid-settings',
@@ -31,6 +46,7 @@ class Sendgrid_Settings {
31
  * Add SendGrid settings page in the plugin list
32
  *
33
  * @param mixed $links links
 
34
  * @return mixed links
35
  */
36
  public static function add_settings_link( $links )
@@ -47,6 +63,7 @@ class Sendgrid_Settings {
47
  * @param mixed $contextual_help contextual help
48
  * @param integer $screen_id screen id
49
  * @param integer $screen screen
 
50
  * @return string
51
  */
52
  public static function show_contextual_help( $contextual_help, $screen_id, $screen )
@@ -62,7 +79,7 @@ class Sendgrid_Settings {
62
  /**
63
  * Include css & javascripts we need for SendGrid settings page and widget
64
  *
65
- * @return void;
66
  */
67
  public static function add_headers( $hook )
68
  {
@@ -71,19 +88,23 @@ class Sendgrid_Settings {
71
  }
72
 
73
  wp_enqueue_style( 'sendgrid', plugin_dir_url( __FILE__ ) . '../view/css/sendgrid.css' );
 
74
 
75
- wp_enqueue_script( 'sendgrid', plugin_dir_url( __FILE__ ) . '../view/js/sendgrid.settings-v1.7.3.js', array('jquery') );
 
76
  }
77
 
78
  /**
79
  * Display SendGrid settings page content
 
 
80
  */
81
  public static function show_settings_page()
82
  {
83
  $response = null;
84
  $error_from_update = false;
85
 
86
- if ( 'POST' == $_SERVER['REQUEST_METHOD'] ) {
87
  $response = self::do_post( $_POST );
88
  if( isset( $response['status'] ) and $response['status'] == 'error' ) {
89
  $error_from_update = true;
@@ -107,11 +128,74 @@ class Sendgrid_Settings {
107
  $content_type = Sendgrid_Tools::get_content_type();
108
  $stats_categories = stripslashes( Sendgrid_Tools::get_stats_categories() );
109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  $allowed_send_methods = array( 'API' );
111
  if ( class_exists( 'Swift' ) ) {
112
  $allowed_send_methods[] = 'SMTP';
113
  }
114
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  if ( ! $error_from_update ) {
116
  if ( ! in_array( strtoupper( $send_method ), $allowed_send_methods ) ) {
117
  $message = 'Invalid send method configured in the config file, available methods are: ' . join( ", ", $allowed_send_methods );
@@ -122,14 +206,17 @@ class Sendgrid_Settings {
122
  if ( ! Sendgrid_Tools::check_api_key( $api_key, true ) ) {
123
  $message = 'API Key is invalid or without permissions.';
124
  $status = 'error';
125
- } else {
 
 
 
126
  $status = 'valid_auth';
127
  }
128
  } elseif ( 'credentials' == $auth_method and ! empty( $user ) and ! empty( $password ) ) {
129
  if ( ! Sendgrid_Tools::check_username_password( $user, $password, true ) ) {
130
  $message = 'Username and password are invalid.';
131
  $status = 'error';
132
- } else {
133
  $status = 'valid_auth';
134
  }
135
  }
@@ -138,7 +225,7 @@ class Sendgrid_Settings {
138
  $message = 'Template not found.';
139
  $status = 'error';
140
  }
141
-
142
  if ( ! in_array( $port, Sendgrid_Tools::$allowed_ports ) ) {
143
  $message = 'Invalid port configured in the config file, available ports are: ' . join( ",", Sendgrid_Tools::$allowed_ports );
144
  $status = 'error';
@@ -171,15 +258,23 @@ class Sendgrid_Settings {
171
  }
172
  }
173
 
174
- $is_env_auth_method = defined( 'SENDGRID_AUTH_METHOD' );
175
- $is_env_send_method = defined( 'SENDGRID_SEND_METHOD' );
176
- $is_env_username = defined( 'SENDGRID_USERNAME' );
177
- $is_env_password = defined( 'SENDGRID_PASSWORD' );
178
- $is_env_api_key = defined( 'SENDGRID_API_KEY' );
179
- $is_env_port = defined( 'SENDGRID_PORT' );
180
- $is_env_content_type = defined( 'SENDGRID_CONTENT_TYPE' );
181
-
182
- if ( $response && $status != 'error' ) {
 
 
 
 
 
 
 
 
183
  $message = $response['message'];
184
  $status = $response['status'];
185
  if( array_key_exists( 'error_type', $response ) ) {
@@ -190,43 +285,198 @@ class Sendgrid_Settings {
190
  require_once dirname( __FILE__ ) . '/../view/sendgrid_settings.php';
191
  }
192
 
 
 
 
 
 
 
 
193
  private static function do_post( $params ) {
 
 
 
 
194
  if ( isset($params['email_test'] ) and $params['email_test'] ) {
195
  return self::send_test_email( $params );
 
 
 
 
196
  }
197
-
198
- return self::save_settings( $params );
199
  }
200
 
201
- private static function save_settings( $params ) {
202
- if ( ! isset( $params['auth_method'] ) ) {
203
- $params['auth_method'] = Sendgrid_Tools::get_auth_method();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  }
205
 
206
- switch ( $params['auth_method'] ) {
207
- case 'apikey':
208
- if ( ! isset( $params['sendgrid_apikey'] ) or empty( $params['sendgrid_apikey'] ) ) {
209
- $response = array(
210
- 'message' => 'API Key is empty.',
211
- 'status' => 'error'
212
- );
 
213
 
214
- Sendgrid_Tools::set_api_key( '' );
 
215
 
216
- break;
217
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
 
219
- if ( ! Sendgrid_Tools::check_api_key( $params['sendgrid_apikey'], true ) ) {
 
 
 
 
 
220
  $response = array(
221
  'message' => 'API Key is invalid or without permissions.',
222
  'status' => 'error'
223
  );
224
-
225
- break;
226
  }
 
 
 
 
 
 
 
 
 
 
227
 
228
- Sendgrid_Tools::set_api_key( $params['sendgrid_apikey'] );
 
 
 
 
 
 
 
 
 
 
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  break;
231
 
232
  case 'credentials':
@@ -342,6 +592,13 @@ class Sendgrid_Settings {
342
  );
343
  }
344
 
 
 
 
 
 
 
 
345
  private static function send_test_email( $params ) {
346
  $to = $params['sendgrid_to'];
347
  if ( ! Sendgrid_Tools::is_valid_email( $to ) ) {
@@ -383,4 +640,65 @@ class Sendgrid_Settings {
383
  'error_type' => 'sending'
384
  );
385
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
386
  }
2
 
3
  require_once plugin_dir_path( __FILE__ ) . 'sendgrid/class-sendgrid-smtp.php';
4
  require_once plugin_dir_path( __FILE__ ) . 'class-sendgrid-tools.php';
5
+ require_once plugin_dir_path( __FILE__ ) . 'class-sendgrid-nlvx.php';
6
+ require_once plugin_dir_path( __FILE__ ) . 'class-sendgrid-mc-optin.php';
7
+ require_once plugin_dir_path( __FILE__ ) . 'class-sendgrid-nlvx-widget.php';
8
 
9
  class Sendgrid_Settings {
10
+ const DEFAULT_SIGNUP_EMAIL_SUBJECT = 'Confirm your subscription to ';
11
+ const DEFAULT_SIGNUP_EMAIL_CONTENT = '&lt;p&gt;Greetings!&lt;/p&gt;&#13;&#10;&#13;&#10;&lt;p&gt;Please click &lt;a href=&quot;%confirmation_link%&quot;&gt;here&lt;/a&gt; in order to subscribe to our newsletter!&lt;/p&gt;&#13;&#10;&#13;&#10;&lt;p&gt;Thank you,&lt;/p&gt;&#13;&#10;&lt;p&gt;';
12
+
13
+ /**
14
+ * Settings class constructor
15
+ *
16
+ * @param string $plugin_directory name of the plugin directory
17
+ *
18
+ * @return void
19
+ */
20
  public function __construct( $plugin_directory )
21
  {
22
  // Add SendGrid settings page in the menu
34
 
35
  /**
36
  * Add SendGrid settings page in the menu
37
+ *
38
+ * @return void
39
  */
40
  public static function add_settings_menu() {
41
  add_options_page( __( 'SendGrid' ), __( 'SendGrid' ), 'manage_options', 'sendgrid-settings',
46
  * Add SendGrid settings page in the plugin list
47
  *
48
  * @param mixed $links links
49
+ *
50
  * @return mixed links
51
  */
52
  public static function add_settings_link( $links )
63
  * @param mixed $contextual_help contextual help
64
  * @param integer $screen_id screen id
65
  * @param integer $screen screen
66
+ *
67
  * @return string
68
  */
69
  public static function show_contextual_help( $contextual_help, $screen_id, $screen )
79
  /**
80
  * Include css & javascripts we need for SendGrid settings page and widget
81
  *
82
+ * @return void
83
  */
84
  public static function add_headers( $hook )
85
  {
88
  }
89
 
90
  wp_enqueue_style( 'sendgrid', plugin_dir_url( __FILE__ ) . '../view/css/sendgrid.css' );
91
+ wp_enqueue_style( 'select2', plugin_dir_url( __FILE__ ) . '../view/css/select2.min.css' );
92
 
93
+ wp_enqueue_script( 'select2', plugin_dir_url( __FILE__ ) . '../view/js/select2.full.min.js', array('jquery') );
94
+ wp_enqueue_script( 'sendgrid', plugin_dir_url( __FILE__ ) . '../view/js/sendgrid.settings-v1.7.3.js', array('jquery', 'select2') );
95
  }
96
 
97
  /**
98
  * Display SendGrid settings page content
99
+ *
100
+ * @return void
101
  */
102
  public static function show_settings_page()
103
  {
104
  $response = null;
105
  $error_from_update = false;
106
 
107
+ if ( 'POST' == $_SERVER['REQUEST_METHOD'] and ! isset( $_POST['sg_dismiss_widget_notice'] ) ) {
108
  $response = self::do_post( $_POST );
109
  if( isset( $response['status'] ) and $response['status'] == 'error' ) {
110
  $error_from_update = true;
128
  $content_type = Sendgrid_Tools::get_content_type();
129
  $stats_categories = stripslashes( Sendgrid_Tools::get_stats_categories() );
130
 
131
+ $mc_api_key = Sendgrid_Tools::get_mc_api_key();
132
+ $mc_list_id = Sendgrid_Tools::get_mc_list_id();
133
+ $mc_opt_use_transactional = Sendgrid_Tools::get_mc_opt_use_transactional();
134
+ $mc_opt_incl_fname_lname = Sendgrid_Tools::get_mc_opt_incl_fname_lname();
135
+ $mc_opt_req_fname_lname = Sendgrid_Tools::get_mc_opt_req_fname_lname();
136
+ $mc_signup_confirmation_page = Sendgrid_Tools::get_mc_signup_confirmation_page();
137
+
138
+ $mc_signup_email_subject = Sendgrid_Tools::get_mc_signup_email_subject();
139
+ if ( false == $mc_signup_email_subject ) {
140
+ $mc_signup_email_subject = self::DEFAULT_SIGNUP_EMAIL_SUBJECT . get_bloginfo('name');
141
+ }
142
+
143
+ $mc_signup_email_content = Sendgrid_Tools::get_mc_signup_email_content();
144
+ if ( false == $mc_signup_email_content ) {
145
+ $mc_signup_email_content = self::DEFAULT_SIGNUP_EMAIL_CONTENT . get_bloginfo('name') . '&lt;/p&gt;';
146
+ }
147
+ $mc_signup_email_content = stripslashes( $mc_signup_email_content );
148
+
149
+ $confirmation_pages = get_pages( array( 'parent' => 0 ) );
150
+
151
+ $checked_use_transactional = '';
152
+ if ( 'true' == $mc_opt_use_transactional ) {
153
+ $checked_use_transactional = 'checked';
154
+ }
155
+
156
+ $checked_incl_fname_lname = '';
157
+ if ( 'true' == $mc_opt_incl_fname_lname ) {
158
+ $checked_incl_fname_lname = 'checked';
159
+ }
160
+
161
+ $checked_req_fname_lname = '';
162
+ if ( 'true' == $mc_opt_req_fname_lname ) {
163
+ $checked_req_fname_lname = 'checked';
164
+ }
165
+
166
+ $contact_lists = Sendgrid_NLVX::get_all_lists();
167
+ $contact_list_id_is_valid = false;
168
+ if ( false != $contact_lists ) {
169
+ foreach ( $contact_lists as $key => $list ) {
170
+ if ( $mc_list_id == $list['id'] ) {
171
+ $contact_list_id_is_valid = true;
172
+ break;
173
+ }
174
+ }
175
+ }
176
+
177
  $allowed_send_methods = array( 'API' );
178
  if ( class_exists( 'Swift' ) ) {
179
  $allowed_send_methods[] = 'SMTP';
180
  }
181
 
182
+ $is_mc_api_key_valid = true;
183
+ if ( 'true' == $mc_opt_use_transactional and 'apikey' == $auth_method and ! empty( $api_key ) ) {
184
+ if ( ! Sendgrid_Tools::check_api_key_mc( $api_key ) ) {
185
+ $is_mc_api_key_valid = false;
186
+ }
187
+ } else if ( 'true' != $mc_opt_use_transactional ) {
188
+ if ( ! Sendgrid_Tools::check_api_key_mc( $mc_api_key ) ) {
189
+ $is_mc_api_key_valid = false;
190
+ }
191
+ }
192
+
193
+ if ( $is_mc_api_key_valid ) {
194
+ Sendgrid_Tools::set_mc_auth_valid( 'true' );
195
+ } else {
196
+ Sendgrid_Tools::set_mc_auth_valid( 'false' );
197
+ }
198
+
199
  if ( ! $error_from_update ) {
200
  if ( ! in_array( strtoupper( $send_method ), $allowed_send_methods ) ) {
201
  $message = 'Invalid send method configured in the config file, available methods are: ' . join( ", ", $allowed_send_methods );
206
  if ( ! Sendgrid_Tools::check_api_key( $api_key, true ) ) {
207
  $message = 'API Key is invalid or without permissions.';
208
  $status = 'error';
209
+ } elseif ( 'true' == $mc_opt_use_transactional and ! $is_mc_api_key_valid ) {
210
+ $message = 'The configured API Key for subscription widget is invalid, empty or without permissions.';
211
+ $status = 'error';
212
+ } elseif ( 'error' != $status ) {
213
  $status = 'valid_auth';
214
  }
215
  } elseif ( 'credentials' == $auth_method and ! empty( $user ) and ! empty( $password ) ) {
216
  if ( ! Sendgrid_Tools::check_username_password( $user, $password, true ) ) {
217
  $message = 'Username and password are invalid.';
218
  $status = 'error';
219
+ } elseif ( 'error' != $status ) {
220
  $status = 'valid_auth';
221
  }
222
  }
225
  $message = 'Template not found.';
226
  $status = 'error';
227
  }
228
+
229
  if ( ! in_array( $port, Sendgrid_Tools::$allowed_ports ) ) {
230
  $message = 'Invalid port configured in the config file, available ports are: ' . join( ",", Sendgrid_Tools::$allowed_ports );
231
  $status = 'error';
258
  }
259
  }
260
 
261
+ $is_env_auth_method = defined( 'SENDGRID_AUTH_METHOD' );
262
+ $is_env_send_method = defined( 'SENDGRID_SEND_METHOD' );
263
+ $is_env_username = defined( 'SENDGRID_USERNAME' );
264
+ $is_env_password = defined( 'SENDGRID_PASSWORD' );
265
+ $is_env_api_key = defined( 'SENDGRID_API_KEY' );
266
+ $is_env_port = defined( 'SENDGRID_PORT' );
267
+ $is_env_content_type = defined( 'SENDGRID_CONTENT_TYPE' );
268
+ $is_env_mc_api_key = defined( 'SENDGRID_MC_API_KEY' );
269
+ $is_env_mc_list_id = defined( 'SENDGRID_MC_LIST_ID' );
270
+ $is_env_mc_opt_use_transactional = defined( 'SENDGRID_MC_OPT_USE_TRANSACTIONAL' );
271
+ $is_env_mc_opt_incl_fname_lname = defined( 'SENDGRID_MC_OPT_INCL_FNAME_LNAME' );
272
+ $is_env_mc_opt_req_fname_lname = defined( 'SENDGRID_MC_OPT_REQ_FNAME_LNAME' );
273
+ $is_env_mc_signup_email_subject = defined( 'SENDGRID_MC_SIGNUP_EMAIL_SUBJECT' );
274
+ $is_env_mc_signup_email_content = defined( 'SENDGRID_MC_SIGNUP_EMAIL_CONTENT' );
275
+ $is_env_mc_signup_confirmation_page = defined( 'SENDGRID_MC_SIGNUP_CONFIRMATION_PAGE' );
276
+
277
+ if ( $response and $status != 'error' ) {
278
  $message = $response['message'];
279
  $status = $response['status'];
280
  if( array_key_exists( 'error_type', $response ) ) {
285
  require_once dirname( __FILE__ ) . '/../view/sendgrid_settings.php';
286
  }
287
 
288
+ /**
289
+ * Routes processing of request parameters depending on the source section of the settings page
290
+ *
291
+ * @param mixed $params array of parameters from $_POST
292
+ *
293
+ * @return mixed response array from the save or send functions
294
+ */
295
  private static function do_post( $params ) {
296
+ if ( isset($params['mc_settings'] ) and $params['mc_settings'] ) {
297
+ return self::save_mc_settings( $params );
298
+ }
299
+
300
  if ( isset($params['email_test'] ) and $params['email_test'] ) {
301
  return self::send_test_email( $params );
302
+ }
303
+
304
+ if ( isset($params['contact_upload_test'] ) and $params['contact_upload_test'] ) {
305
+ return self::send_contact_upload_test( $params );
306
  }
307
+
308
+ return self::save_general_settings( $params );
309
  }
310
 
311
+ /**
312
+ * Saves the Marketing Campaigns parameters sent from the settings page
313
+ *
314
+ * @param mixed $params array of parameters from $_POST
315
+ *
316
+ * @return mixed response array with message and status
317
+ */
318
+ private static function save_mc_settings( $params ) {
319
+ // Use Transactional Option
320
+ $use_transactional_key = false;
321
+
322
+ if ( ! defined( 'SENDGRID_MC_OPT_USE_TRANSACTIONAL' ) ) {
323
+ if ( isset( $params['sendgrid_mc_use_transactional'] ) ) {
324
+ $use_transactional_key = true;
325
+ Sendgrid_Tools::set_mc_opt_use_transactional( 'true' );
326
+ } else {
327
+ Sendgrid_Tools::set_mc_opt_use_transactional( 'false' );
328
+ }
329
+ } else {
330
+ $use_transactional_key = ( 'true' == SENDGRID_MC_OPT_USE_TRANSACTIONAL ? true : false );
331
  }
332
 
333
+ // If Use Transactional Is Set and auth is not through credentials, check the API key for MC scopes.
334
+ if ( $use_transactional_key and 'apikey' == Sendgrid_Tools::get_auth_method() ) {
335
+ $apikey = Sendgrid_Tools::get_api_key();
336
+ if( false == $apikey or empty( $apikey ) ) {
337
+ $response = array(
338
+ 'message' => 'API Key is empty.',
339
+ 'status' => 'error'
340
+ );
341
 
342
+ return $response;
343
+ }
344
 
345
+ if ( ! Sendgrid_Tools::check_api_key_mc( $apikey ) ) {
346
+ $response = array(
347
+ 'message' => 'API Key is invalid or without permissions.',
348
+ 'status' => 'error'
349
+ );
350
+
351
+ return $response;
352
+ }
353
+ }
354
+
355
+ if ( false == $use_transactional_key and ! defined( 'SENDGRID_MC_API_KEY' ) ) {
356
+ // MC API Key was set empty on purpose
357
+ if ( ! isset( $params['sendgrid_mc_apikey'] ) or empty( $params['sendgrid_mc_apikey'] ) ) {
358
+ $response = array(
359
+ 'message' => 'API Key is empty.',
360
+ 'status' => 'error'
361
+ );
362
 
363
+ Sendgrid_Tools::set_mc_api_key( '' );
364
+ } else {
365
+ // MC API Key was set, check scopes and save if correct
366
+ $apikey = $params['sendgrid_mc_apikey'];
367
+
368
+ if ( ! Sendgrid_Tools::check_api_key_mc( $apikey ) ) {
369
  $response = array(
370
  'message' => 'API Key is invalid or without permissions.',
371
  'status' => 'error'
372
  );
373
+ } else {
374
+ Sendgrid_Tools::set_mc_api_key( $apikey );
375
  }
376
+ }
377
+ }
378
+
379
+ if ( ! defined( 'SENDGRID_MC_OPT_INCL_FNAME_LNAME' ) ) {
380
+ if ( isset( $params['sendgrid_mc_incl_fname_lname'] ) ) {
381
+ Sendgrid_Tools::set_mc_opt_incl_fname_lname( 'true' );
382
+ } else {
383
+ Sendgrid_Tools::set_mc_opt_incl_fname_lname( 'false' );
384
+ }
385
+ }
386
 
387
+ if ( ! defined( 'SENDGRID_MC_OPT_REQ_FNAME_LNAME' ) ) {
388
+ if ( isset( $params['sendgrid_mc_req_fname_lname'] ) ) {
389
+ Sendgrid_Tools::set_mc_opt_req_fname_lname( 'true' );
390
+ } else {
391
+ Sendgrid_Tools::set_mc_opt_req_fname_lname( 'false' );
392
+ }
393
+ }
394
+
395
+ if ( isset( $params['sendgrid_mc_contact_list'] ) and ! defined( 'SENDGRID_MC_LIST_ID' ) ) {
396
+ Sendgrid_Tools::set_mc_list_id( $params['sendgrid_mc_contact_list'] );
397
+ }
398
 
399
+ if ( ! defined( 'SENDGRID_MC_SIGNUP_EMAIL_SUBJECT' ) ) {
400
+ if ( ! isset( $params['sendgrid_mc_email_subject'] ) or empty( $params['sendgrid_mc_email_subject'] ) ) {
401
+ $response = array(
402
+ 'message' => 'Signup email subject cannot be empty.',
403
+ 'status' => 'error'
404
+ );
405
+ } else {
406
+ Sendgrid_Tools::set_mc_signup_email_subject( $params['sendgrid_mc_email_subject'] );
407
+ }
408
+ }
409
+
410
+ if ( ! defined( 'SENDGRID_MC_SIGNUP_EMAIL_CONTENT' ) ) {
411
+ if ( ! isset( $params['sendgrid_mc_email_content'] ) or empty( $params['sendgrid_mc_email_content'] ) ) {
412
+ $response = array(
413
+ 'message' => 'Signup email content cannot be empty.',
414
+ 'status' => 'error'
415
+ );
416
+ } else {
417
+ Sendgrid_Tools::set_mc_signup_email_content( $params['sendgrid_mc_email_content'] );
418
+ }
419
+ }
420
+
421
+ if ( isset( $params['sendgrid_mc_signup_page'] ) and ! defined( 'SENDGRID_MC_SIGNUP_CONFIRMATION_PAGE' ) ) {
422
+ Sendgrid_Tools::set_mc_signup_confirmation_page( $params['sendgrid_mc_signup_page'] );
423
+ }
424
+
425
+ if ( isset( $response ) and $response['status'] == 'error' ) {
426
+ return $response;
427
+ }
428
+
429
+ return array(
430
+ 'message' => 'Options are saved.',
431
+ 'status' => 'updated'
432
+ );
433
+ }
434
+
435
+ /**
436
+ * Saves the General Settings parameters sent from the settings page
437
+ *
438
+ * @param mixed $params array of parameters from $_POST
439
+ *
440
+ * @return mixed response array with message and status
441
+ */
442
+ private static function save_general_settings( $params ) {
443
+ if ( ! isset( $params['auth_method'] ) ) {
444
+ $params['auth_method'] = Sendgrid_Tools::get_auth_method();
445
+ }
446
+
447
+ switch ( $params['auth_method'] ) {
448
+ case 'apikey':
449
+ if ( ! defined( 'SENDGRID_API_KEY' ) ) {
450
+ if ( ! isset( $params['sendgrid_apikey'] ) or empty( $params['sendgrid_apikey'] ) ) {
451
+ $response = array(
452
+ 'message' => 'API Key is empty.',
453
+ 'status' => 'error'
454
+ );
455
+
456
+ Sendgrid_Tools::set_api_key( '' );
457
+
458
+ break;
459
+ }
460
+
461
+ if ( ! Sendgrid_Tools::check_api_key( $params['sendgrid_apikey'], true ) ) {
462
+ $response = array(
463
+ 'message' => 'API Key is invalid or without permissions.',
464
+ 'status' => 'error'
465
+ );
466
+
467
+ break;
468
+ }
469
+
470
+ if ( 'true' == Sendgrid_Tools::get_mc_opt_use_transactional() and ! Sendgrid_Tools::check_api_key_mc( $params['sendgrid_apikey'] ) ) {
471
+ $response = array(
472
+ 'message' => 'This API key is also used for the Subscription Widget but does not have Marketing Campaigns permissions.',
473
+ 'status' => 'error'
474
+ );
475
+ }
476
+
477
+ Sendgrid_Tools::set_api_key( $params['sendgrid_apikey'] );
478
+ }
479
+
480
  break;
481
 
482
  case 'credentials':
592
  );
593
  }
594
 
595
+ /**
596
+ * Sends a test email using the parameters specified in the settings page
597
+ *
598
+ * @param mixed $params array of parameters from $_POST
599
+ *
600
+ * @return mixed response array with message and status
601
+ */
602
  private static function send_test_email( $params ) {
603
  $to = $params['sendgrid_to'];
604
  if ( ! Sendgrid_Tools::is_valid_email( $to ) ) {
640
  'error_type' => 'sending'
641
  );
642
  }
643
+
644
+ /**
645
+ * Uploads a contact using the parameters specified in the settings page
646
+ *
647
+ * @param mixed $params array of parameters from $_POST
648
+ *
649
+ * @return mixed response array with message and status
650
+ */
651
+ private static function send_contact_upload_test( $params ) {
652
+ $email = $params['sendgrid_test_email'];
653
+ if ( ! Sendgrid_Tools::is_valid_email( $email ) ) {
654
+ return array(
655
+ 'message' => 'Email address provided is invalid.',
656
+ 'status' => 'error',
657
+ 'error_type' => 'upload'
658
+ );
659
+ }
660
+
661
+ switch ( Sendgrid_Tools::get_auth_method() ) {
662
+ case 'apikey':
663
+ $apikey = Sendgrid_Tools::get_api_key();
664
+ if ( ! Sendgrid_Tools::check_api_key( $apikey, true ) ) {
665
+ return array(
666
+ 'message' => 'API Key used for mail send is invalid or without permissions.',
667
+ 'status' => 'error',
668
+ 'error_type' => 'upload'
669
+ );
670
+ }
671
+ break;
672
+ case 'credentials':
673
+ $username = Sendgrid_Tools::get_username();
674
+ $password = Sendgrid_Tools::get_password();
675
+ if ( ! Sendgrid_Tools::check_username_password( $params['sendgrid_username'], $params['sendgrid_password'], true ) ) {
676
+ return array(
677
+ 'message' => 'Credentials used for mail send are invalid.',
678
+ 'status' => 'error',
679
+ 'error_type' => 'upload'
680
+ );
681
+ }
682
+ break;
683
+ default:
684
+ return array(
685
+ 'message' => 'An error occured when trying to check your transactional credentials. Please check that they are correct on the General Settings tab.',
686
+ 'status' => 'error',
687
+ 'error_type' => 'upload'
688
+ );
689
+ }
690
+
691
+ if ( false == Sendgrid_OptIn_API_Endpoint::send_confirmation_email( $email, '', '', true ) ) {
692
+ return array(
693
+ 'message' => 'An error occured when trying send the subscription email. Please make sure you have configured all settings properly.',
694
+ 'status' => 'error',
695
+ 'error_type' => 'upload'
696
+ );
697
+ }
698
+
699
+ return array(
700
+ 'message' => 'Subscription confirmation email was sent.',
701
+ 'status' => 'updated'
702
+ );
703
+ }
704
  }
lib/class-sendgrid-statistics.php CHANGED
@@ -114,7 +114,7 @@ class Sendgrid_Statistics
114
  wp_enqueue_script( 'jquery-flot-time', plugin_dir_url( __FILE__ ) . '../view/js/jquery.flot.time.js', array('jquery') );
115
  wp_enqueue_script( 'jquery-flot-tofflelegend', plugin_dir_url( __FILE__ ) . '../view/js/jquery.flot.togglelegend.js', array('jquery') );
116
  wp_enqueue_script( 'jquery-flot-symbol', plugin_dir_url( __FILE__ ) . '../view/js/jquery.flot.symbol.js', array('jquery') );
117
- wp_enqueue_script('jquery-ui-datepicker', plugin_dir_url( __FILE__ ) . '../view/js/jquery.ui.datepicker.js', array('jquery', 'jquery-ui-core') );
118
 
119
  // CSS
120
  wp_enqueue_style( 'jquery-ui-datepicker', plugin_dir_url( __FILE__ ) . '../view/css/datepicker/smoothness/jquery-ui-1.10.3.custom.css' );
114
  wp_enqueue_script( 'jquery-flot-time', plugin_dir_url( __FILE__ ) . '../view/js/jquery.flot.time.js', array('jquery') );
115
  wp_enqueue_script( 'jquery-flot-tofflelegend', plugin_dir_url( __FILE__ ) . '../view/js/jquery.flot.togglelegend.js', array('jquery') );
116
  wp_enqueue_script( 'jquery-flot-symbol', plugin_dir_url( __FILE__ ) . '../view/js/jquery.flot.symbol.js', array('jquery') );
117
+ wp_enqueue_script( 'jquery-ui-datepicker', plugin_dir_url( __FILE__ ) . '../view/js/jquery.ui.datepicker.js', array('jquery', 'jquery-ui-core') );
118
 
119
  // CSS
120
  wp_enqueue_style( 'jquery-ui-datepicker', plugin_dir_url( __FILE__ ) . '../view/css/datepicker/smoothness/jquery-ui-1.10.3.custom.css' );
lib/class-sendgrid-tools.php CHANGED
@@ -18,6 +18,7 @@ class Sendgrid_Tools
18
  *
19
  * @param string $username sendgrid username
20
  * @param string $password sendgrid password
 
21
  * @return bool
22
  */
23
  public static function check_username_password( $username, $password, $clear_cache = false )
@@ -40,7 +41,7 @@ class Sendgrid_Tools
40
  $url = 'https://api.sendgrid.com/api/profile.get.json?';
41
  $url .= "api_user=" . urlencode($username) . "&api_key=" . urlencode($password);
42
 
43
- $response = wp_remote_get( $url );
44
 
45
  if ( ! is_array( $response ) or ! isset( $response['body'] ) )
46
  {
@@ -67,6 +68,7 @@ class Sendgrid_Tools
67
  * Check apikey scopes
68
  *
69
  * @param string $apikey sendgrid apikey
 
70
  * @return bool
71
  */
72
  public static function check_api_key_scopes( $apikey, $scopes )
@@ -79,7 +81,8 @@ class Sendgrid_Tools
79
 
80
  $args = array(
81
  'headers' => array(
82
- 'Authorization' => 'Bearer ' . $apikey )
 
83
  );
84
 
85
  $response = wp_remote_get( $url, $args );
@@ -113,6 +116,7 @@ class Sendgrid_Tools
113
  * Check apikey
114
  *
115
  * @param string $apikey sendgrid apikey
 
116
  * @return bool
117
  */
118
  public static function check_api_key( $apikey, $clear_cache = false )
@@ -147,6 +151,7 @@ class Sendgrid_Tools
147
  * Check template
148
  *
149
  * @param string $template sendgrid template
 
150
  * @return bool
151
  */
152
  public static function check_template( $template )
@@ -179,9 +184,10 @@ class Sendgrid_Tools
179
  /**
180
  * Make request to SendGrid API
181
  *
182
- * @param type $api
183
- * @param type $parameters
184
- * @return json
 
185
  */
186
  public static function do_request( $api = 'v3/stats', $parameters = array() )
187
  {
@@ -192,14 +198,16 @@ class Sendgrid_Tools
192
  $args = array(
193
  'headers' => array(
194
  'Authorization' => 'Basic ' . $creds
195
- )
 
196
  );
197
 
198
  } else {
199
  $args = array(
200
  'headers' => array(
201
  'Authorization' => 'Bearer ' . $parameters['apikey']
202
- )
 
203
  );
204
  }
205
 
@@ -223,7 +231,7 @@ class Sendgrid_Tools
223
  /**
224
  * Return username from the database or global variable
225
  *
226
- * @return string username
227
  */
228
  public static function get_username()
229
  {
@@ -242,8 +250,10 @@ class Sendgrid_Tools
242
 
243
  /**
244
  * Sets username in the database
245
- * @param type string $username
246
- * @return bool
 
 
247
  */
248
  public static function set_username( $username )
249
  {
@@ -257,7 +267,7 @@ class Sendgrid_Tools
257
  /**
258
  * Return password from the database or global variable
259
  *
260
- * @return string password
261
  */
262
  public static function get_password()
263
  {
@@ -278,8 +288,10 @@ class Sendgrid_Tools
278
 
279
  /**
280
  * Sets password in the database
281
- * @param type string $password
282
- * @return bool
 
 
283
  */
284
  public static function set_password( $password )
285
  {
@@ -289,7 +301,7 @@ class Sendgrid_Tools
289
  /**
290
  * Return api_key from the database or global variable
291
  *
292
- * @return string api key
293
  */
294
  public static function get_api_key()
295
  {
@@ -308,20 +320,296 @@ class Sendgrid_Tools
308
  }
309
  }
310
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
  /**
312
  * Sets api_key in the database
313
- * @param type string $apikey
314
- * @return bool
 
 
315
  */
316
  public static function set_api_key( $apikey )
317
  {
318
  return update_option( 'sendgrid_api_key', $apikey );
319
  }
320
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  /**
322
  * Return send method from the database or global variable
323
  *
324
- * @return string send_method
325
  */
326
  public static function get_send_method()
327
  {
@@ -337,7 +625,7 @@ class Sendgrid_Tools
337
  /**
338
  * Return auth method from the database or global variable
339
  *
340
- * @return string auth_method
341
  */
342
  public static function get_auth_method()
343
  {
@@ -363,7 +651,7 @@ class Sendgrid_Tools
363
  /**
364
  * Return port from the database or global variable
365
  *
366
- * @return string port
367
  */
368
  public static function get_port()
369
  {
@@ -377,7 +665,7 @@ class Sendgrid_Tools
377
  /**
378
  * Return from name from the database or global variable
379
  *
380
- * @return string from_name
381
  */
382
  public static function get_from_name()
383
  {
@@ -391,7 +679,7 @@ class Sendgrid_Tools
391
  /**
392
  * Return from email address from the database or global variable
393
  *
394
- * @return string from_email
395
  */
396
  public static function get_from_email()
397
  {
@@ -405,7 +693,7 @@ class Sendgrid_Tools
405
  /**
406
  * Return reply to email address from the database or global variable
407
  *
408
- * @return string reply_to
409
  */
410
  public static function get_reply_to()
411
  {
@@ -419,7 +707,7 @@ class Sendgrid_Tools
419
  /**
420
  * Return categories from the database or global variable
421
  *
422
- * @return string categories
423
  */
424
  public static function get_categories()
425
  {
@@ -433,7 +721,7 @@ class Sendgrid_Tools
433
  /**
434
  * Return stats categories from the database or global variable
435
  *
436
- * @return string categories
437
  */
438
  public static function get_stats_categories()
439
  {
@@ -447,7 +735,7 @@ class Sendgrid_Tools
447
  /**
448
  * Return categories array
449
  *
450
- * @return array categories
451
  */
452
  public static function get_categories_array()
453
  {
@@ -461,7 +749,7 @@ class Sendgrid_Tools
461
  /**
462
  * Return template from the database or global variable
463
  *
464
- * @return string template
465
  */
466
  public static function get_template()
467
  {
@@ -475,7 +763,7 @@ class Sendgrid_Tools
475
  /**
476
  * Return content type from the database or global variable
477
  *
478
- * @return string content_type
479
  */
480
  public static function get_content_type()
481
  {
@@ -489,7 +777,7 @@ class Sendgrid_Tools
489
  /**
490
  * Returns decrypted string using the key or empty string in case of error
491
  *
492
- * @return string template
493
  */
494
  private static function decrypt( $encrypted_input_string, $key ) {
495
  if ( ! extension_loaded( 'mcrypt' ) ) {
@@ -524,6 +812,7 @@ class Sendgrid_Tools
524
  * Check apikey stats permissions
525
  *
526
  * @param string $apikey sendgrid apikey
 
527
  * @return bool
528
  */
529
  public static function check_api_key_stats( $apikey )
@@ -533,6 +822,20 @@ class Sendgrid_Tools
533
  return Sendgrid_Tools::check_api_key_scopes( $apikey, $required_scopes );
534
  }
535
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
536
  /**
537
  * Returns true if the email is valid, false otherwise
538
  *
@@ -547,6 +850,7 @@ class Sendgrid_Tools
547
  * Returns true if all the emails in the headers are valid, false otherwise
548
  *
549
  * @param mixed $headers string or array of headers
 
550
  * @return bool
551
  */
552
  public static function valid_emails_in_headers( $headers )
@@ -634,10 +938,45 @@ class Sendgrid_Tools
634
  /**
635
  * Returns the string content of the input with "<url>" replaced by "url"
636
  *
637
- * @return string
638
  */
639
  public static function remove_all_tag_urls( $content )
640
  {
641
  return preg_replace('/<(https?:\/\/[^>]*)>/im', '$1', $content);
642
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
643
  }
18
  *
19
  * @param string $username sendgrid username
20
  * @param string $password sendgrid password
21
+ *
22
  * @return bool
23
  */
24
  public static function check_username_password( $username, $password, $clear_cache = false )
41
  $url = 'https://api.sendgrid.com/api/profile.get.json?';
42
  $url .= "api_user=" . urlencode($username) . "&api_key=" . urlencode($password);
43
 
44
+ $response = wp_remote_get( $url, array( 'decompress' => false ) );
45
 
46
  if ( ! is_array( $response ) or ! isset( $response['body'] ) )
47
  {
68
  * Check apikey scopes
69
  *
70
  * @param string $apikey sendgrid apikey
71
+ *
72
  * @return bool
73
  */
74
  public static function check_api_key_scopes( $apikey, $scopes )
81
 
82
  $args = array(
83
  'headers' => array(
84
+ 'Authorization' => 'Bearer ' . $apikey ),
85
+ 'decompress' => false
86
  );
87
 
88
  $response = wp_remote_get( $url, $args );
116
  * Check apikey
117
  *
118
  * @param string $apikey sendgrid apikey
119
+ *
120
  * @return bool
121
  */
122
  public static function check_api_key( $apikey, $clear_cache = false )
151
  * Check template
152
  *
153
  * @param string $template sendgrid template
154
+ *
155
  * @return bool
156
  */
157
  public static function check_template( $template )
184
  /**
185
  * Make request to SendGrid API
186
  *
187
+ * @param type $api
188
+ * @param type $parameters
189
+ *
190
+ * @return json
191
  */
192
  public static function do_request( $api = 'v3/stats', $parameters = array() )
193
  {
198
  $args = array(
199
  'headers' => array(
200
  'Authorization' => 'Basic ' . $creds
201
+ ),
202
+ 'decompress' => false
203
  );
204
 
205
  } else {
206
  $args = array(
207
  'headers' => array(
208
  'Authorization' => 'Bearer ' . $parameters['apikey']
209
+ ),
210
+ 'decompress' => false
211
  );
212
  }
213
 
231
  /**
232
  * Return username from the database or global variable
233
  *
234
+ * @return mixed username, false if the value is not found
235
  */
236
  public static function get_username()
237
  {
250
 
251
  /**
252
  * Sets username in the database
253
+ *
254
+ * @param type string $username
255
+ *
256
+ * @return bool
257
  */
258
  public static function set_username( $username )
259
  {
267
  /**
268
  * Return password from the database or global variable
269
  *
270
+ * @return mixed password, false if the value is not found
271
  */
272
  public static function get_password()
273
  {
288
 
289
  /**
290
  * Sets password in the database
291
+ *
292
+ * @param type string $password
293
+ *
294
+ * @return bool
295
  */
296
  public static function set_password( $password )
297
  {
301
  /**
302
  * Return api_key from the database or global variable
303
  *
304
+ * @return mixed api key, false if the value is not found
305
  */
306
  public static function get_api_key()
307
  {
320
  }
321
  }
322
 
323
+ /**
324
+ * Return MC api_key from the database or global variable
325
+ *
326
+ * @return mixed api key, false if the value is not found
327
+ */
328
+ public static function get_mc_api_key()
329
+ {
330
+ if ( defined( 'SENDGRID_MC_API_KEY' ) ) {
331
+ return SENDGRID_MC_API_KEY;
332
+ } else {
333
+ return get_option( 'sendgrid_mc_api_key' );
334
+ }
335
+ }
336
+
337
+ /**
338
+ * Return list_id from the database or global variable
339
+ *
340
+ * @return mixed list id, false if the value is not found
341
+ */
342
+ public static function get_mc_list_id()
343
+ {
344
+ if ( defined( 'SENDGRID_MC_LIST_ID' ) ) {
345
+ return SENDGRID_MC_LIST_ID;
346
+ } else {
347
+ return get_option( 'sendgrid_mc_list_id' );
348
+ }
349
+ }
350
+
351
+ /**
352
+ * Return the value for the option to use the transactional credentials from the database or global variable
353
+ *
354
+ * @return mixed 'true' or 'false', false if the value is not found
355
+ */
356
+ public static function get_mc_opt_use_transactional()
357
+ {
358
+ if ( defined( 'SENDGRID_MC_OPT_USE_TRANSACTIONAL' ) ) {
359
+ return SENDGRID_MC_OPT_USE_TRANSACTIONAL;
360
+ } else {
361
+ return get_option( 'sendgrid_mc_opt_use_transactional' );
362
+ }
363
+ }
364
+
365
+ /**
366
+ * Return the value for the option to require first name and last name on subscribe from the database or global variable
367
+ *
368
+ * @return mixed 'true' or 'false', false if the value is not found
369
+ */
370
+ public static function get_mc_opt_req_fname_lname()
371
+ {
372
+ if ( defined( 'SENDGRID_MC_OPT_REQ_FNAME_LNAME' ) ) {
373
+ return SENDGRID_MC_OPT_REQ_FNAME_LNAME;
374
+ } else {
375
+ return get_option( 'sendgrid_mc_opt_req_fname_lname' );
376
+ }
377
+ }
378
+
379
+ /**
380
+ * Return the value for the option to include first name and last name on subscribe from the database or global variable
381
+ *
382
+ * @return mixed 'true' or 'false', false if the value is not found
383
+ */
384
+ public static function get_mc_opt_incl_fname_lname()
385
+ {
386
+ if ( defined( 'SENDGRID_MC_OPT_INCL_FNAME_LNAME' ) ) {
387
+ return SENDGRID_MC_OPT_INCL_FNAME_LNAME;
388
+ } else {
389
+ return get_option( 'sendgrid_mc_opt_incl_fname_lname' );
390
+ }
391
+ }
392
+
393
+ /**
394
+ * Return the value for the signup email subject from the database or global variable
395
+ *
396
+ * @return mixed signup email subject, false if the value is not found
397
+ */
398
+ public static function get_mc_signup_email_subject()
399
+ {
400
+ if ( defined( 'SENDGRID_MC_SIGNUP_EMAIL_SUBJECT' ) ) {
401
+ return SENDGRID_MC_SIGNUP_EMAIL_SUBJECT;
402
+ } else {
403
+ return get_option( 'sendgrid_mc_signup_email_subject' );
404
+ }
405
+ }
406
+
407
+ /**
408
+ * Return the value for the signup email contents from the database or global variable
409
+ *
410
+ * @return mixed signup email contents, false if the value is not found
411
+ */
412
+ public static function get_mc_signup_email_content()
413
+ {
414
+ if ( defined( 'SENDGRID_MC_SIGNUP_EMAIL_CONTENT' ) ) {
415
+ return SENDGRID_MC_SIGNUP_EMAIL_CONTENT;
416
+ } else {
417
+ return get_option( 'sendgrid_mc_signup_email_content' );
418
+ }
419
+ }
420
+
421
+ /**
422
+ * Return the value for the signup confirmation page from the database or global variable
423
+ *
424
+ * @return mixed signup confirmation page, false if the value is not found
425
+ */
426
+ public static function get_mc_signup_confirmation_page()
427
+ {
428
+ if ( defined( 'SENDGRID_MC_SIGNUP_CONFIRMATION_PAGE' ) ) {
429
+ return SENDGRID_MC_SIGNUP_CONFIRMATION_PAGE;
430
+ } else {
431
+ return get_option( 'sendgrid_mc_signup_confirmation_page' );
432
+ }
433
+ }
434
+
435
+ /**
436
+ * Return the value for the signup confirmation page url
437
+ *
438
+ * @return mixed signup confirmation page url, false if the value is not found
439
+ */
440
+ public static function get_mc_signup_confirmation_page_url()
441
+ {
442
+ $page_id = self::get_mc_signup_confirmation_page();
443
+ if ( false == $page_id or 'default' == $page_id ) {
444
+ return false;
445
+ }
446
+
447
+ $confirmation_pages = get_pages( array( 'parent' => 0 ) );
448
+ foreach ($confirmation_pages as $key => $page) {
449
+ if ( $page->ID == $page_id ) {
450
+ return $page->guid;
451
+ }
452
+ }
453
+
454
+ return false;
455
+ }
456
+
457
+ /**
458
+ * Return the value for flag that signifies if the MC authentication settings are valid
459
+ *
460
+ * @return mixed 'true' or 'false', false if the value is not found
461
+ */
462
+ public static function get_mc_auth_valid()
463
+ {
464
+ return get_option( 'sendgrid_mc_auth_valid' );
465
+ }
466
+
467
+ /**
468
+ * Return the value for flag that signifies if the widget notice has been dismissed
469
+ *
470
+ * @return mixed 'true' or 'false', false if the value is not found
471
+ */
472
+ public static function get_mc_widget_notice_dismissed()
473
+ {
474
+ return get_option( 'sendgrid_mc_widget_notice_dismissed' );
475
+ }
476
+
477
  /**
478
  * Sets api_key in the database
479
+ *
480
+ * @param type string $apikey
481
+ *
482
+ * @return bool
483
  */
484
  public static function set_api_key( $apikey )
485
  {
486
  return update_option( 'sendgrid_api_key', $apikey );
487
  }
488
 
489
+ /**
490
+ * Sets MC api_key in the database
491
+ *
492
+ * @param type string $apikey
493
+ *
494
+ * @return bool
495
+ */
496
+ public static function set_mc_api_key( $apikey )
497
+ {
498
+ return update_option( 'sendgrid_mc_api_key', $apikey );
499
+ }
500
+
501
+ /**
502
+ * Sets list id in the database
503
+ *
504
+ * @param type string $list_id
505
+ *
506
+ * @return bool
507
+ */
508
+ public static function set_mc_list_id( $list_id )
509
+ {
510
+ return update_option( 'sendgrid_mc_list_id', $list_id );
511
+ }
512
+
513
+ /**
514
+ * Sets the value for the option to use the transactional credentials in the database
515
+ *
516
+ * @param type string $use_transactional ( 'true' or 'false' )
517
+ *
518
+ * @return bool
519
+ */
520
+ public static function set_mc_opt_use_transactional( $use_transactional )
521
+ {
522
+ return update_option( 'sendgrid_mc_opt_use_transactional', $use_transactional );
523
+ }
524
+
525
+ /**
526
+ * Sets the option for fname and lname requirement in the database
527
+ *
528
+ * @param type string $req_fname_lname ( 'true' or 'false' )
529
+ *
530
+ * @return bool
531
+ */
532
+ public static function set_mc_opt_req_fname_lname( $req_fname_lname )
533
+ {
534
+ return update_option( 'sendgrid_mc_opt_req_fname_lname', $req_fname_lname );
535
+ }
536
+
537
+ /**
538
+ * Sets the option for fname and lname inclusion in the database
539
+ *
540
+ * @param type string $incl_fname_lname ( 'true' or 'false' )
541
+ *
542
+ * @return bool
543
+ */
544
+ public static function set_mc_opt_incl_fname_lname( $incl_fname_lname )
545
+ {
546
+ return update_option( 'sendgrid_mc_opt_incl_fname_lname', $incl_fname_lname );
547
+ }
548
+
549
+ /**
550
+ * Sets the signup email subject in the database
551
+ *
552
+ * @param type string $email_subject
553
+ *
554
+ * @return bool
555
+ */
556
+ public static function set_mc_signup_email_subject( $email_subject )
557
+ {
558
+ return update_option( 'sendgrid_mc_signup_email_subject', $email_subject );
559
+ }
560
+
561
+ /**
562
+ * Sets the signup email contents in the database
563
+ *
564
+ * @param type string $email_content
565
+ *
566
+ * @return bool
567
+ */
568
+ public static function set_mc_signup_email_content( $email_content )
569
+ {
570
+ return update_option( 'sendgrid_mc_signup_email_content', $email_content );
571
+ }
572
+
573
+ /**
574
+ * Sets the signup confirmation page in the database
575
+ *
576
+ * @param type string $confirmation_page
577
+ *
578
+ * @return bool
579
+ */
580
+ public static function set_mc_signup_confirmation_page( $confirmation_page )
581
+ {
582
+ return update_option( 'sendgrid_mc_signup_confirmation_page', $confirmation_page );
583
+ }
584
+
585
+ /**
586
+ * Sets a flag that signifies that the authentication for MC is valid
587
+ *
588
+ * @param type string $auth_valid ( 'true' or 'false' )
589
+ *
590
+ * @return bool
591
+ */
592
+ public static function set_mc_auth_valid( $auth_valid )
593
+ {
594
+ return update_option( 'sendgrid_mc_auth_valid', $auth_valid );
595
+ }
596
+
597
+ /**
598
+ * Sets a flag that signifies that the subscription widget notice has been dismissed
599
+ *
600
+ * @param type string $notice_dismissed ( 'true' or 'false' )
601
+ *
602
+ * @return bool
603
+ */
604
+ public static function set_mc_widget_notice_dismissed( $notice_dismissed )
605
+ {
606
+ return update_option( 'sendgrid_mc_widget_notice_dismissed', $notice_dismissed );
607
+ }
608
+
609
  /**
610
  * Return send method from the database or global variable
611
  *
612
+ * @return string send_method
613
  */
614
  public static function get_send_method()
615
  {
625
  /**
626
  * Return auth method from the database or global variable
627
  *
628
+ * @return string auth_method
629
  */
630
  public static function get_auth_method()
631
  {
651
  /**
652
  * Return port from the database or global variable
653
  *
654
+ * @return mixed port, false if the value is not found
655
  */
656
  public static function get_port()
657
  {
665
  /**
666
  * Return from name from the database or global variable
667
  *
668
+ * @return mixed from_name, false if the value is not found
669
  */
670
  public static function get_from_name()
671
  {
679
  /**
680
  * Return from email address from the database or global variable
681
  *
682
+ * @return mixed from_email, false if the value is not found
683
  */
684
  public static function get_from_email()
685
  {
693
  /**
694
  * Return reply to email address from the database or global variable
695
  *
696
+ * @return mixed reply_to, false if the value is not found
697
  */
698
  public static function get_reply_to()
699
  {
707
  /**
708
  * Return categories from the database or global variable
709
  *
710
+ * @return mixed categories, false if the value is not found
711
  */
712
  public static function get_categories()
713
  {
721
  /**
722
  * Return stats categories from the database or global variable
723
  *
724
+ * @return mixed categories, false if the value is not found
725
  */
726
  public static function get_stats_categories()
727
  {
735
  /**
736
  * Return categories array
737
  *
738
+ * @return array categories
739
  */
740
  public static function get_categories_array()
741
  {
749
  /**
750
  * Return template from the database or global variable
751
  *
752
+ * @return mixed template string, false if the value is not found
753
  */
754
  public static function get_template()
755
  {
763
  /**
764
  * Return content type from the database or global variable
765
  *
766
+ * @return mixed content_type string, false if the value is not found
767
  */
768
  public static function get_content_type()
769
  {
777
  /**
778
  * Returns decrypted string using the key or empty string in case of error
779
  *
780
+ * @return string
781
  */
782
  private static function decrypt( $encrypted_input_string, $key ) {
783
  if ( ! extension_loaded( 'mcrypt' ) ) {
812
  * Check apikey stats permissions
813
  *
814
  * @param string $apikey sendgrid apikey
815
+ *
816
  * @return bool
817
  */
818
  public static function check_api_key_stats( $apikey )
822
  return Sendgrid_Tools::check_api_key_scopes( $apikey, $required_scopes );
823
  }
824
 
825
+ /**
826
+ * Check apikey marketing campaigns permissions
827
+ *
828
+ * @param string $apikey sendgrid apikey
829
+ *
830
+ * @return bool
831
+ */
832
+ public static function check_api_key_mc( $apikey )
833
+ {
834
+ $required_scopes = array( 'marketing_campaigns.create', 'marketing_campaigns.read', 'marketing_campaigns.update', 'marketing_campaigns.delete' );
835
+
836
+ return Sendgrid_Tools::check_api_key_scopes( $apikey, $required_scopes );
837
+ }
838
+
839
  /**
840
  * Returns true if the email is valid, false otherwise
841
  *
850
  * Returns true if all the emails in the headers are valid, false otherwise
851
  *
852
  * @param mixed $headers string or array of headers
853
+ *
854
  * @return bool
855
  */
856
  public static function valid_emails_in_headers( $headers )
938
  /**
939
  * Returns the string content of the input with "<url>" replaced by "url"
940
  *
941
+ * @return string
942
  */
943
  public static function remove_all_tag_urls( $content )
944
  {
945
  return preg_replace('/<(https?:\/\/[^>]*)>/im', '$1', $content);
946
  }
947
+ }
948
+
949
+ /**
950
+ * Function that registers the SendGrid plugin widgets
951
+ *
952
+ * @return void
953
+ */
954
+ function register_sendgrid_widgets() {
955
+ register_widget( 'SendGrid_NLVX_Widget' );
956
+ }
957
+
958
+ /**
959
+ * Function that unregisters the SendGrid plugin widgets
960
+ *
961
+ * @return void
962
+ */
963
+ function unregister_sendgrid_widgets() {
964
+ unregister_widget( 'SendGrid_NLVX_Widget' );
965
+ }
966
+
967
+ /**
968
+ * Function that outputs the SendGrid widget notice
969
+ *
970
+ * @return void
971
+ */
972
+ function sg_subscription_widget_admin_notice() {
973
+ echo '<div class="notice notice-success">';
974
+ echo '<p>';
975
+ echo _e( 'Check out the new SendGrid Subscription Widget! See the SendGrid Plugin settings page in order to configure it.' );
976
+ echo '<form method="post" id="sendgrid_mc_email_form" class="mc_email_form" action="#">';
977
+ echo '<input type="hidden" name="sg_dismiss_widget_notice" id="sg_dismiss_widget_notice" value="true"/>';
978
+ echo '<input type="submit" id="sendgrid_mc_email_submit" value="Dismiss this notice" style="padding: 0!important; font-size: small; background: none; border: none; color: #0066ff; text-decoration: underline; cursor: pointer;" />';
979
+ echo '</form>';
980
+ echo '</p>';
981
+ echo '</div>';
982
  }
lib/class-sendgrid-virtual-pages.php ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( !class_exists( 'SGVirtualPage' ) )
3
+ {
4
+ class SGVirtualPage
5
+ {
6
+ private $slug = NULL;
7
+ private $title = NULL;
8
+ private $content = NULL;
9
+ private $author = NULL;
10
+ private $date = NULL;
11
+ private $type = NULL;
12
+
13
+ public function __construct( $args )
14
+ {
15
+ if ( ! isset( $args['slug'] ) ) {
16
+ throw new Exception( 'No slug given for virtual page' );
17
+ }
18
+
19
+ $this->slug = $args['slug'];
20
+ $this->title = isset( $args['title'] ) ? $args['title'] : '';
21
+ $this->content = isset( $args['content'] ) ? $args['content'] : '';
22
+ $this->author = isset( $args['author'] ) ? $args['author'] : 1;
23
+ $this->date = isset( $args['date'] ) ? $args['date'] : current_time( 'mysql' );
24
+ $this->dategmt = isset( $args['date'] ) ? $args['date'] : current_time( 'mysql', 1 );
25
+ $this->type = isset( $args['type'] ) ? $args['type'] : 'page';
26
+
27
+ add_filter( 'the_posts', array( &$this, 'virtualPage' ) );
28
+ }
29
+
30
+ /**
31
+ * Filter to create virtual page content
32
+ *
33
+ * @param mixed $posts posts saved in wp
34
+ * @return mixed $posts posts saved in wp
35
+ */
36
+ public function virtualPage( $posts )
37
+ {
38
+ global $wp, $wp_query;
39
+
40
+ if ( count( $posts ) == 0 and
41
+ ( strcasecmp( $wp->request, $this->slug ) == 0 or $wp->query_vars['page_id'] == $this->slug ) )
42
+ {
43
+ $post = new stdClass;
44
+
45
+ $post->ID = -1;
46
+ $post->post_author = $this->author;
47
+ $post->post_date = $this->date;
48
+ $post->post_date_gmt = $this->dategmt;
49
+ $post->post_content = $this->content;
50
+ $post->post_title = $this->title;
51
+ $post->post_excerpt = '';
52
+ $post->post_status = 'publish';
53
+ $post->comment_status = 'closed';
54
+ $post->ping_status = 'closed';
55
+ $post->post_password = '';
56
+ $post->post_name = $this->slug;
57
+ $post->to_ping = '';
58
+ $post->pinged = '';
59
+ $post->modified = $post->post_date;
60
+ $post->modified_gmt = $post->post_date_gmt;
61
+ $post->post_content_filtered = '';
62
+ $post->post_parent = 0;
63
+ $post->guid = get_home_url('/' . $this->slug);
64
+ $post->menu_order = 0;
65
+ $post->post_tyle = $this->type;
66
+ $post->post_mime_type = '';
67
+ $post->comment_count = 0;
68
+
69
+ $posts = array( $post );
70
+
71
+ $wp_query->is_page = TRUE;
72
+ $wp_query->is_singular = TRUE;
73
+ $wp_query->is_home = FALSE;
74
+ $wp_query->is_archive = FALSE;
75
+ $wp_query->is_category = FALSE;
76
+
77
+ unset( $wp_query->query['error'] );
78
+ $wp_query->query_vars['error'] = '';
79
+ $wp_query->is_404 = FALSE;
80
+ }
81
+
82
+ return $posts;
83
+ }
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Filter to create general error page
89
+ *
90
+ * @return void
91
+ */
92
+ function sg_create_subscribe_general_error_page()
93
+ {
94
+ $url = basename( $_SERVER['REQUEST_URI'] );
95
+
96
+ if ( $url == 'sg-error' )
97
+ {
98
+ $args = array('slug' => 'sg-error',
99
+ 'title' => 'Subscribe error',
100
+ 'content' => "Something went wrong while trying to send information.");
101
+ $pg = new SGVirtualPage( $args );
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Filter to create invalid token error page
107
+ *
108
+ * @return void
109
+ */
110
+ function sg_create_subscribe_invalid_token_error_page()
111
+ {
112
+ $url = basename( $_SERVER['REQUEST_URI'] );
113
+
114
+ if ( $url == 'sg-subscription-invalid-token' )
115
+ {
116
+ $args = array( 'slug' => 'sg-subscription-invalid-token',
117
+ 'title' => 'Subscribe error',
118
+ 'content' => "Token is invalid, you are not subscribed to our newsletter." );
119
+ $pg = new SGVirtualPage( $args );
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Filter to create missing token error page
125
+ *
126
+ * @return void
127
+ */
128
+ function sg_create_subscribe_missing_token_error_page()
129
+ {
130
+ $url = basename( $_SERVER['REQUEST_URI'] );
131
+
132
+ if ( $url == 'sg-subscription-missing-token' )
133
+ {
134
+ $args = array( 'slug' => 'sg-subscription-missing-token',
135
+ 'title' => 'Subscribe error',
136
+ 'content' => "Token is missing, you are not subscribed to our newsletter." );
137
+ $pg = new SGVirtualPage( $args );
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Filter to create subscribe success page
143
+ *
144
+ * @return void
145
+ */
146
+ function sg_create_subscribe_success_page()
147
+ {
148
+ $url = basename( $_SERVER['REQUEST_URI'] );
149
+
150
+ if ( $url == 'sg-subscription-success' )
151
+ {
152
+ $args = array( 'slug' => 'sg-subscription-success',
153
+ 'title' => 'Subscribe success',
154
+ 'content' => "You have been successfully subscribed to our newsletter." );
155
+ $pg = new SGVirtualPage( $args );
156
+ }
157
+ }
lib/sendgrid/class-sendgrid-api.php CHANGED
@@ -42,7 +42,7 @@ class Sendgrid_API implements Sendgrid_Send {
42
  }
43
 
44
 
45
- $data = array('body' => $fields);
46
  if ( count( $headers ) ) {
47
  $data['headers'] = $headers;
48
  }
42
  }
43
 
44
 
45
+ $data = array( 'body' => $fields, 'decompress' => false );
46
  if ( count( $headers ) ) {
47
  $data['headers'] = $headers;
48
  }
readme.txt CHANGED
@@ -2,31 +2,31 @@
2
  Contributors: team-rs
3
  Donate link: http://sendgrid.com/
4
  Tags: email, email reliability, email templates, sendgrid, smtp, transactional email, wp_mail,email infrastructure, email marketing, marketing email, deliverability, email deliverability, email delivery, email server, mail server, email integration, cloud email
5
- Requires at least: 3.3
6
  Tested up to: 4.5
7
- Stable tag: 1.8.2
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
11
- Send emails throught SendGrid from your WordPress installation using SMTP or API integration.
12
 
13
  == Description ==
14
 
15
  SendGrid's cloud-based email infrastructure relieves businesses of the cost and complexity of maintaining custom email systems. SendGrid provides reliable delivery, scalability and real-time analytics along with flexible APIs that make custom integration a breeze.
16
 
17
- The SendGrid plugin uses SMTP or API integration to send outgoing emails from your WordPress installation. It replaces the wp_mail function included with WordPress.
18
 
19
- First, you need to have PHP-curl extension enabled. To send emails through SMTP you need to install also the 'Swift Mailer' plugin.
20
 
21
- To have the SendGrid plugin running after you have activated it, go to the plugin's settings page and set the SendGrid credentials, and how your email will be sent - either through SMTP or API.
22
 
23
  You can also set default values for the "Name", "Sending Address" and the "Reply Address", so that you don't need to set these headers every time you want to send an email from your application.
24
 
25
- You can set the template ID to be used in all your emails on the settings page or you can set it for each email in headers.
26
 
27
- You can have an individual email sent to each recipient by setting x-smtpapi-to in headers: `'x-smtpapi-to: address1@sendgrid.com,address2@sendgrid.com'`. Note: when using SMTP method you need to have also the `to` address set (this may be dummy data since will be overwritten with the addresses from x-smtpapi-to) in order to be able to send emails.
28
 
29
- Emails are tracked and automatically tagged for statistics within the SendGrid Dashboard. You can also add general tags to every email sent, as well as particular tags based on selected emails defined by your requirements.
30
 
31
  There are a couple levels of integration between your WordPress installation and the SendGrid plugin:
32
 
@@ -129,6 +129,20 @@ Categories used for emails can be set:
129
 
130
  If you would like to configure categories for statistics, you can configure it by setting the 'Categories' field in the 'Statistics settings' section
131
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  == Installation ==
133
 
134
  Requirements:
@@ -155,7 +169,7 @@ To auto install the SendGrid Plugin from the WordPress admin:
155
 
156
  SendGrid settings can optionally be defined as global variables (wp-config.php):
157
 
158
- 1. Set credentials (You can use credentials or Api key. If using credentials, both need to be set in order to get credentials from variables and not from the database. If using API key you need to make sure you set the Mail Send permissions to FULL ACCESS, Stats to READ ACCESS and Template Engine to READ or FULL ACCESS when you created the api key on SendGrid side, so you can send emails and see statistics on wordpress):
159
  * Auth method ('apikey' or 'credentials'): define('SENDGRID_AUTH_METHOD', 'apikey');
160
  * Username: define('SENDGRID_USERNAME', 'sendgrid_username');
161
  * Password: define('SENDGRID_PASSWORD', 'sendgrid_password');
@@ -170,9 +184,19 @@ SendGrid settings can optionally be defined as global variables (wp-config.php):
170
  * Template: define('SENDGRID_TEMPLATE', 'templateID');
171
  * Content-type: define('SENDGRID_CONTENT_TYPE', 'html');
172
 
 
 
 
 
 
 
 
 
 
 
173
  == Frequently asked questions ==
174
 
175
- = What credentials do I need to add on settings page =
176
 
177
  Create a SendGrid account at <a href="http://sendgrid.com/partner/wordpress" target="_blank">https://sendgrid.com/partner/wordpress</a> and generate a new API key on <https://app.sendgrid.com/settings/api_keys>.
178
 
@@ -180,7 +204,7 @@ Create a SendGrid account at <a href="http://sendgrid.com/partner/wordpress" tar
180
 
181
  Add it into your wp-config.php file. Example: `define('SENDGRID_API_KEY', 'your_api_key');`.
182
 
183
- = How to use SendGrid with WP Better Emails plugin =
184
 
185
  If you have WP Better Emails plugin installed and you want to use the template defined here instead of the SendGrid template you can add the following code in your functions.php file from your theme:
186
 
@@ -202,6 +226,19 @@ Using the default templates from WP Better Emails will cause all emails to be se
202
 
203
  For a detailed explanation see this page: https://support.sendgrid.com/hc/en-us/articles/200181418-Plain-text-emails-converted-to-HTML
204
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  == Screenshots ==
206
 
207
  1. Go to Admin Panel, section Plugins and activate the SendGrid plugin. If you want to send emails through SMTP you need to install also the 'Swift Mailer' plugin.
@@ -219,6 +256,11 @@ For a detailed explanation see this page: https://support.sendgrid.com/hc/en-us/
219
 
220
  == Changelog ==
221
 
 
 
 
 
 
222
  = 1.8.2 =
223
  * Update SendGrid logos
224
  = 1.8.1 =
@@ -323,6 +365,11 @@ For a detailed explanation see this page: https://support.sendgrid.com/hc/en-us/
323
 
324
  == Upgrade notice ==
325
 
 
 
 
 
 
326
  = 1.8.2 =
327
  * Update SendGrid logos
328
  = 1.8.1 =
2
  Contributors: team-rs
3
  Donate link: http://sendgrid.com/
4
  Tags: email, email reliability, email templates, sendgrid, smtp, transactional email, wp_mail,email infrastructure, email marketing, marketing email, deliverability, email deliverability, email delivery, email server, mail server, email integration, cloud email
5
+ Requires at least: 4.2
6
  Tested up to: 4.5
7
+ Stable tag: 1.9.0
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
11
+ Send emails and upload contacts through SendGrid from your WordPress installation using SMTP or API integration.
12
 
13
  == Description ==
14
 
15
  SendGrid's cloud-based email infrastructure relieves businesses of the cost and complexity of maintaining custom email systems. SendGrid provides reliable delivery, scalability and real-time analytics along with flexible APIs that make custom integration a breeze.
16
 
17
+ The SendGrid plugin uses SMTP or API integration to send outgoing emails from your WordPress installation. It replaces the wp_mail function included with WordPress. It also offers the possibility to upload contacts to your SendGrid account for marketing emails through a subscription widget.
18
 
19
+ In order to send emails through SMTP you need to install the 'Swift Mailer' plugin.
20
 
21
+ To get the SendGrid plugin running after you have activated it, go to the plugin's settings page and set the SendGrid credentials, then choose how your email will be sent - either through SMTP or API.
22
 
23
  You can also set default values for the "Name", "Sending Address" and the "Reply Address", so that you don't need to set these headers every time you want to send an email from your application.
24
 
25
+ You can set the template ID to be used in all your emails on the settings page or you can set it for each email in the headers.
26
 
27
+ You can have an individual email sent to each recipient by setting x-smtpapi-to in headers: `'x-smtpapi-to: address1@sendgrid.com,address2@sendgrid.com'`. Note: when using SMTP method you need to have also the `to` address set (this may be dummy data since will be overwritten with the addresses from x-smtpapi-to) in order to be able to send emails.
28
 
29
+ Emails are tracked and automatically tagged for statistics within the SendGrid Dashboard. You can also add general tags to every email sent, as well as particular tags based on selected emails defined by your requirements.
30
 
31
  There are a couple levels of integration between your WordPress installation and the SendGrid plugin:
32
 
129
 
130
  If you would like to configure categories for statistics, you can configure it by setting the 'Categories' field in the 'Statistics settings' section
131
 
132
+ = The Subscription Widget =
133
+
134
+ You can let your users subscribe to your marketing emails using the subscription widget. In order to set it up, go to the "Subscription Widget" tab on the plugin's settings page. You can use a separate API key for contact upload, other that what you set up for transactional emails. If you want to use the same credentials for contact upload as you do for sending emails just check the "Use same authentication as transactional" option.
135
+
136
+ After you set up a valid API key you will be able to select the list where contacts will be uploaded. If you don't have a list set up, go to your SendGrid Dashboard -> Marketing -> Contacts and set one up.
137
+
138
+ Once you have set up a valid authentication method and selected a valid list, a form to test the subscription will appear at the bottom of the page on the Subscription Widget settings tab. You can test the subscription process by entering an email address and clicking 'Test'.
139
+
140
+ An email will be sent to that email address with a confirmation link. By clicking it, you will be redirected to your website and the email will be uploaded to your contacts on the SendGrid Dashboard. You can customize the subject and contents of the email in the settings page.
141
+
142
+ In order to display the widget on your website, go to the widgets page and using drag and drop, add it to the section of your page where you want it displayed. You can configure the title and messages that are being displayed to the user. By default, only the email field is displayed and required. You can configure this in the plugin settings page, on the Subscription Widget tab, if you wish to also display the First Name and Last Name fields and whether they are required or not.
143
+
144
+ You can also configure the page that will be displayed to the user by selecting it from the drop down menu on the settings page.
145
+
146
  == Installation ==
147
 
148
  Requirements:
169
 
170
  SendGrid settings can optionally be defined as global variables (wp-config.php):
171
 
172
+ 1. Set credentials (You can use credentials or API key. If using credentials, both need to be set in order to get credentials from variables and not from the database. If using API key you need to make sure you set the Mail Send permissions to FULL ACCESS, Stats to READ ACCESS and Template Engine to READ or FULL ACCESS when you created the api key on SendGrid side, so you can send emails and see statistics on wordpress):
173
  * Auth method ('apikey' or 'credentials'): define('SENDGRID_AUTH_METHOD', 'apikey');
174
  * Username: define('SENDGRID_USERNAME', 'sendgrid_username');
175
  * Password: define('SENDGRID_PASSWORD', 'sendgrid_password');
184
  * Template: define('SENDGRID_TEMPLATE', 'templateID');
185
  * Content-type: define('SENDGRID_CONTENT_TYPE', 'html');
186
 
187
+ 3. Set widget related settings:
188
+ * Marketing Campaigns API key: define('SENDGRID_MC_API_KEY', 'sendgrid_mc_api_key');
189
+ * Use the same authentication as for sending emails ('true' or 'false'): define('SENDGRID_MC_OPT_USE_TRANSACTIONAL', 'false');
190
+ * The contact list ID: define('SENDGRID_MC_LIST_ID', 'listID');
191
+ * Display the first and last name fields ('true' or 'false'): define('SENDGRID_MC_OPT_INCL_FNAME_LNAME', 'true');
192
+ * First and last name fields are required ('true' or 'false'): define('SENDGRID_MC_OPT_REQ_FNAME_LNAME', 'true');
193
+ * Signup confirmation email subject: define('SENDGRID_MC_SIGNUP_EMAIL_SUBJECT', 'Confirm subscription');
194
+ * Signup confirmation email content: define('SENDGRID_MC_SIGNUP_EMAIL_CONTENT', '&lt;a href="%confirmation_link%"&gt;click here&lt;/a&gt;');
195
+ * Signup confirmation page ID: define('SENDGRID_MC_SIGNUP_CONFIRMATION_PAGE', 'page_id');
196
+
197
  == Frequently asked questions ==
198
 
199
+ = What credentials do I need to add on settings page ? =
200
 
201
  Create a SendGrid account at <a href="http://sendgrid.com/partner/wordpress" target="_blank">https://sendgrid.com/partner/wordpress</a> and generate a new API key on <https://app.sendgrid.com/settings/api_keys>.
202
 
204
 
205
  Add it into your wp-config.php file. Example: `define('SENDGRID_API_KEY', 'your_api_key');`.
206
 
207
+ = How to use SendGrid with WP Better Emails plugin ? =
208
 
209
  If you have WP Better Emails plugin installed and you want to use the template defined here instead of the SendGrid template you can add the following code in your functions.php file from your theme:
210
 
226
 
227
  For a detailed explanation see this page: https://support.sendgrid.com/hc/en-us/articles/200181418-Plain-text-emails-converted-to-HTML
228
 
229
+ = Will contacts from the widget be uploaded to Marketing Campaigns or Legacy Newsletter ? =
230
+
231
+ The contacts will only be uploaded to Marketing Campaigns.
232
+
233
+ = What permissions should my API keys have ? =
234
+
235
+ For the API Key used for sending emails, that is entered on the General tab, the key needs to have Full Access to Mail Send and Read Access to Stats.
236
+ For the API Key used for contact upload, that is entered on the Subscription Widget tab, the key needs to have Full Access to Marketings Campaigns.
237
+
238
+ = Can I disable the opt-in email? =
239
+
240
+ No. SendGrid’s Email Policy requires all email addressing being sent to by SendGrid customers be confirmed opt-in addresses.
241
+
242
  == Screenshots ==
243
 
244
  1. Go to Admin Panel, section Plugins and activate the SendGrid plugin. If you want to send emails through SMTP you need to install also the 'Swift Mailer' plugin.
256
 
257
  == Changelog ==
258
 
259
+ = 1.9.0 =
260
+ * Added the SendGrid Subscription Widget
261
+ * The settings page now has tabs to separate the configuration of general settings from the widget settings
262
+ * Fixed an issue where a 'gzinflate()' warning was displayed in Query Monitor for each plugin request
263
+ * Fixed an issue where the API Key would be deleted from the db if it was set in wp-config
264
  = 1.8.2 =
265
  * Update SendGrid logos
266
  = 1.8.1 =
365
 
366
  == Upgrade notice ==
367
 
368
+ = 1.9.0 =
369
+ * Added the SendGrid Subscription Widget
370
+ * The settings page now has tabs to separate the configuration of general settings from the widget settings
371
+ * Fixed an issue where a 'gzinflate()' warning was displayed in Query Monitor for each plugin request
372
+ * Fixed an issue where the API Key would be deleted from the db if it was set in wp-config
373
  = 1.8.2 =
374
  * Update SendGrid logos
375
  = 1.8.1 =
view/css/select2.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single .select2-selection__clear{position:relative}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-search--inline{float:left}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;padding:0}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select2-results{display:block}.select2-results__options{list-style:none;margin:0;padding:0}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none}.select2-results__option[aria-selected]{cursor:pointer}.select2-container--open .select2-dropdown{left:0}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-search--dropdown{display:block;padding:4px}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-search--dropdown.select2-search--hide{display:none}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select2-hidden-accessible{border:0 !important;clip:rect(0 0 0 0) !important;height:1px !important;margin:-1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;width:1px !important}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text}.select2-container--default .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%}.select2-container--default .select2-selection--multiple .select2-selection__rendered li{list-style:none}.select2-container--default .select2-selection--multiple .select2-selection__placeholder{color:#999;margin-top:5px;float:left}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#333}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline{float:right}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--default.select2-container--focus .select2-selection--multiple{border:solid black 1px;outline:0}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none}.select2-container--default.select2-container--open.select2-container--above .select2-selection--single,.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple{border-top-left-radius:0;border-top-right-radius:0}.select2-container--default.select2-container--open.select2-container--below .select2-selection--single,.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa}.select2-container--default .select2-search--inline .select2-search__field{background:transparent;border:none;outline:0;box-shadow:none;-webkit-appearance:textfield}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--default .select2-results__option[role=group]{padding:0}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic .select2-selection--single{background-color:#f7f7f7;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #fff 50%, #eee 100%);background-image:-o-linear-gradient(top, #fff 50%, #eee 100%);background-image:linear-gradient(to bottom, #fff 50%, #eee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eee 50%, #ccc 100%);background-image:-o-linear-gradient(top, #eee 50%, #ccc 100%);background-image:linear-gradient(to bottom, #eee 50%, #ccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0)}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:transparent;border:none}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #fff 0%, #eee 50%);background-image:-o-linear-gradient(top, #fff 0%, #eee 50%);background-image:linear-gradient(to bottom, #fff 0%, #eee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eee 50%, #fff 100%);background-image:-o-linear-gradient(top, #eee 50%, #fff 100%);background-image:linear-gradient(to bottom, #eee 50%, #fff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0)}.select2-container--classic .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{float:right}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;box-shadow:none}.select2-container--classic .select2-dropdown{background-color:#fff;border:1px solid transparent}.select2-container--classic .select2-dropdown--above{border-bottom:none}.select2-container--classic .select2-dropdown--below{border-top:none}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--classic .select2-results__option[role=group]{padding:0}.select2-container--classic .select2-results__option[aria-disabled=true]{color:grey}.select2-container--classic .select2-results__option--highlighted[aria-selected]{background-color:#3875d7;color:#fff}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb}
view/css/sendgrid.css CHANGED
@@ -384,4 +384,18 @@
384
 
385
  #sendgrid-statistics-page .error {
386
  width: 1660px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387
  }
384
 
385
  #sendgrid-statistics-page .error {
386
  width: 1660px;
387
+ }
388
+
389
+ .sendgrid-settings-top-header {
390
+ margin-top: 0px;
391
+ }
392
+
393
+ .sengrid-settings-select {
394
+ width: 435px;
395
+ }
396
+
397
+ .sengrid-settings-nav-bar {
398
+ font-size: 0.9em;
399
+ padding-bottom: 0px;
400
+ margin-bottom: 20px!important;
401
  }
view/js/select2.full.min.js ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ /*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){var b=function(){if(a&&a.fn&&a.fn.select2&&a.fn.select2.amd)var b=a.fn.select2.amd;var b;return function(){if(!b||!b.requirejs){b?c=b:b={};var a,c,d;!function(b){function e(a,b){return u.call(a,b)}function f(a,b){var c,d,e,f,g,h,i,j,k,l,m,n=b&&b.split("/"),o=s.map,p=o&&o["*"]||{};if(a&&"."===a.charAt(0))if(b){for(a=a.split("/"),g=a.length-1,s.nodeIdCompat&&w.test(a[g])&&(a[g]=a[g].replace(w,"")),a=n.slice(0,n.length-1).concat(a),k=0;k<a.length;k+=1)if(m=a[k],"."===m)a.splice(k,1),k-=1;else if(".."===m){if(1===k&&(".."===a[2]||".."===a[0]))break;k>0&&(a.splice(k-1,2),k-=2)}a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if((n||p)&&o){for(c=a.split("/"),k=c.length;k>0;k-=1){if(d=c.slice(0,k).join("/"),n)for(l=n.length;l>0;l-=1)if(e=o[n.slice(0,l).join("/")],e&&(e=e[d])){f=e,h=k;break}if(f)break;!i&&p&&p[d]&&(i=p[d],j=k)}!f&&i&&(f=i,h=j),f&&(c.splice(0,h,f),a=c.join("/"))}return a}function g(a,c){return function(){var d=v.call(arguments,0);return"string"!=typeof d[0]&&1===d.length&&d.push(null),n.apply(b,d.concat([a,c]))}}function h(a){return function(b){return f(b,a)}}function i(a){return function(b){q[a]=b}}function j(a){if(e(r,a)){var c=r[a];delete r[a],t[a]=!0,m.apply(b,c)}if(!e(q,a)&&!e(t,a))throw new Error("No "+a);return q[a]}function k(a){var b,c=a?a.indexOf("!"):-1;return c>-1&&(b=a.substring(0,c),a=a.substring(c+1,a.length)),[b,a]}function l(a){return function(){return s&&s.config&&s.config[a]||{}}}var m,n,o,p,q={},r={},s={},t={},u=Object.prototype.hasOwnProperty,v=[].slice,w=/\.js$/;o=function(a,b){var c,d=k(a),e=d[0];return a=d[1],e&&(e=f(e,b),c=j(e)),e?a=c&&c.normalize?c.normalize(a,h(b)):f(a,b):(a=f(a,b),d=k(a),e=d[0],a=d[1],e&&(c=j(e))),{f:e?e+"!"+a:a,n:a,pr:e,p:c}},p={require:function(a){return g(a)},exports:function(a){var b=q[a];return"undefined"!=typeof b?b:q[a]={}},module:function(a){return{id:a,uri:"",exports:q[a],config:l(a)}}},m=function(a,c,d,f){var h,k,l,m,n,s,u=[],v=typeof d;if(f=f||a,"undefined"===v||"function"===v){for(c=!c.length&&d.length?["require","exports","module"]:c,n=0;n<c.length;n+=1)if(m=o(c[n],f),k=m.f,"require"===k)u[n]=p.require(a);else if("exports"===k)u[n]=p.exports(a),s=!0;else if("module"===k)h=u[n]=p.module(a);else if(e(q,k)||e(r,k)||e(t,k))u[n]=j(k);else{if(!m.p)throw new Error(a+" missing "+k);m.p.load(m.n,g(f,!0),i(k),{}),u[n]=q[k]}l=d?d.apply(q[a],u):void 0,a&&(h&&h.exports!==b&&h.exports!==q[a]?q[a]=h.exports:l===b&&s||(q[a]=l))}else a&&(q[a]=d)},a=c=n=function(a,c,d,e,f){if("string"==typeof a)return p[a]?p[a](c):j(o(a,c).f);if(!a.splice){if(s=a,s.deps&&n(s.deps,s.callback),!c)return;c.splice?(a=c,c=d,d=null):a=b}return c=c||function(){},"function"==typeof d&&(d=e,e=f),e?m(b,a,c,d):setTimeout(function(){m(b,a,c,d)},4),n},n.config=function(a){return n(a)},a._defined=q,d=function(a,b,c){if("string"!=typeof a)throw new Error("See almond README: incorrect module build, no module name");b.splice||(c=b,b=[]),e(q,a)||e(r,a)||(r[a]=[a,b,c])},d.amd={jQuery:!0}}(),b.requirejs=a,b.require=c,b.define=d}}(),b.define("almond",function(){}),b.define("jquery",[],function(){var b=a||$;return null==b&&console&&console.error&&console.error("Select2: An instance of jQuery or a jQuery-compatible library was not found. Make sure that you are including jQuery before Select2 on your web page."),b}),b.define("select2/utils",["jquery"],function(a){function b(a){var b=a.prototype,c=[];for(var d in b){var e=b[d];"function"==typeof e&&"constructor"!==d&&c.push(d)}return c}var c={};c.Extend=function(a,b){function c(){this.constructor=a}var d={}.hasOwnProperty;for(var e in b)d.call(b,e)&&(a[e]=b[e]);return c.prototype=b.prototype,a.prototype=new c,a.__super__=b.prototype,a},c.Decorate=function(a,c){function d(){var b=Array.prototype.unshift,d=c.prototype.constructor.length,e=a.prototype.constructor;d>0&&(b.call(arguments,a.prototype.constructor),e=c.prototype.constructor),e.apply(this,arguments)}function e(){this.constructor=d}var f=b(c),g=b(a);c.displayName=a.displayName,d.prototype=new e;for(var h=0;h<g.length;h++){var i=g[h];d.prototype[i]=a.prototype[i]}for(var j=(function(a){var b=function(){};a in d.prototype&&(b=d.prototype[a]);var e=c.prototype[a];return function(){var a=Array.prototype.unshift;return a.call(arguments,b),e.apply(this,arguments)}}),k=0;k<f.length;k++){var l=f[k];d.prototype[l]=j(l)}return d};var d=function(){this.listeners={}};return d.prototype.on=function(a,b){this.listeners=this.listeners||{},a in this.listeners?this.listeners[a].push(b):this.listeners[a]=[b]},d.prototype.trigger=function(a){var b=Array.prototype.slice,c=b.call(arguments,1);this.listeners=this.listeners||{},null==c&&(c=[]),0===c.length&&c.push({}),c[0]._type=a,a in this.listeners&&this.invoke(this.listeners[a],b.call(arguments,1)),"*"in this.listeners&&this.invoke(this.listeners["*"],arguments)},d.prototype.invoke=function(a,b){for(var c=0,d=a.length;d>c;c++)a[c].apply(this,b)},c.Observable=d,c.generateChars=function(a){for(var b="",c=0;a>c;c++){var d=Math.floor(36*Math.random());b+=d.toString(36)}return b},c.bind=function(a,b){return function(){a.apply(b,arguments)}},c._convertData=function(a){for(var b in a){var c=b.split("-"),d=a;if(1!==c.length){for(var e=0;e<c.length;e++){var f=c[e];f=f.substring(0,1).toLowerCase()+f.substring(1),f in d||(d[f]={}),e==c.length-1&&(d[f]=a[b]),d=d[f]}delete a[b]}}return a},c.hasScroll=function(b,c){var d=a(c),e=c.style.overflowX,f=c.style.overflowY;return e!==f||"hidden"!==f&&"visible"!==f?"scroll"===e||"scroll"===f?!0:d.innerHeight()<c.scrollHeight||d.innerWidth()<c.scrollWidth:!1},c.escapeMarkup=function(a){var b={"\\":"&#92;","&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#47;"};return"string"!=typeof a?a:String(a).replace(/[&<>"'\/\\]/g,function(a){return b[a]})},c.appendMany=function(b,c){if("1.7"===a.fn.jquery.substr(0,3)){var d=a();a.map(c,function(a){d=d.add(a)}),c=d}b.append(c)},c}),b.define("select2/results",["jquery","./utils"],function(a,b){function c(a,b,d){this.$element=a,this.data=d,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('<ul class="select2-results__options" role="tree"></ul>');return this.options.get("multiple")&&b.attr("aria-multiselectable","true"),this.$results=b,b},c.prototype.clear=function(){this.$results.empty()},c.prototype.displayMessage=function(b){var c=this.options.get("escapeMarkup");this.clear(),this.hideLoading();var d=a('<li role="treeitem" aria-live="assertive" class="select2-results__option"></li>'),e=this.options.get("translations").get(b.message);d.append(c(e(b.args))),d[0].className+=" select2-results__message",this.$results.append(d)},c.prototype.hideMessages=function(){this.$results.find(".select2-results__message").remove()},c.prototype.append=function(a){this.hideLoading();var b=[];if(null==a.results||0===a.results.length)return void(0===this.$results.children().length&&this.trigger("results:message",{message:"noResults"}));a.results=this.sort(a.results);for(var c=0;c<a.results.length;c++){var d=a.results[c],e=this.option(d);b.push(e)}this.$results.append(b)},c.prototype.position=function(a,b){var c=b.find(".select2-results");c.append(a)},c.prototype.sort=function(a){var b=this.options.get("sorter");return b(a)},c.prototype.highlightFirstItem=function(){var a=this.$results.find(".select2-results__option[aria-selected]"),b=a.filter("[aria-selected=true]");b.length>0?b.first().trigger("mouseenter"):a.first().trigger("mouseenter"),this.ensureHighlightVisible()},c.prototype.setClasses=function(){var b=this;this.data.current(function(c){var d=a.map(c,function(a){return a.id.toString()}),e=b.$results.find(".select2-results__option[aria-selected]");e.each(function(){var b=a(this),c=a.data(this,"data"),e=""+c.id;null!=c.element&&c.element.selected||null==c.element&&a.inArray(e,d)>-1?b.attr("aria-selected","true"):b.attr("aria-selected","false")})})},c.prototype.showLoading=function(a){this.hideLoading();var b=this.options.get("translations").get("searching"),c={disabled:!0,loading:!0,text:b(a)},d=this.option(c);d.className+=" loading-results",this.$results.prepend(d)},c.prototype.hideLoading=function(){this.$results.find(".loading-results").remove()},c.prototype.option=function(b){var c=document.createElement("li");c.className="select2-results__option";var d={role:"treeitem","aria-selected":"false"};b.disabled&&(delete d["aria-selected"],d["aria-disabled"]="true"),null==b.id&&delete d["aria-selected"],null!=b._resultId&&(c.id=b._resultId),b.title&&(c.title=b.title),b.children&&(d.role="group",d["aria-label"]=b.text,delete d["aria-selected"]);for(var e in d){var f=d[e];c.setAttribute(e,f)}if(b.children){var g=a(c),h=document.createElement("strong");h.className="select2-results__group";a(h);this.template(b,h);for(var i=[],j=0;j<b.children.length;j++){var k=b.children[j],l=this.option(k);i.push(l)}var m=a("<ul></ul>",{"class":"select2-results__options select2-results__options--nested"});m.append(i),g.append(h),g.append(m)}else this.template(b,c);return a.data(c,"data",b),c},c.prototype.bind=function(b,c){var d=this,e=b.id+"-results";this.$results.attr("id",e),b.on("results:all",function(a){d.clear(),d.append(a.data),b.isOpen()&&(d.setClasses(),d.highlightFirstItem())}),b.on("results:append",function(a){d.append(a.data),b.isOpen()&&d.setClasses()}),b.on("query",function(a){d.hideMessages(),d.showLoading(a)}),b.on("select",function(){b.isOpen()&&(d.setClasses(),d.highlightFirstItem())}),b.on("unselect",function(){b.isOpen()&&(d.setClasses(),d.highlightFirstItem())}),b.on("open",function(){d.$results.attr("aria-expanded","true"),d.$results.attr("aria-hidden","false"),d.setClasses(),d.ensureHighlightVisible()}),b.on("close",function(){d.$results.attr("aria-expanded","false"),d.$results.attr("aria-hidden","true"),d.$results.removeAttr("aria-activedescendant")}),b.on("results:toggle",function(){var a=d.getHighlightedResults();0!==a.length&&a.trigger("mouseup")}),b.on("results:select",function(){var a=d.getHighlightedResults();if(0!==a.length){var b=a.data("data");"true"==a.attr("aria-selected")?d.trigger("close",{}):d.trigger("select",{data:b})}}),b.on("results:previous",function(){var a=d.getHighlightedResults(),b=d.$results.find("[aria-selected]"),c=b.index(a);if(0!==c){var e=c-1;0===a.length&&(e=0);var f=b.eq(e);f.trigger("mouseenter");var g=d.$results.offset().top,h=f.offset().top,i=d.$results.scrollTop()+(h-g);0===e?d.$results.scrollTop(0):0>h-g&&d.$results.scrollTop(i)}}),b.on("results:next",function(){var a=d.getHighlightedResults(),b=d.$results.find("[aria-selected]"),c=b.index(a),e=c+1;if(!(e>=b.length)){var f=b.eq(e);f.trigger("mouseenter");var g=d.$results.offset().top+d.$results.outerHeight(!1),h=f.offset().top+f.outerHeight(!1),i=d.$results.scrollTop()+h-g;0===e?d.$results.scrollTop(0):h>g&&d.$results.scrollTop(i)}}),b.on("results:focus",function(a){a.element.addClass("select2-results__option--highlighted")}),b.on("results:message",function(a){d.displayMessage(a)}),a.fn.mousewheel&&this.$results.on("mousewheel",function(a){var b=d.$results.scrollTop(),c=d.$results.get(0).scrollHeight-b+a.deltaY,e=a.deltaY>0&&b-a.deltaY<=0,f=a.deltaY<0&&c<=d.$results.height();e?(d.$results.scrollTop(0),a.preventDefault(),a.stopPropagation()):f&&(d.$results.scrollTop(d.$results.get(0).scrollHeight-d.$results.height()),a.preventDefault(),a.stopPropagation())}),this.$results.on("mouseup",".select2-results__option[aria-selected]",function(b){var c=a(this),e=c.data("data");return"true"===c.attr("aria-selected")?void(d.options.get("multiple")?d.trigger("unselect",{originalEvent:b,data:e}):d.trigger("close",{})):void d.trigger("select",{originalEvent:b,data:e})}),this.$results.on("mouseenter",".select2-results__option[aria-selected]",function(b){var c=a(this).data("data");d.getHighlightedResults().removeClass("select2-results__option--highlighted"),d.trigger("results:focus",{data:c,element:a(this)})})},c.prototype.getHighlightedResults=function(){var a=this.$results.find(".select2-results__option--highlighted");return a},c.prototype.destroy=function(){this.$results.remove()},c.prototype.ensureHighlightVisible=function(){var a=this.getHighlightedResults();if(0!==a.length){var b=this.$results.find("[aria-selected]"),c=b.index(a),d=this.$results.offset().top,e=a.offset().top,f=this.$results.scrollTop()+(e-d),g=e-d;f-=2*a.outerHeight(!1),2>=c?this.$results.scrollTop(0):(g>this.$results.outerHeight()||0>g)&&this.$results.scrollTop(f)}},c.prototype.template=function(b,c){var d=this.options.get("templateResult"),e=this.options.get("escapeMarkup"),f=d(b,c);null==f?c.style.display="none":"string"==typeof f?c.innerHTML=e(f):a(c).append(f)},c}),b.define("select2/keys",[],function(){var a={BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46};return a}),b.define("select2/selection/base",["jquery","../utils","../keys"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,b.Observable),d.prototype.render=function(){var b=a('<span class="select2-selection" role="combobox" aria-haspopup="true" aria-expanded="false"></span>');return this._tabindex=0,null!=this.$element.data("old-tabindex")?this._tabindex=this.$element.data("old-tabindex"):null!=this.$element.attr("tabindex")&&(this._tabindex=this.$element.attr("tabindex")),b.attr("title",this.$element.attr("title")),b.attr("tabindex",this._tabindex),this.$selection=b,b},d.prototype.bind=function(a,b){var d=this,e=(a.id+"-container",a.id+"-results");this.container=a,this.$selection.on("focus",function(a){d.trigger("focus",a)}),this.$selection.on("blur",function(a){d._handleBlur(a)}),this.$selection.on("keydown",function(a){d.trigger("keypress",a),a.which===c.SPACE&&a.preventDefault()}),a.on("results:focus",function(a){d.$selection.attr("aria-activedescendant",a.data._resultId)}),a.on("selection:update",function(a){d.update(a.data)}),a.on("open",function(){d.$selection.attr("aria-expanded","true"),d.$selection.attr("aria-owns",e),d._attachCloseHandler(a)}),a.on("close",function(){d.$selection.attr("aria-expanded","false"),d.$selection.removeAttr("aria-activedescendant"),d.$selection.removeAttr("aria-owns"),d.$selection.focus(),d._detachCloseHandler(a)}),a.on("enable",function(){d.$selection.attr("tabindex",d._tabindex)}),a.on("disable",function(){d.$selection.attr("tabindex","-1")})},d.prototype._handleBlur=function(b){var c=this;window.setTimeout(function(){document.activeElement==c.$selection[0]||a.contains(c.$selection[0],document.activeElement)||c.trigger("blur",b)},1)},d.prototype._attachCloseHandler=function(b){a(document.body).on("mousedown.select2."+b.id,function(b){var c=a(b.target),d=c.closest(".select2"),e=a(".select2.select2-container--open");e.each(function(){var b=a(this);if(this!=d[0]){var c=b.data("element");c.select2("close")}})})},d.prototype._detachCloseHandler=function(b){a(document.body).off("mousedown.select2."+b.id)},d.prototype.position=function(a,b){var c=b.find(".selection");c.append(a)},d.prototype.destroy=function(){this._detachCloseHandler(this.container)},d.prototype.update=function(a){throw new Error("The `update` method must be defined in child classes.")},d}),b.define("select2/selection/single",["jquery","./base","../utils","../keys"],function(a,b,c,d){function e(){e.__super__.constructor.apply(this,arguments)}return c.Extend(e,b),e.prototype.render=function(){var a=e.__super__.render.call(this);return a.addClass("select2-selection--single"),a.html('<span class="select2-selection__rendered"></span><span class="select2-selection__arrow" role="presentation"><b role="presentation"></b></span>'),a},e.prototype.bind=function(a,b){var c=this;e.__super__.bind.apply(this,arguments);var d=a.id+"-container";this.$selection.find(".select2-selection__rendered").attr("id",d),this.$selection.attr("aria-labelledby",d),this.$selection.on("mousedown",function(a){1===a.which&&c.trigger("toggle",{originalEvent:a})}),this.$selection.on("focus",function(a){}),this.$selection.on("blur",function(a){}),a.on("focus",function(b){a.isOpen()||c.$selection.focus()}),a.on("selection:update",function(a){c.update(a.data)})},e.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},e.prototype.display=function(a,b){var c=this.options.get("templateSelection"),d=this.options.get("escapeMarkup");return d(c(a,b))},e.prototype.selectionContainer=function(){return a("<span></span>")},e.prototype.update=function(a){if(0===a.length)return void this.clear();var b=a[0],c=this.$selection.find(".select2-selection__rendered"),d=this.display(b,c);c.empty().append(d),c.prop("title",b.title||b.text)},e}),b.define("select2/selection/multiple",["jquery","./base","../utils"],function(a,b,c){function d(a,b){d.__super__.constructor.apply(this,arguments)}return c.Extend(d,b),d.prototype.render=function(){var a=d.__super__.render.call(this);return a.addClass("select2-selection--multiple"),a.html('<ul class="select2-selection__rendered"></ul>'),a},d.prototype.bind=function(b,c){var e=this;d.__super__.bind.apply(this,arguments),this.$selection.on("click",function(a){e.trigger("toggle",{originalEvent:a})}),this.$selection.on("click",".select2-selection__choice__remove",function(b){if(!e.options.get("disabled")){var c=a(this),d=c.parent(),f=d.data("data");e.trigger("unselect",{originalEvent:b,data:f})}})},d.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},d.prototype.display=function(a,b){var c=this.options.get("templateSelection"),d=this.options.get("escapeMarkup");return d(c(a,b))},d.prototype.selectionContainer=function(){var b=a('<li class="select2-selection__choice"><span class="select2-selection__choice__remove" role="presentation">&times;</span></li>');return b},d.prototype.update=function(a){if(this.clear(),0!==a.length){for(var b=[],d=0;d<a.length;d++){var e=a[d],f=this.selectionContainer(),g=this.display(e,f);f.append(g),f.prop("title",e.title||e.text),f.data("data",e),b.push(f)}var h=this.$selection.find(".select2-selection__rendered");c.appendMany(h,b)}},d}),b.define("select2/selection/placeholder",["../utils"],function(a){function b(a,b,c){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c)}return b.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},b.prototype.createPlaceholder=function(a,b){var c=this.selectionContainer();return c.html(this.display(b)),c.addClass("select2-selection__placeholder").removeClass("select2-selection__choice"),c},b.prototype.update=function(a,b){var c=1==b.length&&b[0].id!=this.placeholder.id,d=b.length>1;if(d||c)return a.call(this,b);this.clear();var e=this.createPlaceholder(this.placeholder);this.$selection.find(".select2-selection__rendered").append(e)},b}),b.define("select2/selection/allowClear",["jquery","../keys"],function(a,b){function c(){}return c.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),null==this.placeholder&&this.options.get("debug")&&window.console&&console.error&&console.error("Select2: The `allowClear` option should be used in combination with the `placeholder` option."),this.$selection.on("mousedown",".select2-selection__clear",function(a){d._handleClear(a)}),b.on("keypress",function(a){d._handleKeyboardClear(a,b)})},c.prototype._handleClear=function(a,b){if(!this.options.get("disabled")){var c=this.$selection.find(".select2-selection__clear");if(0!==c.length){b.stopPropagation();for(var d=c.data("data"),e=0;e<d.length;e++){var f={data:d[e]};if(this.trigger("unselect",f),f.prevented)return}this.$element.val(this.placeholder.id).trigger("change"),this.trigger("toggle",{})}}},c.prototype._handleKeyboardClear=function(a,c,d){d.isOpen()||(c.which==b.DELETE||c.which==b.BACKSPACE)&&this._handleClear(c)},c.prototype.update=function(b,c){if(b.call(this,c),!(this.$selection.find(".select2-selection__placeholder").length>0||0===c.length)){var d=a('<span class="select2-selection__clear">&times;</span>');d.data("data",c),this.$selection.find(".select2-selection__rendered").prepend(d)}},c}),b.define("select2/selection/search",["jquery","../utils","../keys"],function(a,b,c){function d(a,b,c){a.call(this,b,c)}return d.prototype.render=function(b){var c=a('<li class="select2-search select2-search--inline"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" role="textbox" aria-autocomplete="list" /></li>');this.$searchContainer=c,this.$search=c.find("input");var d=b.call(this);return this._transferTabIndex(),d},d.prototype.bind=function(a,b,d){var e=this;a.call(this,b,d),b.on("open",function(){e.$search.trigger("focus")}),b.on("close",function(){e.$search.val(""),e.$search.removeAttr("aria-activedescendant"),e.$search.trigger("focus")}),b.on("enable",function(){e.$search.prop("disabled",!1),e._transferTabIndex()}),b.on("disable",function(){e.$search.prop("disabled",!0)}),b.on("focus",function(a){e.$search.trigger("focus")}),b.on("results:focus",function(a){e.$search.attr("aria-activedescendant",a.id)}),this.$selection.on("focusin",".select2-search--inline",function(a){e.trigger("focus",a)}),this.$selection.on("focusout",".select2-search--inline",function(a){e._handleBlur(a)}),this.$selection.on("keydown",".select2-search--inline",function(a){a.stopPropagation(),e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented();var b=a.which;if(b===c.BACKSPACE&&""===e.$search.val()){var d=e.$searchContainer.prev(".select2-selection__choice");if(d.length>0){var f=d.data("data");e.searchRemoveChoice(f),a.preventDefault()}}});var f=document.documentMode,g=f&&11>=f;this.$selection.on("input.searchcheck",".select2-search--inline",function(a){return g?void e.$selection.off("input.search input.searchcheck"):void e.$selection.off("keyup.search")}),this.$selection.on("keyup.search input.search",".select2-search--inline",function(a){if(g&&"input"===a.type)return void e.$selection.off("input.search input.searchcheck");var b=a.which;b!=c.SHIFT&&b!=c.CTRL&&b!=c.ALT&&b!=c.TAB&&e.handleSearch(a)})},d.prototype._transferTabIndex=function(a){this.$search.attr("tabindex",this.$selection.attr("tabindex")),this.$selection.attr("tabindex","-1")},d.prototype.createPlaceholder=function(a,b){this.$search.attr("placeholder",b.text)},d.prototype.update=function(a,b){var c=this.$search[0]==document.activeElement;this.$search.attr("placeholder",""),a.call(this,b),this.$selection.find(".select2-selection__rendered").append(this.$searchContainer),this.resizeSearch(),c&&this.$search.focus()},d.prototype.handleSearch=function(){if(this.resizeSearch(),!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},d.prototype.searchRemoveChoice=function(a,b){this.trigger("unselect",{data:b}),this.$search.val(b.text),this.handleSearch()},d.prototype.resizeSearch=function(){this.$search.css("width","25px");var a="";if(""!==this.$search.attr("placeholder"))a=this.$selection.find(".select2-selection__rendered").innerWidth();else{var b=this.$search.val().length+1;a=.75*b+"em"}this.$search.css("width",a)},d}),b.define("select2/selection/eventRelay",["jquery"],function(a){function b(){}return b.prototype.bind=function(b,c,d){var e=this,f=["open","opening","close","closing","select","selecting","unselect","unselecting"],g=["opening","closing","selecting","unselecting"];b.call(this,c,d),c.on("*",function(b,c){if(-1!==a.inArray(b,f)){c=c||{};var d=a.Event("select2:"+b,{params:c});e.$element.trigger(d),-1!==a.inArray(b,g)&&(c.prevented=d.isDefaultPrevented())}})},b}),b.define("select2/translation",["jquery","require"],function(a,b){function c(a){this.dict=a||{}}return c.prototype.all=function(){return this.dict},c.prototype.get=function(a){return this.dict[a]},c.prototype.extend=function(b){this.dict=a.extend({},b.all(),this.dict)},c._cache={},c.loadPath=function(a){if(!(a in c._cache)){var d=b(a);c._cache[a]=d}return new c(c._cache[a])},c}),b.define("select2/diacritics",[],function(){var a={"Ⓐ":"A","A":"A","À":"A","Á":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ā":"A","Ă":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ǡ":"A","Ä":"A","Ǟ":"A","Ả":"A","Å":"A","Ǻ":"A","Ǎ":"A","Ȁ":"A","Ȃ":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ą":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ǣ":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ⓑ":"B","B":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Ƃ":"B","Ɓ":"B","Ⓒ":"C","C":"C","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","Ç":"C","Ḉ":"C","Ƈ":"C","Ȼ":"C","Ꜿ":"C","Ⓓ":"D","D":"D","Ḋ":"D","Ď":"D","Ḍ":"D","Ḑ":"D","Ḓ":"D","Ḏ":"D","Đ":"D","Ƌ":"D","Ɗ":"D","Ɖ":"D","Ꝺ":"D","DZ":"DZ","DŽ":"DZ","Dz":"Dz","Dž":"Dz","Ⓔ":"E","E":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ē":"E","Ḕ":"E","Ḗ":"E","Ĕ":"E","Ė":"E","Ë":"E","Ẻ":"E","Ě":"E","Ȅ":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Ɛ":"E","Ǝ":"E","Ⓕ":"F","F":"F","Ḟ":"F","Ƒ":"F","Ꝼ":"F","Ⓖ":"G","G":"G","Ǵ":"G","Ĝ":"G","Ḡ":"G","Ğ":"G","Ġ":"G","Ǧ":"G","Ģ":"G","Ǥ":"G","Ɠ":"G","Ꞡ":"G","Ᵹ":"G","Ꝿ":"G","Ⓗ":"H","H":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Ȟ":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","Ⱨ":"H","Ⱶ":"H","Ɥ":"H","Ⓘ":"I","I":"I","Ì":"I","Í":"I","Î":"I","Ĩ":"I","Ī":"I","Ĭ":"I","İ":"I","Ï":"I","Ḯ":"I","Ỉ":"I","Ǐ":"I","Ȉ":"I","Ȋ":"I","Ị":"I","Į":"I","Ḭ":"I","Ɨ":"I","Ⓙ":"J","J":"J","Ĵ":"J","Ɉ":"J","Ⓚ":"K","K":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","Ꝁ":"K","Ꝃ":"K","Ꝅ":"K","Ꞣ":"K","Ⓛ":"L","L":"L","Ŀ":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ļ":"L","Ḽ":"L","Ḻ":"L","Ł":"L","Ƚ":"L","Ɫ":"L","Ⱡ":"L","Ꝉ":"L","Ꝇ":"L","Ꞁ":"L","LJ":"LJ","Lj":"Lj","Ⓜ":"M","M":"M","Ḿ":"M","Ṁ":"M","Ṃ":"M","Ɱ":"M","Ɯ":"M","Ⓝ":"N","N":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Ņ":"N","Ṋ":"N","Ṉ":"N","Ƞ":"N","Ɲ":"N","Ꞑ":"N","Ꞥ":"N","NJ":"NJ","Nj":"Nj","Ⓞ":"O","O":"O","Ò":"O","Ó":"O","Ô":"O","Ồ":"O","Ố":"O","Ỗ":"O","Ổ":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","Ō":"O","Ṑ":"O","Ṓ":"O","Ŏ":"O","Ȯ":"O","Ȱ":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Ő":"O","Ǒ":"O","Ȍ":"O","Ȏ":"O","Ơ":"O","Ờ":"O","Ớ":"O","Ỡ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","Ɵ":"O","Ꝋ":"O","Ꝍ":"O","Ƣ":"OI","Ꝏ":"OO","Ȣ":"OU","Ⓟ":"P","P":"P","Ṕ":"P","Ṗ":"P","Ƥ":"P","Ᵽ":"P","Ꝑ":"P","Ꝓ":"P","Ꝕ":"P","Ⓠ":"Q","Q":"Q","Ꝗ":"Q","Ꝙ":"Q","Ɋ":"Q","Ⓡ":"R","R":"R","Ŕ":"R","Ṙ":"R","Ř":"R","Ȑ":"R","Ȓ":"R","Ṛ":"R","Ṝ":"R","Ŗ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꝛ":"R","Ꞧ":"R","Ꞃ":"R","Ⓢ":"S","S":"S","ẞ":"S","Ś":"S","Ṥ":"S","Ŝ":"S","Ṡ":"S","Š":"S","Ṧ":"S","Ṣ":"S","Ṩ":"S","Ș":"S","Ş":"S","Ȿ":"S","Ꞩ":"S","Ꞅ":"S","Ⓣ":"T","T":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Ț":"T","Ţ":"T","Ṱ":"T","Ṯ":"T","Ŧ":"T","Ƭ":"T","Ʈ":"T","Ⱦ":"T","Ꞇ":"T","Ꜩ":"TZ","Ⓤ":"U","U":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ü":"U","Ǜ":"U","Ǘ":"U","Ǖ":"U","Ǚ":"U","Ủ":"U","Ů":"U","Ű":"U","Ǔ":"U","Ȕ":"U","Ȗ":"U","Ư":"U","Ừ":"U","Ứ":"U","Ữ":"U","Ử":"U","Ự":"U","Ụ":"U","Ṳ":"U","Ų":"U","Ṷ":"U","Ṵ":"U","Ʉ":"U","Ⓥ":"V","V":"V","Ṽ":"V","Ṿ":"V","Ʋ":"V","Ꝟ":"V","Ʌ":"V","Ꝡ":"VY","Ⓦ":"W","W":"W","Ẁ":"W","Ẃ":"W","Ŵ":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","Ⱳ":"W","Ⓧ":"X","X":"X","Ẋ":"X","Ẍ":"X","Ⓨ":"Y","Y":"Y","Ỳ":"Y","Ý":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","Ỷ":"Y","Ỵ":"Y","Ƴ":"Y","Ɏ":"Y","Ỿ":"Y","Ⓩ":"Z","Z":"Z","Ź":"Z","Ẑ":"Z","Ż":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","Ꝣ":"Z","ⓐ":"a","a":"a","ẚ":"a","à":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","ā":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","ǡ":"a","ä":"a","ǟ":"a","ả":"a","å":"a","ǻ":"a","ǎ":"a","ȁ":"a","ȃ":"a","ạ":"a","ậ":"a","ặ":"a","ḁ":"a","ą":"a","ⱥ":"a","ɐ":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","ǣ":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ⓑ":"b","b":"b","ḃ":"b","ḅ":"b","ḇ":"b","ƀ":"b","ƃ":"b","ɓ":"b","ⓒ":"c","c":"c","ć":"c","ĉ":"c","ċ":"c","č":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","ⓓ":"d","d":"d","ḋ":"d","ď":"d","ḍ":"d","ḑ":"d","ḓ":"d","ḏ":"d","đ":"d","ƌ":"d","ɖ":"d","ɗ":"d","ꝺ":"d","dz":"dz","dž":"dz","ⓔ":"e","e":"e","è":"e","é":"e","ê":"e","ề":"e","ế":"e","ễ":"e","ể":"e","ẽ":"e","ē":"e","ḕ":"e","ḗ":"e","ĕ":"e","ė":"e","ë":"e","ẻ":"e","ě":"e","ȅ":"e","ȇ":"e","ẹ":"e","ệ":"e","ȩ":"e","ḝ":"e","ę":"e","ḙ":"e","ḛ":"e","ɇ":"e","ɛ":"e","ǝ":"e","ⓕ":"f","f":"f","ḟ":"f","ƒ":"f","ꝼ":"f","ⓖ":"g","g":"g","ǵ":"g","ĝ":"g","ḡ":"g","ğ":"g","ġ":"g","ǧ":"g","ģ":"g","ǥ":"g","ɠ":"g","ꞡ":"g","ᵹ":"g","ꝿ":"g","ⓗ":"h","h":"h","ĥ":"h","ḣ":"h","ḧ":"h","ȟ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","ⱶ":"h","ɥ":"h","ƕ":"hv","ⓘ":"i","i":"i","ì":"i","í":"i","î":"i","ĩ":"i","ī":"i","ĭ":"i","ï":"i","ḯ":"i","ỉ":"i","ǐ":"i","ȉ":"i","ȋ":"i","ị":"i","į":"i","ḭ":"i","ɨ":"i","ı":"i","ⓙ":"j","j":"j","ĵ":"j","ǰ":"j","ɉ":"j","ⓚ":"k","k":"k","ḱ":"k","ǩ":"k","ḳ":"k","ķ":"k","ḵ":"k","ƙ":"k","ⱪ":"k","ꝁ":"k","ꝃ":"k","ꝅ":"k","ꞣ":"k","ⓛ":"l","l":"l","ŀ":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","ſ":"l","ł":"l","ƚ":"l","ɫ":"l","ⱡ":"l","ꝉ":"l","ꞁ":"l","ꝇ":"l","lj":"lj","ⓜ":"m","m":"m","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ɯ":"m","ⓝ":"n","n":"n","ǹ":"n","ń":"n","ñ":"n","ṅ":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","ƞ":"n","ɲ":"n","ʼn":"n","ꞑ":"n","ꞥ":"n","nj":"nj","ⓞ":"o","o":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","ỗ":"o","ổ":"o","õ":"o","ṍ":"o","ȭ":"o","ṏ":"o","ō":"o","ṑ":"o","ṓ":"o","ŏ":"o","ȯ":"o","ȱ":"o","ö":"o","ȫ":"o","ỏ":"o","ő":"o","ǒ":"o","ȍ":"o","ȏ":"o","ơ":"o","ờ":"o","ớ":"o","ỡ":"o","ở":"o","ợ":"o","ọ":"o","ộ":"o","ǫ":"o","ǭ":"o","ø":"o","ǿ":"o","ɔ":"o","ꝋ":"o","ꝍ":"o","ɵ":"o","ƣ":"oi","ȣ":"ou","ꝏ":"oo","ⓟ":"p","p":"p","ṕ":"p","ṗ":"p","ƥ":"p","ᵽ":"p","ꝑ":"p","ꝓ":"p","ꝕ":"p","ⓠ":"q","q":"q","ɋ":"q","ꝗ":"q","ꝙ":"q","ⓡ":"r","r":"r","ŕ":"r","ṙ":"r","ř":"r","ȑ":"r","ȓ":"r","ṛ":"r","ṝ":"r","ŗ":"r","ṟ":"r","ɍ":"r","ɽ":"r","ꝛ":"r","ꞧ":"r","ꞃ":"r","ⓢ":"s","s":"s","ß":"s","ś":"s","ṥ":"s","ŝ":"s","ṡ":"s","š":"s","ṧ":"s","ṣ":"s","ṩ":"s","ș":"s","ş":"s","ȿ":"s","ꞩ":"s","ꞅ":"s","ẛ":"s","ⓣ":"t","t":"t","ṫ":"t","ẗ":"t","ť":"t","ṭ":"t","ț":"t","ţ":"t","ṱ":"t","ṯ":"t","ŧ":"t","ƭ":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","ꜩ":"tz","ⓤ":"u","u":"u","ù":"u","ú":"u","û":"u","ũ":"u","ṹ":"u","ū":"u","ṻ":"u","ŭ":"u","ü":"u","ǜ":"u","ǘ":"u","ǖ":"u","ǚ":"u","ủ":"u","ů":"u","ű":"u","ǔ":"u","ȕ":"u","ȗ":"u","ư":"u","ừ":"u","ứ":"u","ữ":"u","ử":"u","ự":"u","ụ":"u","ṳ":"u","ų":"u","ṷ":"u","ṵ":"u","ʉ":"u","ⓥ":"v","v":"v","ṽ":"v","ṿ":"v","ʋ":"v","ꝟ":"v","ʌ":"v","ꝡ":"vy","ⓦ":"w","w":"w","ẁ":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","ⱳ":"w","ⓧ":"x","x":"x","ẋ":"x","ẍ":"x","ⓨ":"y","y":"y","ỳ":"y","ý":"y","ŷ":"y","ỹ":"y","ȳ":"y","ẏ":"y","ÿ":"y","ỷ":"y","ẙ":"y","ỵ":"y","ƴ":"y","ɏ":"y","ỿ":"y","ⓩ":"z","z":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","ȥ":"z","ɀ":"z","ⱬ":"z","ꝣ":"z","Ά":"Α","Έ":"Ε","Ή":"Η","Ί":"Ι","Ϊ":"Ι","Ό":"Ο","Ύ":"Υ","Ϋ":"Υ","Ώ":"Ω","ά":"α","έ":"ε","ή":"η","ί":"ι","ϊ":"ι","ΐ":"ι","ό":"ο","ύ":"υ","ϋ":"υ","ΰ":"υ","ω":"ω","ς":"σ"};return a}),b.define("select2/data/base",["../utils"],function(a){function b(a,c){b.__super__.constructor.call(this)}return a.Extend(b,a.Observable),b.prototype.current=function(a){throw new Error("The `current` method must be defined in child classes.")},b.prototype.query=function(a,b){throw new Error("The `query` method must be defined in child classes.")},b.prototype.bind=function(a,b){},b.prototype.destroy=function(){},b.prototype.generateResultId=function(b,c){var d=b.id+"-result-";return d+=a.generateChars(4),d+=null!=c.id?"-"+c.id.toString():"-"+a.generateChars(4)},b}),b.define("select2/data/select",["./base","../utils","jquery"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,a),d.prototype.current=function(a){var b=[],d=this;this.$element.find(":selected").each(function(){var a=c(this),e=d.item(a);b.push(e)}),a(b)},d.prototype.select=function(a){var b=this;if(a.selected=!0,c(a.element).is("option"))return a.element.selected=!0,void this.$element.trigger("change");
2
+ if(this.$element.prop("multiple"))this.current(function(d){var e=[];a=[a],a.push.apply(a,d);for(var f=0;f<a.length;f++){var g=a[f].id;-1===c.inArray(g,e)&&e.push(g)}b.$element.val(e),b.$element.trigger("change")});else{var d=a.id;this.$element.val(d),this.$element.trigger("change")}},d.prototype.unselect=function(a){var b=this;if(this.$element.prop("multiple"))return a.selected=!1,c(a.element).is("option")?(a.element.selected=!1,void this.$element.trigger("change")):void this.current(function(d){for(var e=[],f=0;f<d.length;f++){var g=d[f].id;g!==a.id&&-1===c.inArray(g,e)&&e.push(g)}b.$element.val(e),b.$element.trigger("change")})},d.prototype.bind=function(a,b){var c=this;this.container=a,a.on("select",function(a){c.select(a.data)}),a.on("unselect",function(a){c.unselect(a.data)})},d.prototype.destroy=function(){this.$element.find("*").each(function(){c.removeData(this,"data")})},d.prototype.query=function(a,b){var d=[],e=this,f=this.$element.children();f.each(function(){var b=c(this);if(b.is("option")||b.is("optgroup")){var f=e.item(b),g=e.matches(a,f);null!==g&&d.push(g)}}),b({results:d})},d.prototype.addOptions=function(a){b.appendMany(this.$element,a)},d.prototype.option=function(a){var b;a.children?(b=document.createElement("optgroup"),b.label=a.text):(b=document.createElement("option"),void 0!==b.textContent?b.textContent=a.text:b.innerText=a.text),a.id&&(b.value=a.id),a.disabled&&(b.disabled=!0),a.selected&&(b.selected=!0),a.title&&(b.title=a.title);var d=c(b),e=this._normalizeItem(a);return e.element=b,c.data(b,"data",e),d},d.prototype.item=function(a){var b={};if(b=c.data(a[0],"data"),null!=b)return b;if(a.is("option"))b={id:a.val(),text:a.text(),disabled:a.prop("disabled"),selected:a.prop("selected"),title:a.prop("title")};else if(a.is("optgroup")){b={text:a.prop("label"),children:[],title:a.prop("title")};for(var d=a.children("option"),e=[],f=0;f<d.length;f++){var g=c(d[f]),h=this.item(g);e.push(h)}b.children=e}return b=this._normalizeItem(b),b.element=a[0],c.data(a[0],"data",b),b},d.prototype._normalizeItem=function(a){c.isPlainObject(a)||(a={id:a,text:a}),a=c.extend({},{text:""},a);var b={selected:!1,disabled:!1};return null!=a.id&&(a.id=a.id.toString()),null!=a.text&&(a.text=a.text.toString()),null==a._resultId&&a.id&&null!=this.container&&(a._resultId=this.generateResultId(this.container,a)),c.extend({},b,a)},d.prototype.matches=function(a,b){var c=this.options.get("matcher");return c(a,b)},d}),b.define("select2/data/array",["./select","../utils","jquery"],function(a,b,c){function d(a,b){var c=b.get("data")||[];d.__super__.constructor.call(this,a,b),this.addOptions(this.convertToOptions(c))}return b.Extend(d,a),d.prototype.select=function(a){var b=this.$element.find("option").filter(function(b,c){return c.value==a.id.toString()});0===b.length&&(b=this.option(a),this.addOptions(b)),d.__super__.select.call(this,a)},d.prototype.convertToOptions=function(a){function d(a){return function(){return c(this).val()==a.id}}for(var e=this,f=this.$element.find("option"),g=f.map(function(){return e.item(c(this)).id}).get(),h=[],i=0;i<a.length;i++){var j=this._normalizeItem(a[i]);if(c.inArray(j.id,g)>=0){var k=f.filter(d(j)),l=this.item(k),m=c.extend(!0,{},j,l),n=this.option(m);k.replaceWith(n)}else{var o=this.option(j);if(j.children){var p=this.convertToOptions(j.children);b.appendMany(o,p)}h.push(o)}}return h},d}),b.define("select2/data/ajax",["./array","../utils","jquery"],function(a,b,c){function d(a,b){this.ajaxOptions=this._applyDefaults(b.get("ajax")),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),d.__super__.constructor.call(this,a,b)}return b.Extend(d,a),d.prototype._applyDefaults=function(a){var b={data:function(a){return c.extend({},a,{q:a.term})},transport:function(a,b,d){var e=c.ajax(a);return e.then(b),e.fail(d),e}};return c.extend({},b,a,!0)},d.prototype.processResults=function(a){return a},d.prototype.query=function(a,b){function d(){var d=f.transport(f,function(d){var f=e.processResults(d,a);e.options.get("debug")&&window.console&&console.error&&(f&&f.results&&c.isArray(f.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),b(f)},function(){d.status&&"0"===d.status||e.trigger("results:message",{message:"errorLoading"})});e._request=d}var e=this;null!=this._request&&(c.isFunction(this._request.abort)&&this._request.abort(),this._request=null);var f=c.extend({type:"GET"},this.ajaxOptions);"function"==typeof f.url&&(f.url=f.url.call(this.$element,a)),"function"==typeof f.data&&(f.data=f.data.call(this.$element,a)),this.ajaxOptions.delay&&null!=a.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(d,this.ajaxOptions.delay)):d()},d}),b.define("select2/data/tags",["jquery"],function(a){function b(b,c,d){var e=d.get("tags"),f=d.get("createTag");void 0!==f&&(this.createTag=f);var g=d.get("insertTag");if(void 0!==g&&(this.insertTag=g),b.call(this,c,d),a.isArray(e))for(var h=0;h<e.length;h++){var i=e[h],j=this._normalizeItem(i),k=this.option(j);this.$element.append(k)}}return b.prototype.query=function(a,b,c){function d(a,f){for(var g=a.results,h=0;h<g.length;h++){var i=g[h],j=null!=i.children&&!d({results:i.children},!0),k=i.text===b.term;if(k||j)return f?!1:(a.data=g,void c(a))}if(f)return!0;var l=e.createTag(b);if(null!=l){var m=e.option(l);m.attr("data-select2-tag",!0),e.addOptions([m]),e.insertTag(g,l)}a.results=g,c(a)}var e=this;return this._removeOldTags(),null==b.term||null!=b.page?void a.call(this,b,c):void a.call(this,b,d)},b.prototype.createTag=function(b,c){var d=a.trim(c.term);return""===d?null:{id:d,text:d}},b.prototype.insertTag=function(a,b,c){b.unshift(c)},b.prototype._removeOldTags=function(b){var c=(this._lastTag,this.$element.find("option[data-select2-tag]"));c.each(function(){this.selected||a(this).remove()})},b}),b.define("select2/data/tokenizer",["jquery"],function(a){function b(a,b,c){var d=c.get("tokenizer");void 0!==d&&(this.tokenizer=d),a.call(this,b,c)}return b.prototype.bind=function(a,b,c){a.call(this,b,c),this.$search=b.dropdown.$search||b.selection.$search||c.find(".select2-search__field")},b.prototype.query=function(b,c,d){function e(b){var c=g._normalizeItem(b),d=g.$element.find("option").filter(function(){return a(this).val()===c.id});if(!d.length){var e=g.option(c);e.attr("data-select2-tag",!0),g._removeOldTags(),g.addOptions([e])}f(c)}function f(a){g.trigger("select",{data:a})}var g=this;c.term=c.term||"";var h=this.tokenizer(c,this.options,e);h.term!==c.term&&(this.$search.length&&(this.$search.val(h.term),this.$search.focus()),c.term=h.term),b.call(this,c,d)},b.prototype.tokenizer=function(b,c,d,e){for(var f=d.get("tokenSeparators")||[],g=c.term,h=0,i=this.createTag||function(a){return{id:a.term,text:a.term}};h<g.length;){var j=g[h];if(-1!==a.inArray(j,f)){var k=g.substr(0,h),l=a.extend({},c,{term:k}),m=i(l);null!=m?(e(m),g=g.substr(h+1)||"",h=0):h++}else h++}return{term:g}},b}),b.define("select2/data/minimumInputLength",[],function(){function a(a,b,c){this.minimumInputLength=c.get("minimumInputLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){return b.term=b.term||"",b.term.length<this.minimumInputLength?void this.trigger("results:message",{message:"inputTooShort",args:{minimum:this.minimumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumInputLength",[],function(){function a(a,b,c){this.maximumInputLength=c.get("maximumInputLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){return b.term=b.term||"",this.maximumInputLength>0&&b.term.length>this.maximumInputLength?void this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumSelectionLength",[],function(){function a(a,b,c){this.maximumSelectionLength=c.get("maximumSelectionLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){var d=this;this.current(function(e){var f=null!=e?e.length:0;return d.maximumSelectionLength>0&&f>=d.maximumSelectionLength?void d.trigger("results:message",{message:"maximumSelected",args:{maximum:d.maximumSelectionLength}}):void a.call(d,b,c)})},a}),b.define("select2/dropdown",["jquery","./utils"],function(a,b){function c(a,b){this.$element=a,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('<span class="select2-dropdown"><span class="select2-results"></span></span>');return b.attr("dir",this.options.get("dir")),this.$dropdown=b,b},c.prototype.bind=function(){},c.prototype.position=function(a,b){},c.prototype.destroy=function(){this.$dropdown.remove()},c}),b.define("select2/dropdown/search",["jquery","../utils"],function(a,b){function c(){}return c.prototype.render=function(b){var c=b.call(this),d=a('<span class="select2-search select2-search--dropdown"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" role="textbox" /></span>');return this.$searchContainer=d,this.$search=d.find("input"),c.prepend(d),c},c.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),this.$search.on("keydown",function(a){e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented()}),this.$search.on("input",function(b){a(this).off("keyup")}),this.$search.on("keyup input",function(a){e.handleSearch(a)}),c.on("open",function(){e.$search.attr("tabindex",0),e.$search.focus(),window.setTimeout(function(){e.$search.focus()},0)}),c.on("close",function(){e.$search.attr("tabindex",-1),e.$search.val("")}),c.on("focus",function(){c.isOpen()&&e.$search.focus()}),c.on("results:all",function(a){if(null==a.query.term||""===a.query.term){var b=e.showSearch(a);b?e.$searchContainer.removeClass("select2-search--hide"):e.$searchContainer.addClass("select2-search--hide")}})},c.prototype.handleSearch=function(a){if(!this._keyUpPrevented){var b=this.$search.val();this.trigger("query",{term:b})}this._keyUpPrevented=!1},c.prototype.showSearch=function(a,b){return!0},c}),b.define("select2/dropdown/hidePlaceholder",[],function(){function a(a,b,c,d){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c,d)}return a.prototype.append=function(a,b){b.results=this.removePlaceholder(b.results),a.call(this,b)},a.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},a.prototype.removePlaceholder=function(a,b){for(var c=b.slice(0),d=b.length-1;d>=0;d--){var e=b[d];this.placeholder.id===e.id&&c.splice(d,1)}return c},a}),b.define("select2/dropdown/infiniteScroll",["jquery"],function(a){function b(a,b,c,d){this.lastParams={},a.call(this,b,c,d),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return b.prototype.append=function(a,b){this.$loadingMore.remove(),this.loading=!1,a.call(this,b),this.showLoadingMore(b)&&this.$results.append(this.$loadingMore)},b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),c.on("query",function(a){e.lastParams=a,e.loading=!0}),c.on("query:append",function(a){e.lastParams=a,e.loading=!0}),this.$results.on("scroll",function(){var b=a.contains(document.documentElement,e.$loadingMore[0]);if(!e.loading&&b){var c=e.$results.offset().top+e.$results.outerHeight(!1),d=e.$loadingMore.offset().top+e.$loadingMore.outerHeight(!1);c+50>=d&&e.loadMore()}})},b.prototype.loadMore=function(){this.loading=!0;var b=a.extend({},{page:1},this.lastParams);b.page++,this.trigger("query:append",b)},b.prototype.showLoadingMore=function(a,b){return b.pagination&&b.pagination.more},b.prototype.createLoadingMore=function(){var b=a('<li class="select2-results__option select2-results__option--load-more"role="treeitem" aria-disabled="true"></li>'),c=this.options.get("translations").get("loadingMore");return b.html(c(this.lastParams)),b},b}),b.define("select2/dropdown/attachBody",["jquery","../utils"],function(a,b){function c(b,c,d){this.$dropdownParent=d.get("dropdownParent")||a(document.body),b.call(this,c,d)}return c.prototype.bind=function(a,b,c){var d=this,e=!1;a.call(this,b,c),b.on("open",function(){d._showDropdown(),d._attachPositioningHandler(b),e||(e=!0,b.on("results:all",function(){d._positionDropdown(),d._resizeDropdown()}),b.on("results:append",function(){d._positionDropdown(),d._resizeDropdown()}))}),b.on("close",function(){d._hideDropdown(),d._detachPositioningHandler(b)}),this.$dropdownContainer.on("mousedown",function(a){a.stopPropagation()})},c.prototype.destroy=function(a){a.call(this),this.$dropdownContainer.remove()},c.prototype.position=function(a,b,c){b.attr("class",c.attr("class")),b.removeClass("select2"),b.addClass("select2-container--open"),b.css({position:"absolute",top:-999999}),this.$container=c},c.prototype.render=function(b){var c=a("<span></span>"),d=b.call(this);return c.append(d),this.$dropdownContainer=c,c},c.prototype._hideDropdown=function(a){this.$dropdownContainer.detach()},c.prototype._attachPositioningHandler=function(c,d){var e=this,f="scroll.select2."+d.id,g="resize.select2."+d.id,h="orientationchange.select2."+d.id,i=this.$container.parents().filter(b.hasScroll);i.each(function(){a(this).data("select2-scroll-position",{x:a(this).scrollLeft(),y:a(this).scrollTop()})}),i.on(f,function(b){var c=a(this).data("select2-scroll-position");a(this).scrollTop(c.y)}),a(window).on(f+" "+g+" "+h,function(a){e._positionDropdown(),e._resizeDropdown()})},c.prototype._detachPositioningHandler=function(c,d){var e="scroll.select2."+d.id,f="resize.select2."+d.id,g="orientationchange.select2."+d.id,h=this.$container.parents().filter(b.hasScroll);h.off(e),a(window).off(e+" "+f+" "+g)},c.prototype._positionDropdown=function(){var b=a(window),c=this.$dropdown.hasClass("select2-dropdown--above"),d=this.$dropdown.hasClass("select2-dropdown--below"),e=null,f=this.$container.offset();f.bottom=f.top+this.$container.outerHeight(!1);var g={height:this.$container.outerHeight(!1)};g.top=f.top,g.bottom=f.top+g.height;var h={height:this.$dropdown.outerHeight(!1)},i={top:b.scrollTop(),bottom:b.scrollTop()+b.height()},j=i.top<f.top-h.height,k=i.bottom>f.bottom+h.height,l={left:f.left,top:g.bottom},m=this.$dropdownParent;"static"===m.css("position")&&(m=m.offsetParent());var n=m.offset();l.top-=n.top,l.left-=n.left,c||d||(e="below"),k||!j||c?!j&&k&&c&&(e="below"):e="above",("above"==e||c&&"below"!==e)&&(l.top=g.top-n.top-h.height),null!=e&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+e),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+e)),this.$dropdownContainer.css(l)},c.prototype._resizeDropdown=function(){var a={width:this.$container.outerWidth(!1)+"px"};this.options.get("dropdownAutoWidth")&&(a.minWidth=a.width,a.position="relative",a.width="auto"),this.$dropdown.css(a)},c.prototype._showDropdown=function(a){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},c}),b.define("select2/dropdown/minimumResultsForSearch",[],function(){function a(b){for(var c=0,d=0;d<b.length;d++){var e=b[d];e.children?c+=a(e.children):c++}return c}function b(a,b,c,d){this.minimumResultsForSearch=c.get("minimumResultsForSearch"),this.minimumResultsForSearch<0&&(this.minimumResultsForSearch=1/0),a.call(this,b,c,d)}return b.prototype.showSearch=function(b,c){return a(c.data.results)<this.minimumResultsForSearch?!1:b.call(this,c)},b}),b.define("select2/dropdown/selectOnClose",[],function(){function a(){}return a.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),b.on("close",function(a){d._handleSelectOnClose(a)})},a.prototype._handleSelectOnClose=function(a,b){if(b&&null!=b.originalSelect2Event){var c=b.originalSelect2Event;if("select"===c._type||"unselect"===c._type)return}var d=this.getHighlightedResults();if(!(d.length<1)){var e=d.data("data");null!=e.element&&e.element.selected||null==e.element&&e.selected||this.trigger("select",{data:e})}},a}),b.define("select2/dropdown/closeOnSelect",[],function(){function a(){}return a.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),b.on("select",function(a){d._selectTriggered(a)}),b.on("unselect",function(a){d._selectTriggered(a)})},a.prototype._selectTriggered=function(a,b){var c=b.originalEvent;c&&c.ctrlKey||this.trigger("close",{originalEvent:c,originalSelect2Event:b})},a}),b.define("select2/i18n/en",[],function(){return{errorLoading:function(){return"The results could not be loaded."},inputTooLong:function(a){var b=a.input.length-a.maximum,c="Please delete "+b+" character";return 1!=b&&(c+="s"),c},inputTooShort:function(a){var b=a.minimum-a.input.length,c="Please enter "+b+" or more characters";return c},loadingMore:function(){return"Loading more results…"},maximumSelected:function(a){var b="You can only select "+a.maximum+" item";return 1!=a.maximum&&(b+="s"),b},noResults:function(){return"No results found"},searching:function(){return"Searching…"}}}),b.define("select2/defaults",["jquery","require","./results","./selection/single","./selection/multiple","./selection/placeholder","./selection/allowClear","./selection/search","./selection/eventRelay","./utils","./translation","./diacritics","./data/select","./data/array","./data/ajax","./data/tags","./data/tokenizer","./data/minimumInputLength","./data/maximumInputLength","./data/maximumSelectionLength","./dropdown","./dropdown/search","./dropdown/hidePlaceholder","./dropdown/infiniteScroll","./dropdown/attachBody","./dropdown/minimumResultsForSearch","./dropdown/selectOnClose","./dropdown/closeOnSelect","./i18n/en"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C){function D(){this.reset()}D.prototype.apply=function(l){if(l=a.extend(!0,{},this.defaults,l),null==l.dataAdapter){if(null!=l.ajax?l.dataAdapter=o:null!=l.data?l.dataAdapter=n:l.dataAdapter=m,l.minimumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,r)),l.maximumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,s)),l.maximumSelectionLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,t)),l.tags&&(l.dataAdapter=j.Decorate(l.dataAdapter,p)),(null!=l.tokenSeparators||null!=l.tokenizer)&&(l.dataAdapter=j.Decorate(l.dataAdapter,q)),null!=l.query){var C=b(l.amdBase+"compat/query");l.dataAdapter=j.Decorate(l.dataAdapter,C)}if(null!=l.initSelection){var D=b(l.amdBase+"compat/initSelection");l.dataAdapter=j.Decorate(l.dataAdapter,D)}}if(null==l.resultsAdapter&&(l.resultsAdapter=c,null!=l.ajax&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,x)),null!=l.placeholder&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,w)),l.selectOnClose&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,A))),null==l.dropdownAdapter){if(l.multiple)l.dropdownAdapter=u;else{var E=j.Decorate(u,v);l.dropdownAdapter=E}if(0!==l.minimumResultsForSearch&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,z)),l.closeOnSelect&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,B)),null!=l.dropdownCssClass||null!=l.dropdownCss||null!=l.adaptDropdownCssClass){var F=b(l.amdBase+"compat/dropdownCss");l.dropdownAdapter=j.Decorate(l.dropdownAdapter,F)}l.dropdownAdapter=j.Decorate(l.dropdownAdapter,y)}if(null==l.selectionAdapter){if(l.multiple?l.selectionAdapter=e:l.selectionAdapter=d,null!=l.placeholder&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,f)),l.allowClear&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,g)),l.multiple&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,h)),null!=l.containerCssClass||null!=l.containerCss||null!=l.adaptContainerCssClass){var G=b(l.amdBase+"compat/containerCss");l.selectionAdapter=j.Decorate(l.selectionAdapter,G)}l.selectionAdapter=j.Decorate(l.selectionAdapter,i)}if("string"==typeof l.language)if(l.language.indexOf("-")>0){var H=l.language.split("-"),I=H[0];l.language=[l.language,I]}else l.language=[l.language];if(a.isArray(l.language)){var J=new k;l.language.push("en");for(var K=l.language,L=0;L<K.length;L++){var M=K[L],N={};try{N=k.loadPath(M)}catch(O){try{M=this.defaults.amdLanguageBase+M,N=k.loadPath(M)}catch(P){l.debug&&window.console&&console.warn&&console.warn('Select2: The language file for "'+M+'" could not be automatically loaded. A fallback will be used instead.');continue}}J.extend(N)}l.translations=J}else{var Q=k.loadPath(this.defaults.amdLanguageBase+"en"),R=new k(l.language);R.extend(Q),l.translations=R}return l},D.prototype.reset=function(){function b(a){function b(a){return l[a]||a}return a.replace(/[^\u0000-\u007E]/g,b)}function c(d,e){if(""===a.trim(d.term))return e;if(e.children&&e.children.length>0){for(var f=a.extend(!0,{},e),g=e.children.length-1;g>=0;g--){var h=e.children[g],i=c(d,h);null==i&&f.children.splice(g,1)}return f.children.length>0?f:c(d,f)}var j=b(e.text).toUpperCase(),k=b(d.term).toUpperCase();return j.indexOf(k)>-1?e:null}this.defaults={amdBase:"./",amdLanguageBase:"./i18n/",closeOnSelect:!0,debug:!1,dropdownAutoWidth:!1,escapeMarkup:j.escapeMarkup,language:C,matcher:c,minimumInputLength:0,maximumInputLength:0,maximumSelectionLength:0,minimumResultsForSearch:0,selectOnClose:!1,sorter:function(a){return a},templateResult:function(a){return a.text},templateSelection:function(a){return a.text},theme:"default",width:"resolve"}},D.prototype.set=function(b,c){var d=a.camelCase(b),e={};e[d]=c;var f=j._convertData(e);a.extend(this.defaults,f)};var E=new D;return E}),b.define("select2/options",["require","jquery","./defaults","./utils"],function(a,b,c,d){function e(b,e){if(this.options=b,null!=e&&this.fromElement(e),this.options=c.apply(this.options),e&&e.is("input")){var f=a(this.get("amdBase")+"compat/inputData");this.options.dataAdapter=d.Decorate(this.options.dataAdapter,f)}}return e.prototype.fromElement=function(a){var c=["select2"];null==this.options.multiple&&(this.options.multiple=a.prop("multiple")),null==this.options.disabled&&(this.options.disabled=a.prop("disabled")),null==this.options.language&&(a.prop("lang")?this.options.language=a.prop("lang").toLowerCase():a.closest("[lang]").prop("lang")&&(this.options.language=a.closest("[lang]").prop("lang"))),null==this.options.dir&&(a.prop("dir")?this.options.dir=a.prop("dir"):a.closest("[dir]").prop("dir")?this.options.dir=a.closest("[dir]").prop("dir"):this.options.dir="ltr"),a.prop("disabled",this.options.disabled),a.prop("multiple",this.options.multiple),a.data("select2Tags")&&(this.options.debug&&window.console&&console.warn&&console.warn('Select2: The `data-select2-tags` attribute has been changed to use the `data-data` and `data-tags="true"` attributes and will be removed in future versions of Select2.'),a.data("data",a.data("select2Tags")),a.data("tags",!0)),a.data("ajaxUrl")&&(this.options.debug&&window.console&&console.warn&&console.warn("Select2: The `data-ajax-url` attribute has been changed to `data-ajax--url` and support for the old attribute will be removed in future versions of Select2."),a.attr("ajax--url",a.data("ajaxUrl")),a.data("ajax--url",a.data("ajaxUrl")));var e={};e=b.fn.jquery&&"1."==b.fn.jquery.substr(0,2)&&a[0].dataset?b.extend(!0,{},a[0].dataset,a.data()):a.data();var f=b.extend(!0,{},e);f=d._convertData(f);for(var g in f)b.inArray(g,c)>-1||(b.isPlainObject(this.options[g])?b.extend(this.options[g],f[g]):this.options[g]=f[g]);return this},e.prototype.get=function(a){return this.options[a]},e.prototype.set=function(a,b){this.options[a]=b},e}),b.define("select2/core",["jquery","./options","./utils","./keys"],function(a,b,c,d){var e=function(a,c){null!=a.data("select2")&&a.data("select2").destroy(),this.$element=a,this.id=this._generateId(a),c=c||{},this.options=new b(c,a),e.__super__.constructor.call(this);var d=a.attr("tabindex")||0;a.data("old-tabindex",d),a.attr("tabindex","-1");var f=this.options.get("dataAdapter");this.dataAdapter=new f(a,this.options);var g=this.render();this._placeContainer(g);var h=this.options.get("selectionAdapter");this.selection=new h(a,this.options),this.$selection=this.selection.render(),this.selection.position(this.$selection,g);var i=this.options.get("dropdownAdapter");this.dropdown=new i(a,this.options),this.$dropdown=this.dropdown.render(),this.dropdown.position(this.$dropdown,g);var j=this.options.get("resultsAdapter");this.results=new j(a,this.options,this.dataAdapter),this.$results=this.results.render(),this.results.position(this.$results,this.$dropdown);var k=this;this._bindAdapters(),this._registerDomEvents(),this._registerDataEvents(),this._registerSelectionEvents(),this._registerDropdownEvents(),this._registerResultsEvents(),this._registerEvents(),this.dataAdapter.current(function(a){k.trigger("selection:update",{data:a})}),a.addClass("select2-hidden-accessible"),a.attr("aria-hidden","true"),this._syncAttributes(),a.data("select2",this)};return c.Extend(e,c.Observable),e.prototype._generateId=function(a){var b="";return b=null!=a.attr("id")?a.attr("id"):null!=a.attr("name")?a.attr("name")+"-"+c.generateChars(2):c.generateChars(4),b=b.replace(/(:|\.|\[|\]|,)/g,""),b="select2-"+b},e.prototype._placeContainer=function(a){a.insertAfter(this.$element);var b=this._resolveWidth(this.$element,this.options.get("width"));null!=b&&a.css("width",b)},e.prototype._resolveWidth=function(a,b){var c=/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;if("resolve"==b){var d=this._resolveWidth(a,"style");return null!=d?d:this._resolveWidth(a,"element")}if("element"==b){var e=a.outerWidth(!1);return 0>=e?"auto":e+"px"}if("style"==b){var f=a.attr("style");if("string"!=typeof f)return null;for(var g=f.split(";"),h=0,i=g.length;i>h;h+=1){var j=g[h].replace(/\s/g,""),k=j.match(c);if(null!==k&&k.length>=1)return k[1]}return null}return b},e.prototype._bindAdapters=function(){this.dataAdapter.bind(this,this.$container),this.selection.bind(this,this.$container),this.dropdown.bind(this,this.$container),this.results.bind(this,this.$container)},e.prototype._registerDomEvents=function(){var b=this;this.$element.on("change.select2",function(){b.dataAdapter.current(function(a){b.trigger("selection:update",{data:a})})}),this.$element.on("focus.select2",function(a){b.trigger("focus",a)}),this._syncA=c.bind(this._syncAttributes,this),this._syncS=c.bind(this._syncSubtree,this),this.$element[0].attachEvent&&this.$element[0].attachEvent("onpropertychange",this._syncA);var d=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;null!=d?(this._observer=new d(function(c){a.each(c,b._syncA),a.each(c,b._syncS)}),this._observer.observe(this.$element[0],{attributes:!0,childList:!0,subtree:!1})):this.$element[0].addEventListener&&(this.$element[0].addEventListener("DOMAttrModified",b._syncA,!1),this.$element[0].addEventListener("DOMNodeInserted",b._syncS,!1),this.$element[0].addEventListener("DOMNodeRemoved",b._syncS,!1))},e.prototype._registerDataEvents=function(){var a=this;this.dataAdapter.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerSelectionEvents=function(){var b=this,c=["toggle","focus"];this.selection.on("toggle",function(){b.toggleDropdown()}),this.selection.on("focus",function(a){b.focus(a)}),this.selection.on("*",function(d,e){-1===a.inArray(d,c)&&b.trigger(d,e)})},e.prototype._registerDropdownEvents=function(){var a=this;this.dropdown.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerResultsEvents=function(){var a=this;this.results.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerEvents=function(){var a=this;this.on("open",function(){a.$container.addClass("select2-container--open")}),this.on("close",function(){a.$container.removeClass("select2-container--open")}),this.on("enable",function(){a.$container.removeClass("select2-container--disabled")}),this.on("disable",function(){a.$container.addClass("select2-container--disabled")}),this.on("blur",function(){a.$container.removeClass("select2-container--focus")}),this.on("query",function(b){a.isOpen()||a.trigger("open",{}),this.dataAdapter.query(b,function(c){a.trigger("results:all",{data:c,query:b})})}),this.on("query:append",function(b){this.dataAdapter.query(b,function(c){a.trigger("results:append",{data:c,query:b})})}),this.on("keypress",function(b){var c=b.which;a.isOpen()?c===d.ESC||c===d.TAB||c===d.UP&&b.altKey?(a.close(),b.preventDefault()):c===d.ENTER?(a.trigger("results:select",{}),b.preventDefault()):c===d.SPACE&&b.ctrlKey?(a.trigger("results:toggle",{}),b.preventDefault()):c===d.UP?(a.trigger("results:previous",{}),b.preventDefault()):c===d.DOWN&&(a.trigger("results:next",{}),b.preventDefault()):(c===d.ENTER||c===d.SPACE||c===d.DOWN&&b.altKey)&&(a.open(),b.preventDefault())})},e.prototype._syncAttributes=function(){this.options.set("disabled",this.$element.prop("disabled")),this.options.get("disabled")?(this.isOpen()&&this.close(),this.trigger("disable",{})):this.trigger("enable",{})},e.prototype._syncSubtree=function(a,b){var c=!1,d=this;if(!a||!a.target||"OPTION"===a.target.nodeName||"OPTGROUP"===a.target.nodeName){if(b)if(b.addedNodes&&b.addedNodes.length>0)for(var e=0;e<b.addedNodes.length;e++){var f=b.addedNodes[e];f.selected&&(c=!0)}else b.removedNodes&&b.removedNodes.length>0&&(c=!0);else c=!0;c&&this.dataAdapter.current(function(a){d.trigger("selection:update",{data:a})})}},e.prototype.trigger=function(a,b){var c=e.__super__.trigger,d={open:"opening",close:"closing",select:"selecting",unselect:"unselecting"};if(void 0===b&&(b={}),a in d){var f=d[a],g={prevented:!1,name:a,args:b};if(c.call(this,f,g),g.prevented)return void(b.prevented=!0)}c.call(this,a,b)},e.prototype.toggleDropdown=function(){this.options.get("disabled")||(this.isOpen()?this.close():this.open())},e.prototype.open=function(){this.isOpen()||this.trigger("query",{})},e.prototype.close=function(){this.isOpen()&&this.trigger("close",{})},e.prototype.isOpen=function(){return this.$container.hasClass("select2-container--open")},e.prototype.hasFocus=function(){return this.$container.hasClass("select2-container--focus")},e.prototype.focus=function(a){this.hasFocus()||(this.$container.addClass("select2-container--focus"),this.trigger("focus",{}))},e.prototype.enable=function(a){this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("enable")` method has been deprecated and will be removed in later Select2 versions. Use $element.prop("disabled") instead.'),(null==a||0===a.length)&&(a=[!0]);var b=!a[0];this.$element.prop("disabled",b)},e.prototype.data=function(){this.options.get("debug")&&arguments.length>0&&window.console&&console.warn&&console.warn('Select2: Data can no longer be set using `select2("data")`. You should consider setting the value instead using `$element.val()`.');var a=[];return this.dataAdapter.current(function(b){a=b}),a},e.prototype.val=function(b){if(this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("val")` method has been deprecated and will be removed in later Select2 versions. Use $element.val() instead.'),null==b||0===b.length)return this.$element.val();var c=b[0];a.isArray(c)&&(c=a.map(c,function(a){return a.toString()})),this.$element.val(c).trigger("change")},e.prototype.destroy=function(){this.$container.remove(),this.$element[0].detachEvent&&this.$element[0].detachEvent("onpropertychange",this._syncA),null!=this._observer?(this._observer.disconnect(),this._observer=null):this.$element[0].removeEventListener&&(this.$element[0].removeEventListener("DOMAttrModified",this._syncA,!1),this.$element[0].removeEventListener("DOMNodeInserted",this._syncS,!1),this.$element[0].removeEventListener("DOMNodeRemoved",this._syncS,!1)),this._syncA=null,this._syncS=null,this.$element.off(".select2"),this.$element.attr("tabindex",this.$element.data("old-tabindex")),this.$element.removeClass("select2-hidden-accessible"),this.$element.attr("aria-hidden","false"),this.$element.removeData("select2"),this.dataAdapter.destroy(),this.selection.destroy(),this.dropdown.destroy(),this.results.destroy(),this.dataAdapter=null,this.selection=null,this.dropdown=null,this.results=null;
3
+ },e.prototype.render=function(){var b=a('<span class="select2 select2-container"><span class="selection"></span><span class="dropdown-wrapper" aria-hidden="true"></span></span>');return b.attr("dir",this.options.get("dir")),this.$container=b,this.$container.addClass("select2-container--"+this.options.get("theme")),b.data("element",this.$element),b},e}),b.define("select2/compat/utils",["jquery"],function(a){function b(b,c,d){var e,f,g=[];e=a.trim(b.attr("class")),e&&(e=""+e,a(e.split(/\s+/)).each(function(){0===this.indexOf("select2-")&&g.push(this)})),e=a.trim(c.attr("class")),e&&(e=""+e,a(e.split(/\s+/)).each(function(){0!==this.indexOf("select2-")&&(f=d(this),null!=f&&g.push(f))})),b.attr("class",g.join(" "))}return{syncCssClasses:b}}),b.define("select2/compat/containerCss",["jquery","./utils"],function(a,b){function c(a){return null}function d(){}return d.prototype.render=function(d){var e=d.call(this),f=this.options.get("containerCssClass")||"";a.isFunction(f)&&(f=f(this.$element));var g=this.options.get("adaptContainerCssClass");if(g=g||c,-1!==f.indexOf(":all:")){f=f.replace(":all:","");var h=g;g=function(a){var b=h(a);return null!=b?b+" "+a:a}}var i=this.options.get("containerCss")||{};return a.isFunction(i)&&(i=i(this.$element)),b.syncCssClasses(e,this.$element,g),e.css(i),e.addClass(f),e},d}),b.define("select2/compat/dropdownCss",["jquery","./utils"],function(a,b){function c(a){return null}function d(){}return d.prototype.render=function(d){var e=d.call(this),f=this.options.get("dropdownCssClass")||"";a.isFunction(f)&&(f=f(this.$element));var g=this.options.get("adaptDropdownCssClass");if(g=g||c,-1!==f.indexOf(":all:")){f=f.replace(":all:","");var h=g;g=function(a){var b=h(a);return null!=b?b+" "+a:a}}var i=this.options.get("dropdownCss")||{};return a.isFunction(i)&&(i=i(this.$element)),b.syncCssClasses(e,this.$element,g),e.css(i),e.addClass(f),e},d}),b.define("select2/compat/initSelection",["jquery"],function(a){function b(a,b,c){c.get("debug")&&window.console&&console.warn&&console.warn("Select2: The `initSelection` option has been deprecated in favor of a custom data adapter that overrides the `current` method. This method is now called multiple times instead of a single time when the instance is initialized. Support will be removed for the `initSelection` option in future versions of Select2"),this.initSelection=c.get("initSelection"),this._isInitialized=!1,a.call(this,b,c)}return b.prototype.current=function(b,c){var d=this;return this._isInitialized?void b.call(this,c):void this.initSelection.call(null,this.$element,function(b){d._isInitialized=!0,a.isArray(b)||(b=[b]),c(b)})},b}),b.define("select2/compat/inputData",["jquery"],function(a){function b(a,b,c){this._currentData=[],this._valueSeparator=c.get("valueSeparator")||",","hidden"===b.prop("type")&&c.get("debug")&&console&&console.warn&&console.warn("Select2: Using a hidden input with Select2 is no longer supported and may stop working in the future. It is recommended to use a `<select>` element instead."),a.call(this,b,c)}return b.prototype.current=function(b,c){function d(b,c){var e=[];return b.selected||-1!==a.inArray(b.id,c)?(b.selected=!0,e.push(b)):b.selected=!1,b.children&&e.push.apply(e,d(b.children,c)),e}for(var e=[],f=0;f<this._currentData.length;f++){var g=this._currentData[f];e.push.apply(e,d(g,this.$element.val().split(this._valueSeparator)))}c(e)},b.prototype.select=function(b,c){if(this.options.get("multiple")){var d=this.$element.val();d+=this._valueSeparator+c.id,this.$element.val(d),this.$element.trigger("change")}else this.current(function(b){a.map(b,function(a){a.selected=!1})}),this.$element.val(c.id),this.$element.trigger("change")},b.prototype.unselect=function(a,b){var c=this;b.selected=!1,this.current(function(a){for(var d=[],e=0;e<a.length;e++){var f=a[e];b.id!=f.id&&d.push(f.id)}c.$element.val(d.join(c._valueSeparator)),c.$element.trigger("change")})},b.prototype.query=function(a,b,c){for(var d=[],e=0;e<this._currentData.length;e++){var f=this._currentData[e],g=this.matches(b,f);null!==g&&d.push(g)}c({results:d})},b.prototype.addOptions=function(b,c){var d=a.map(c,function(b){return a.data(b[0],"data")});this._currentData.push.apply(this._currentData,d)},b}),b.define("select2/compat/matcher",["jquery"],function(a){function b(b){function c(c,d){var e=a.extend(!0,{},d);if(null==c.term||""===a.trim(c.term))return e;if(d.children){for(var f=d.children.length-1;f>=0;f--){var g=d.children[f],h=b(c.term,g.text,g);h||e.children.splice(f,1)}if(e.children.length>0)return e}return b(c.term,d.text,d)?e:null}return c}return b}),b.define("select2/compat/query",[],function(){function a(a,b,c){c.get("debug")&&window.console&&console.warn&&console.warn("Select2: The `query` option has been deprecated in favor of a custom data adapter that overrides the `query` method. Support will be removed for the `query` option in future versions of Select2."),a.call(this,b,c)}return a.prototype.query=function(a,b,c){b.callback=c;var d=this.options.get("query");d.call(null,b)},a}),b.define("select2/dropdown/attachContainer",[],function(){function a(a,b,c){a.call(this,b,c)}return a.prototype.position=function(a,b,c){var d=c.find(".dropdown-wrapper");d.append(b),b.addClass("select2-dropdown--below"),c.addClass("select2-container--below")},a}),b.define("select2/dropdown/stopPropagation",[],function(){function a(){}return a.prototype.bind=function(a,b,c){a.call(this,b,c);var d=["blur","change","click","dblclick","focus","focusin","focusout","input","keydown","keyup","keypress","mousedown","mouseenter","mouseleave","mousemove","mouseover","mouseup","search","touchend","touchstart"];this.$dropdown.on(d.join(" "),function(a){a.stopPropagation()})},a}),b.define("select2/selection/stopPropagation",[],function(){function a(){}return a.prototype.bind=function(a,b,c){a.call(this,b,c);var d=["blur","change","click","dblclick","focus","focusin","focusout","input","keydown","keyup","keypress","mousedown","mouseenter","mouseleave","mousemove","mouseover","mouseup","search","touchend","touchstart"];this.$selection.on(d.join(" "),function(a){a.stopPropagation()})},a}),function(c){"function"==typeof b.define&&b.define.amd?b.define("jquery-mousewheel",["jquery"],c):"object"==typeof exports?module.exports=c:c(a)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})}),b.define("jquery.select2",["jquery","jquery-mousewheel","./select2/core","./select2/defaults"],function(a,b,c,d){if(null==a.fn.select2){var e=["open","close","destroy"];a.fn.select2=function(b){if(b=b||{},"object"==typeof b)return this.each(function(){var d=a.extend(!0,{},b);new c(a(this),d)}),this;if("string"==typeof b){var d,f=Array.prototype.slice.call(arguments,1);return this.each(function(){var c=a(this).data("select2");null==c&&window.console&&console.error&&console.error("The select2('"+b+"') method was called on an element that is not using Select2."),d=c[b].apply(c,f)}),a.inArray(b,e)>-1?this:d}throw new Error("Invalid arguments for Select2: "+b)}}return null==a.fn.select2.defaults&&(a.fn.select2.defaults=d),c}),{define:b.define,require:b.require}}(),c=b.require("jquery.select2");return a.fn.select2.amd=b,c});
view/js/sendgrid.settings-v1.7.3.js CHANGED
@@ -1,38 +1,113 @@
1
  jQuery(document).ready(function($) {
2
 
3
- if ( $('#auth_method').find("option:selected").val() == 'apikey' ) {
4
- $(".apikey").show();
5
- $(".credentials").hide();
6
- $(".send_method").show();
7
- } else if ( $('#auth_method').find("option:selected").val() == 'credentials' ) {
8
- $(".apikey").hide();
9
- $(".credentials").show();
10
- $(".send_method").show();
 
 
 
 
 
 
11
  }
12
 
13
- if ( $('#send_method').find("option:selected").val() == 'api' ) {
14
- $(".port").hide();
15
- } else if ( $('#send_method').find("option:selected").val() == 'smtp' ) {
16
- $(".port").show();
17
  }
18
 
19
- $('#auth_method').change(function() {
20
- authMethod = $(this).find("option:selected").val();
21
  if ( authMethod == 'apikey' ) {
22
- $(".apikey").show();
23
- $(".credentials").hide();
24
  } else {
25
- $(".apikey").hide();
26
- $(".credentials").show();
27
  }
28
- });
29
 
30
- $('#send_method').change(function() {
31
- sendMethod = $(this).find("option:selected").val();
32
  if ( sendMethod == 'api' ) {
33
- $(".port").hide();
34
  } else {
35
- $(".port").show();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  }
37
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  });
1
  jQuery(document).ready(function($) {
2
 
3
+ var invalid_api_key_placeholder = 'Please set a valid API Key';
4
+ var no_list_placeholder = 'Go to Marketing Campaigns to create a list';
5
+ var select_contact_placeholder = 'Select a contact list';
6
+ var select_page_placeholder = 'Select a page';
7
+ var no_pages_placeholder = 'Please create a page to select';
8
+
9
+ if ( $('#auth_method').find( 'option:selected' ).val() == 'apikey' ) {
10
+ $('.apikey').show();
11
+ $('.credentials').hide();
12
+ $('.send_method').show();
13
+ } else if ( $('#auth_method').find( 'option:selected' ).val() == 'credentials' ) {
14
+ $('.apikey').hide();
15
+ $('.credentials').show();
16
+ $('.send_method').show();
17
  }
18
 
19
+ if ( $('#send_method').find( 'option:selected' ).val() == 'api' ) {
20
+ $('.port').hide();
21
+ } else if ( $('#send_method').find( 'option:selected' ).val() == 'smtp' ) {
22
+ $('.port').show();
23
  }
24
 
25
+ $('#auth_method').change( function() {
26
+ authMethod = $(this).find( 'option:selected' ).val();
27
  if ( authMethod == 'apikey' ) {
28
+ $('.apikey').show();
29
+ $('.credentials').hide();
30
  } else {
31
+ $('.apikey').hide();
32
+ $('.credentials').show();
33
  }
34
+ } );
35
 
36
+ $('#send_method').change( function() {
37
+ sendMethod = $(this).find( 'option:selected' ).val();
38
  if ( sendMethod == 'api' ) {
39
+ $('.port').hide();
40
  } else {
41
+ $('.port').show();
42
+ }
43
+ });
44
+
45
+ if ( $('#use_transactional').is( ':checked' ) ) {
46
+ $('#mc_apikey').prop( 'disabled', true );
47
+ } else {
48
+ $('#mc_apikey').prop( 'disabled', false );
49
+ }
50
+
51
+ $('#use_transactional').change( function() {
52
+ if ( $(this).is( ':checked' ) ) {
53
+ $('#mc_apikey').prop( 'disabled', true );
54
+ } else if ( $("#mc_api_key_defined_in_env").length == 0 ) {
55
+ $('#mc_apikey').prop( 'disabled', false );
56
  }
57
  });
58
+
59
+ if ( $('select#select_contact_list option').length == 0 ) {
60
+ if ( $("#mc_api_key_is_valid").length == 0 ) {
61
+ $('#select_contact_list').select2( {
62
+ placeholder: invalid_api_key_placeholder
63
+ } );
64
+ } else {
65
+ $('#select_contact_list').select2( {
66
+ placeholder: no_list_placeholder
67
+ } );
68
+ }
69
+
70
+ $('#select_contact_list').prop( 'disabled', true );
71
+ } else {
72
+ $('#select_contact_list').select2( {
73
+ placeholder: select_contact_placeholder
74
+ } );
75
+
76
+ $('#select_contact_list').prop( 'disabled', false );
77
+ }
78
+
79
+ if ( $('select#signup_select_page option').length == 0 ) {
80
+ $('#signup_select_page').select2( {
81
+ placeholder: no_pages_placeholder
82
+ } );
83
+
84
+ $('#select_contact_list').prop( 'disabled', true );
85
+ } else {
86
+ $('#signup_select_page').select2( {
87
+ placeholder: select_page_placeholder
88
+ } );
89
+
90
+ $('#signup_select_page').prop( 'disabled', false );
91
+ }
92
+
93
+ if( $('#mc_list_id_defined_in_env').length != 0 ) {
94
+ $('#select_contact_list').prop( 'disabled', true );
95
+
96
+ if ( $('select#select_contact_list option').length != 0 ) {
97
+ var selected_value_text = $('#select_contact_list option[selected="selected"]').text();
98
+ $('#select2-select_contact_list-container').prop('title', selected_value_text);
99
+ $('#select2-select_contact_list-container').html(selected_value_text);
100
+ }
101
+ }
102
+
103
+ if( $('#mc_signup_page_defined_in_env').length != 0 ) {
104
+ $('#signup_select_page').prop( 'disabled', true );
105
+
106
+ if ( $('select#signup_select_page option').length != 0 ) {
107
+ var selected_value_text = $('#signup_select_page option[selected="selected"]').text();
108
+ $('#select2-signup_select_page-container').prop('title', selected_value_text);
109
+ $('#select2-signup_select_page-container').html(selected_value_text);
110
+ }
111
+ }
112
+
113
  });
view/sendgrid_settings.php CHANGED
@@ -1,8 +1,7 @@
1
  <div class="wrap">
2
  <a href="http://sendgrid.com" target="_blank">
3
- <img src="<?php echo plugins_url('/images/logo.png', __FILE__) ?>" width="100" alt="" />
4
  </a>
5
- <h2><?php echo _e('SendGrid Options') ?></h2>
6
  <?php if ( isset( $status ) and ( 'updated' == $status or 'error' == $status ) ): ?>
7
  <div id="message" class="<?php echo $status ?>">
8
  <p>
@@ -10,194 +9,24 @@
10
  </p>
11
  </div>
12
  <?php endif; ?>
13
- <h3><?php echo _e('SendGrid credentials') ?></h3>
14
- <form class="form-table" name="sendgrid_form" method="POST" action="<?php echo str_replace( '%7E', '~', $_SERVER['REQUEST_URI'] ); ?>">
15
- <table class="form-table">
16
- <tbody>
17
- <tr valign="top">
18
- <th scope="row"><?php _e("Authentication method: "); ?></th>
19
- <td>
20
- <select name="auth_method" id="auth_method" <?php disabled( $is_env_auth_method ); ?> >
21
- <option value="apikey" id="apikey" <?php echo ( 'apikey' == $auth_method ) ? 'selected' : '' ?>><?php _e('Api Key') ?></option>
22
- <option value="credentials" id="credentials" <?php echo ( 'credentials' == $auth_method ) ? 'selected' : '' ?>><?php _e('Username&Password') ?></option>
23
- <?php if ( ! in_array( $auth_method, Sendgrid_Tools::$allowed_auth_methods ) ) { ?>
24
- <option value="<?php echo $auth_method; ?>" id="<?php echo $auth_method; ?>" selected><?php echo $auth_method; ?></option>
25
- <?php } ?>
26
- </select>
27
- </td>
28
- </tr>
29
- <tr valign="top" class="apikey" style="display: none;">
30
- <th scope="row"><?php _e("API key: "); ?></th>
31
- <td>
32
- <input type="password" name="sendgrid_apikey" value="<?php echo ( $is_env_api_key ? "************" : $api_key ); ?>" size="50" <?php disabled( $is_env_api_key ); ?>>
33
- </td>
34
- </tr>
35
- <tr valign="top" class="credentials" style="display: none;">
36
- <th scope="row"><?php _e("Username: "); ?></th>
37
- <td>
38
- <input type="text" name="sendgrid_username" value="<?php echo $user; ?>" size="20" class="regular-text" <?php disabled( $is_env_username ); ?>>
39
- </td>
40
- </tr>
41
- <tr valign="top" class="credentials" style="display: none;">
42
- <th scope="row"><?php _e("Password: "); ?></th>
43
- <td>
44
- <input type="password" name="sendgrid_password" value="<?php echo ( $is_env_password ? "******" : $password ); ?>" size="20" class="regular-text" <?php disabled( $is_env_password ); ?>>
45
- </td>
46
- </tr>
47
- <tr valign="top" class="send_method" style="display: none;">
48
- <th scope="row"><?php _e("Send Mail with: "); ?></th>
49
- <td>
50
- <select name="send_method" id="send_method" <?php disabled( defined('SENDGRID_SEND_METHOD') ); ?>>
51
- <?php foreach ( $allowed_send_methods as $method ): ?>
52
- <option value="<?php echo strtolower( $method ); ?>" <?php echo ( strtolower( $method ) == $send_method ) ? 'selected' : '' ?>><?php _e( $method ) ?></option>
53
- <?php endforeach; ?>
54
- </select>
55
- <?php if ( ! in_array( "SMTP", $allowed_send_methods ) ): ?>
56
- <p>
57
- <?php _e('Swift is required in order to be able to send via SMTP.'); ?>
58
- </p>
59
- <?php endif; ?>
60
- </td>
61
- </tr>
62
- <tr valign="top" class="port" style="display: none;">
63
- <th scope="row"><?php _e("Port: "); ?></th>
64
- <td>
65
- <select name="sendgrid_port" id="sendgrid_port" <?php disabled( $is_env_port ); ?>>
66
- <option value="<?php echo SendGrid_SMTP::TLS ?>" id="tls" <?php echo ( ( SendGrid_SMTP::TLS == $port ) or (! $port ) ) ? 'selected' : '' ?>><?php _e( SendGrid_SMTP::TLS ) ?></option>
67
- <option value="<?php echo SendGrid_SMTP::TLS_ALTERNATIVE ?>" id="tls_alt" <?php echo ( SendGrid_SMTP::TLS_ALTERNATIVE == $port ) ? 'selected' : '' ?>><?php _e( SendGrid_SMTP::TLS_ALTERNATIVE ) ?></option>
68
- <option value="<?php echo SendGrid_SMTP::SSL ?>" id="ssl" <?php echo ( SendGrid_SMTP::SSL == $port ) ? 'selected' : '' ?>><?php _e( SendGrid_SMTP::SSL ) ?></option>
69
- </select>
70
- </td>
71
- </tr>
72
- <?php if ( $is_env_auth_method or $is_env_send_method or $is_env_api_key or $is_env_username or $is_env_password or $is_env_port ) : ?>
73
- <tr valign="top">
74
- <td colspan="2">
75
- <p>
76
- <?php _e('Disabled fields are already configured in the config file.'); ?>
77
- </p>
78
- </td>
79
- </tr>
80
- <?php endif; ?>
81
- </tbody>
82
- </table>
83
- <br />
84
- <h3><?php _e('Mail settings') ?></h3>
85
- <table class="form-table">
86
- <tbody>
87
- <tr valign="top">
88
- <th scope="row"><?php _e("Name: "); ?></th>
89
- <td>
90
- <input type="text" name="sendgrid_name" value="<?php echo $name; ?>" size="20" class="regular-text" <?php disabled( defined('SENDGRID_FROM_NAME') ); ?>>
91
- <p class="description"><?php _e('Name as it will appear in recipient clients.') ?></p>
92
- </td>
93
- </tr>
94
- <tr valign="top">
95
- <th scope="row"><?php _e("Sending Address: "); ?></th>
96
- <td>
97
- <input type="text" name="sendgrid_email" value="<?php echo $email; ?>" size="20" class="regular-text" <?php disabled( defined('SENDGRID_FROM_EMAIL') ); ?>>
98
- <p class="description"><?php _e('Email address from which the message will be sent.') ?></p>
99
- </td>
100
- </tr>
101
- <tr valign="top">
102
- <th scope="row"><?php _e("Reply Address: "); ?></th>
103
- <td>
104
- <input type="text" name="sendgrid_reply_to" value="<?php echo $reply_to; ?>" size="20" class="regular-text" <?php disabled( defined('SENDGRID_REPLY_TO') ); ?>>
105
- <span><small><em><?php _e('Leave blank to use Sending Address.') ?></em></small></span>
106
- <p class="description"><?php _e('Email address where replies will be returned.') ?></p>
107
- </td>
108
- </tr>
109
- <tr valign="top">
110
- <th scope="row"><?php _e("Categories: "); ?></th>
111
- <td>
112
- <input type="text" name="sendgrid_categories" value="<?php echo $categories; ?>" size="20" class="regular-text" <?php disabled( defined('SENDGRID_CATEGORIES') ); ?>>
113
- <span><small><em><?php _e('Leave blank to send without categories.') ?></em></small></span>
114
- <p class="description"><?php _e('Associates the category of the email this should be logged as. <br />
115
- Categories must be separated by commas (Example: category1,category2).') ?></p>
116
- </td>
117
- </tr>
118
- <tr valign="top">
119
- <th scope="row"><?php _e("Template: "); ?></th>
120
- <td>
121
- <input type="text" name="sendgrid_template" value="<?php echo $template; ?>" size="20" class="regular-text" <?php disabled( defined('SENDGRID_TEMPLATE') ); ?>>
122
- <span><small><em><?php _e('Leave blank to send without template.') ?></em></small></span>
123
- <p class="description"><?php _e('The template ID used to send emails. <br />
124
- Example: 0b1240a5-188d-4ea7-93c1-19a7a89466b2.') ?></p>
125
- </td>
126
- </tr>
127
- <tr valign="top">
128
- <th scope="row"><?php _e("Content-type: "); ?></th>
129
- <td>
130
- <select name="content_type" id="content_type" <?php disabled( $is_env_content_type ); ?> >
131
- <option value="plaintext" id="plaintext" <?php echo ( 'plaintext' == $content_type ) ? 'selected' : '' ?>><?php _e('text/plain') ?></option>
132
- <option value="html" id="html" <?php echo ( 'html' == $content_type ) ? 'selected' : '' ?>><?php _e('text/html') ?></option>
133
- </select>
134
- </td>
135
- </tr>
136
- </tbody>
137
- </table>
138
- <br />
139
- <h3><?php _e('Statistics settings') ?></h3>
140
- <table class="form-table">
141
- <tbody>
142
- <tr valign="top">
143
- <th scope="row"><?php _e("Categories: "); ?></th>
144
- <td>
145
- <input type="text" name="sendgrid_stats_categories" value="<?php echo $stats_categories; ?>" size="20" class="regular-text" <?php disabled( defined('SENDGRID_STATS_CATEGORIES') ); ?>>
146
- <span><small><em><?php _e('Leave blank for not showing category stats.') ?></em></small></span>
147
- <p class="description"><?php _e('Add some categories for which you would like to see your stats. <br />
148
- Categories must be separated by commas (Example: category1,category2).') ?></p>
149
- </td>
150
- </tr>
151
- <tr valign="top">
152
- <td colspan="2">
153
- <p>
154
- <?php _e('Disabled fields in this form means that they are already configured in the config file.'); ?>
155
- </p>
156
- </td>
157
- </tr>
158
- </tbody>
159
- </table>
160
- <p class="submit">
161
- <input class="button button-primary" type="submit" name="Submit" value="<?php _e('Update Settings') ?>" />
162
- </p>
163
- </form>
164
- <br />
165
- <?php if ( ! isset($status) or ( 'updated' == $status ) or ( 'valid_auth' == $status) or ( 'error' == $status and isset( $error_type ) and 'sending' == $error_type ) ): ?>
166
- <h2><?php _e('SendGrid Test') ?></h2>
167
- <h3><?php _e('Send a test email with these settings') ?></h3>
168
- <form name="sendgrid_test" method="POST" action="<?php echo str_replace('%7E', '~', $_SERVER['REQUEST_URI']); ?>">
169
- <table class="form-table">
170
- <tbody>
171
- <tr valign="top">
172
- <th scope="row"><?php _e("To: "); ?></th>
173
- <td>
174
- <input type="text" name="sendgrid_to" required="true" value="<?php echo isset($success) ? '' : isset($to) ? $to : '' ; ?>" size="20" class="regular-text">
175
- </td>
176
- </tr>
177
- <tr valign="top">
178
- <th scope="row"><?php _e("Subject: "); ?></th>
179
- <td>
180
- <input type="text" name="sendgrid_subj" required="true" value="<?php echo isset($success) ? '' : isset($subject) ? $subject : '' ; ?>" size="20" class="regular-text">
181
- </td>
182
- </tr>
183
- <tr valign="top">
184
- <th scope="row"><?php _e("Body: "); ?></th>
185
- <td>
186
- <textarea name="sendgrid_body" rows="5" class="large-text"><?php echo isset($success) ? '' : isset($body) ? $body : '' ; ?></textarea>
187
- </td>
188
- </tr>
189
- <tr valign="top">
190
- <th scope="row"><?php _e("Headers: "); ?></th>
191
- <td>
192
- <textarea name="sendgrid_headers" rows="3" class="large-text"><?php echo isset($success) ? '' : isset($headers) ? $headers : ''; ?></textarea>
193
- </td>
194
- </tr>
195
- </table>
196
- </tbody>
197
- <input type="hidden" name="email_test" value="true"/>
198
- <p class="submit">
199
- <input class="button button-primary" type="submit" name="Submit" value="<?php _e('Send') ?>" />
200
- </p>
201
- </form>
202
- <?php endif; ?>
203
  </div>
1
  <div class="wrap">
2
  <a href="http://sendgrid.com" target="_blank">
3
+ <img src="<?php echo plugins_url( '/images/logo.png', __FILE__ ) ?>" width="100" alt="" />
4
  </a>
 
5
  <?php if ( isset( $status ) and ( 'updated' == $status or 'error' == $status ) ): ?>
6
  <div id="message" class="<?php echo $status ?>">
7
  <p>
9
  </p>
10
  </div>
11
  <?php endif; ?>
12
+
13
+ <?php
14
+ $tabs = array( 'general' => 'General', 'marketing' => 'Subscription Widget' );
15
+
16
+ $active_tab = current( array_keys( $tabs ) );
17
+ if ( isset( $_GET[ 'tab' ] ) ) {
18
+ $selected_tab = $_GET[ 'tab' ];
19
+ if ( array_key_exists( $selected_tab, $tabs ) ) {
20
+ $active_tab = $selected_tab;
21
+ }
22
+ }
23
+ ?>
24
+
25
+ <?php
26
+ require_once plugin_dir_path( __FILE__ ) . 'sendgrid_settings_nav.php';
27
+ require_once plugin_dir_path( __FILE__ ) . 'sendgrid_settings_general.php';
28
+ require_once plugin_dir_path( __FILE__ ) . 'sendgrid_settings_test_email.php';
29
+ require_once plugin_dir_path( __FILE__ ) . 'sendgrid_settings_nlvx.php';
30
+ require_once plugin_dir_path( __FILE__ ) . 'sendgrid_settings_test_contact.php';
31
+ ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  </div>
view/sendgrid_settings_general.php ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if ( $active_tab == 'general' ): ?>
2
+ <form class="form-table" name="sendgrid_form" method="POST" action="<?php echo str_replace( '%7E', '~', $_SERVER['REQUEST_URI'] ); ?>">
3
+ <table class="form-table">
4
+ <tbody>
5
+ <tr valign="top">
6
+ <td colspan="2">
7
+ <h3 class="sendgrid-settings-top-header"><?php echo _e('SendGrid Credentials') ?></h3>
8
+ </td>
9
+ </tr>
10
+ <tr valign="top">
11
+ <th scope="row"><?php _e("Authentication method: "); ?></th>
12
+ <td>
13
+ <select name="auth_method" id="auth_method" <?php disabled( $is_env_auth_method ); ?> >
14
+ <option value="apikey" id="apikey" <?php echo ( 'apikey' == $auth_method ) ? 'selected' : '' ?>><?php _e('Api Key') ?></option>
15
+ <option value="credentials" id="credentials" <?php echo ( 'credentials' == $auth_method ) ? 'selected' : '' ?>><?php _e('Username&Password') ?></option>
16
+ <?php if ( ! in_array( $auth_method, Sendgrid_Tools::$allowed_auth_methods ) ) { ?>
17
+ <option value="<?php echo $auth_method; ?>" id="<?php echo $auth_method; ?>" selected><?php echo $auth_method; ?></option>
18
+ <?php } ?>
19
+ </select>
20
+ </td>
21
+ </tr>
22
+ <tr valign="top" class="apikey" style="display: none;">
23
+ <th scope="row"><?php _e("API key: "); ?></th>
24
+ <td>
25
+ <input type="password" name="sendgrid_apikey" value="<?php echo ( $is_env_api_key ? "************" : $api_key ); ?>" size="50" <?php disabled( $is_env_api_key ); ?>>
26
+ </td>
27
+ </tr>
28
+ <tr valign="top" class="credentials" style="display: none;">
29
+ <th scope="row"><?php _e("Username: "); ?></th>
30
+ <td>
31
+ <input type="text" name="sendgrid_username" value="<?php echo $user; ?>" size="20" class="regular-text" <?php disabled( $is_env_username ); ?>>
32
+ </td>
33
+ </tr>
34
+ <tr valign="top" class="credentials" style="display: none;">
35
+ <th scope="row"><?php _e("Password: "); ?></th>
36
+ <td>
37
+ <input type="password" name="sendgrid_password" value="<?php echo ( $is_env_password ? "******" : $password ); ?>" size="20" class="regular-text" <?php disabled( $is_env_password ); ?>>
38
+ </td>
39
+ </tr>
40
+ <tr valign="top" class="send_method" style="display: none;">
41
+ <th scope="row"><?php _e("Send Mail with: "); ?></th>
42
+ <td>
43
+ <select name="send_method" id="send_method" <?php disabled( defined('SENDGRID_SEND_METHOD') ); ?>>
44
+ <?php foreach ( $allowed_send_methods as $method ): ?>
45
+ <option value="<?php echo strtolower( $method ); ?>" <?php echo ( strtolower( $method ) == $send_method ) ? 'selected' : '' ?>><?php _e( $method ) ?></option>
46
+ <?php endforeach; ?>
47
+ </select>
48
+ <?php if ( ! in_array( "SMTP", $allowed_send_methods ) ): ?>
49
+ <p>
50
+ <?php _e('Swift is required in order to be able to send via SMTP.'); ?>
51
+ </p>
52
+ <?php endif; ?>
53
+ </td>
54
+ </tr>
55
+ <tr valign="top" class="port" style="display: none;">
56
+ <th scope="row"><?php _e("Port: "); ?></th>
57
+ <td>
58
+ <select name="sendgrid_port" id="sendgrid_port" <?php disabled( $is_env_port ); ?>>
59
+ <option value="<?php echo SendGrid_SMTP::TLS ?>" id="tls" <?php echo ( ( SendGrid_SMTP::TLS == $port ) or (! $port ) ) ? 'selected' : '' ?>><?php _e( SendGrid_SMTP::TLS ) ?></option>
60
+ <option value="<?php echo SendGrid_SMTP::TLS_ALTERNATIVE ?>" id="tls_alt" <?php echo ( SendGrid_SMTP::TLS_ALTERNATIVE == $port ) ? 'selected' : '' ?>><?php _e( SendGrid_SMTP::TLS_ALTERNATIVE ) ?></option>
61
+ <option value="<?php echo SendGrid_SMTP::SSL ?>" id="ssl" <?php echo ( SendGrid_SMTP::SSL == $port ) ? 'selected' : '' ?>><?php _e( SendGrid_SMTP::SSL ) ?></option>
62
+ </select>
63
+ </td>
64
+ </tr>
65
+ <?php if ( $is_env_auth_method or $is_env_send_method or $is_env_api_key or $is_env_username or $is_env_password or $is_env_port ) : ?>
66
+ <tr valign="top">
67
+ <td colspan="2">
68
+ <p>
69
+ <?php _e('Disabled fields are already configured in the config file.'); ?>
70
+ </p>
71
+ </td>
72
+ </tr>
73
+ <?php endif; ?>
74
+ </tbody>
75
+ </table>
76
+ <br />
77
+ <table class="form-table">
78
+ <tbody>
79
+ <tr valign="top">
80
+ <td colspan="2">
81
+ <h3><?php echo _e('Mail settings') ?></h3>
82
+ </td>
83
+ </tr>
84
+ <tr valign="top">
85
+ <th scope="row"><?php _e("Name: "); ?></th>
86
+ <td>
87
+ <input type="text" name="sendgrid_name" value="<?php echo $name; ?>" size="20" class="regular-text" <?php disabled( defined('SENDGRID_FROM_NAME') ); ?>>
88
+ <p class="description"><?php _e('Name as it will appear in recipient clients.') ?></p>
89
+ </td>
90
+ </tr>
91
+ <tr valign="top">
92
+ <th scope="row"><?php _e("Sending Address: "); ?></th>
93
+ <td>
94
+ <input type="text" name="sendgrid_email" value="<?php echo $email; ?>" size="20" class="regular-text" <?php disabled( defined('SENDGRID_FROM_EMAIL') ); ?>>
95
+ <p class="description"><?php _e('Email address from which the message will be sent.') ?></p>
96
+ </td>
97
+ </tr>
98
+ <tr valign="top">
99
+ <th scope="row"><?php _e("Reply Address: "); ?></th>
100
+ <td>
101
+ <input type="text" name="sendgrid_reply_to" value="<?php echo $reply_to; ?>" size="20" class="regular-text" <?php disabled( defined('SENDGRID_REPLY_TO') ); ?>>
102
+ <span><small><em><?php _e('Leave blank to use Sending Address.') ?></em></small></span>
103
+ <p class="description"><?php _e('Email address where replies will be returned.') ?></p>
104
+ </td>
105
+ </tr>
106
+ <tr valign="top">
107
+ <th scope="row"><?php _e("Categories: "); ?></th>
108
+ <td>
109
+ <input type="text" name="sendgrid_categories" value="<?php echo $categories; ?>" size="20" class="regular-text" <?php disabled( defined('SENDGRID_CATEGORIES') ); ?>>
110
+ <span><small><em><?php _e('Leave blank to send without categories.') ?></em></small></span>
111
+ <p class="description"><?php _e('Associates the category of the email this should be logged as. <br />
112
+ Categories must be separated by commas (Example: category1,category2).') ?></p>
113
+ </td>
114
+ </tr>
115
+ <tr valign="top">
116
+ <th scope="row"><?php _e("Template: "); ?></th>
117
+ <td>
118
+ <input type="text" name="sendgrid_template" value="<?php echo $template; ?>" size="20" class="regular-text" <?php disabled( defined('SENDGRID_TEMPLATE') ); ?>>
119
+ <span><small><em><?php _e('Leave blank to send without template.') ?></em></small></span>
120
+ <p class="description"><?php _e('The template ID used to send emails. <br />
121
+ Example: 0b1240a5-188d-4ea7-93c1-19a7a89466b2.') ?></p>
122
+ </td>
123
+ </tr>
124
+ <tr valign="top">
125
+ <th scope="row"><?php _e("Content-type: "); ?></th>
126
+ <td>
127
+ <select name="content_type" id="content_type" <?php disabled( $is_env_content_type ); ?> >
128
+ <option value="plaintext" id="plaintext" <?php echo ( 'plaintext' == $content_type ) ? 'selected' : '' ?>><?php _e('text/plain') ?></option>
129
+ <option value="html" id="html" <?php echo ( 'html' == $content_type ) ? 'selected' : '' ?>><?php _e('text/html') ?></option>
130
+ </select>
131
+ </td>
132
+ </tr>
133
+ </tbody>
134
+ </table>
135
+ <br />
136
+ <table class="form-table">
137
+ <tbody>
138
+ <tr valign="top">
139
+ <td colspan="2">
140
+ <h3><?php echo _e('Statistics settings') ?></h3>
141
+ </td>
142
+ </tr>
143
+ <tr valign="top">
144
+ <th scope="row"><?php _e("Categories: "); ?></th>
145
+ <td>
146
+ <input type="text" name="sendgrid_stats_categories" value="<?php echo $stats_categories; ?>" size="20" class="regular-text" <?php disabled( defined('SENDGRID_STATS_CATEGORIES') ); ?>>
147
+ <span><small><em><?php _e('Leave blank for not showing category stats.') ?></em></small></span>
148
+ <p class="description"><?php _e('Add some categories for which you would like to see your stats. <br />
149
+ Categories must be separated by commas (Example: category1,category2).') ?></p>
150
+ </td>
151
+ </tr>
152
+ <tr valign="top">
153
+ <td colspan="2">
154
+ <p>
155
+ <?php _e('Disabled fields in this form means that they are already configured in the config file.'); ?>
156
+ </p>
157
+ </td>
158
+ </tr>
159
+ </tbody>
160
+ </table>
161
+ <p class="submit">
162
+ <input class="button button-primary" type="submit" name="Submit" value="<?php _e('Update Settings') ?>" />
163
+ </p>
164
+ <input type="hidden" name="general_settings" value="true"/>
165
+ </form>
166
+ <br />
167
+ <?php endif; ?>
view/sendgrid_settings_nav.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <h2 class="nav-tab-wrapper sengrid-settings-nav-bar">
2
+ <?php
3
+ foreach ( $tabs as $tab_key => $tab_description ) {
4
+ if ( $active_tab == $tab_key ) {
5
+ echo '<a href="?page=sendgrid-settings&tab=' . $tab_key . '" class="nav-tab nav-tab-active">' . $tab_description . '</a>';
6
+ } else {
7
+ echo '<a href="?page=sendgrid-settings&tab=' . $tab_key . '" class="nav-tab">' . $tab_description . '</a>';
8
+ }
9
+ }
10
+ ?>
11
+ </h2>
view/sendgrid_settings_nlvx.php ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if ( $active_tab == 'marketing' ): ?>
2
+ <form class="form-table" name="sendgrid_form" method="POST" action="<?php echo str_replace( '%7E', '~', $_SERVER['REQUEST_URI'] ); ?>">
3
+ <table class="form-table">
4
+ <tbody>
5
+ <tr valign="top">
6
+ <td colspan="2">
7
+ <h3 class="sendgrid-settings-top-header"><?php echo _e('SendGrid Credentials') ?></h3>
8
+ </td>
9
+ </tr>
10
+ <tr valign="top" class="mc_apikey">
11
+ <th scope="row"><?php _e("API key: "); ?></th>
12
+ <td>
13
+ <input type="password" id="mc_apikey" name="sendgrid_mc_apikey" value="<?php echo ( $is_env_mc_api_key ? "************" : $mc_api_key ); ?>" size="50" <?php disabled( $is_env_mc_api_key ); ?>>
14
+ <p class="description"><?php _e('An API Key to use for uploading contacts to SendGrid. This API Key needs to have full Marketing Campaigns permissions.') ?></p>
15
+ </td>
16
+ </tr>
17
+ <tr valign="top" class="use_transactional">
18
+ <th scope="row"><?php _e("Use same authentication as transactional: "); ?></th>
19
+ <td>
20
+ <input type="checkbox" id="use_transactional" name="sendgrid_mc_use_transactional" value="true" <?php echo $checked_use_transactional; disabled( $is_env_mc_opt_use_transactional ); ?>>
21
+ <p class="description"><?php _e('If checked, the contacts will be uploaded using the same credentials that are used for sending emails.') ?></p>
22
+ </td>
23
+ </tr>
24
+
25
+ <tr valign="top">
26
+ <td colspan="2">
27
+ <h3><?php echo _e('Subscription Options') ?></h3>
28
+ </td>
29
+ </tr>
30
+ <tr valign="top" class="select_contact_list">
31
+ <th scope="row"><?php _e("Contact list to upload to: "); ?></th>
32
+ <td>
33
+ <select id="select_contact_list" class="sengrid-settings-select" name="sendgrid_mc_contact_list" <?php disabled( $is_env_mc_list_id ); ?>>
34
+ <?php
35
+ if ( false != $contact_lists && $is_mc_api_key_valid ) {
36
+ foreach ( $contact_lists as $key => $list ) {
37
+ if ( $mc_list_id == $list['id'] ) {
38
+ echo '<option value="' . $list['id'] . '" selected="selected">' . $list['name'] . '</option>';
39
+ } else {
40
+ echo '<option value="' . $list['id'] . '">' . $list['name'] . '</option>';
41
+ }
42
+ }
43
+ }
44
+ ?>
45
+ </select>
46
+ <p class="description"><?php _e('The contact details of a subscriber will be uploaded to the selected list.') ?></p>
47
+ </td>
48
+ </tr>
49
+
50
+ <tr valign="top" class="include_fname_lname">
51
+ <th scope="row"> <?php _e("Include First and Last Name fields:"); ?> </th>
52
+ <td>
53
+ <input type="checkbox" id="include_fname_lname" name="sendgrid_mc_incl_fname_lname" value="true" <?php echo $checked_incl_fname_lname; disabled( $is_env_mc_opt_incl_fname_lname ); ?>>
54
+ <p class="description"><?php _e('If checked, the first and last name fields will be displayed in the widget.') ?></p>
55
+ </td>
56
+ </tr>
57
+
58
+ <tr valign="top" class="req_fname_lname">
59
+ <th scope="row"> <?php _e("First and Last Name are required:"); ?> </th>
60
+ <td>
61
+ <input type="checkbox" id="req_fname_lname" name="sendgrid_mc_req_fname_lname" value="true" <?php echo $checked_req_fname_lname; disabled( $is_env_mc_opt_req_fname_lname ); ?>>
62
+ <p class="description"><?php _e('If checked, empty values for the first and last name fields will be rejected.') ?></p>
63
+ </td>
64
+ </tr>
65
+
66
+ <tr valign="top" class="signup_email_subject">
67
+ <th scope="row"> <?php _e("Signup email subject:"); ?></th>
68
+ <td>
69
+ <input type="text" id="signup_email_subject" name="sendgrid_mc_email_subject" size="50" value="<?php echo $mc_signup_email_subject; ?>" <?php disabled( $is_env_mc_signup_email_subject ); ?>>
70
+ <p class="description"><?php _e('The subject for the confirmation email.') ?></p>
71
+ </td>
72
+ </tr>
73
+
74
+ <tr valign="top" class="signup_email_content">
75
+ <th scope="row"> <?php _e("Signup email content:"); ?></th>
76
+ <td>
77
+ <textarea rows="8" cols="48" id="signup_email_content" name="sendgrid_mc_email_content" class="regular-text" <?php disabled( $is_env_mc_signup_email_content ); ?>><?php echo $mc_signup_email_content; ?></textarea>
78
+ <p class="description"><?php _e('Confirmation emails must contain a verification link to confirm the email address being added.') ?> <br/> <?php _e(' You can control the placement of this link by inserting a <b>&lt;a href="%confirmation_link%"&gt; &lt;/a&gt;</b> tag in your email content. This tag is required.') ?></p>
79
+ </td>
80
+ </tr>
81
+
82
+ <tr valign="top" class="signup_select_page">
83
+ <th scope="row"> <?php _e("Signup confirmation page:"); ?></th>
84
+ <td>
85
+ <select id="signup_select_page" class="sengrid-settings-select" name="sendgrid_mc_signup_page" <?php disabled( $is_env_mc_signup_confirmation_page ); ?>>
86
+ <?php
87
+ if ( 'default' == $mc_signup_confirmation_page ) {
88
+ echo '<option value="default" selected>Default Confirmation Page</option>';
89
+ } else {
90
+ echo '<option value="default">Default Confirmation Page</option>';
91
+ }
92
+
93
+ if ( false != $confirmation_pages ) {
94
+ foreach ($confirmation_pages as $key => $page) {
95
+ if ( $mc_signup_confirmation_page == $page->ID ) {
96
+ echo '<option value="' . $page->ID . '" selected="selected">' . $page->post_title . '</option>';
97
+ } else {
98
+ echo '<option value="' . $page->ID . '">' . $page->post_title . '</option>';
99
+ }
100
+ }
101
+ }
102
+ ?>
103
+ </select>
104
+ <p class="description"><?php _e('If the user clicks the confirmation link received in the email, he will be redirected to this page after the contact details are uploaded successfully to SendGrid.') ?></p>
105
+ </td>
106
+ </tr>
107
+
108
+ <?php if ( $is_env_mc_api_key or $is_env_mc_opt_use_transactional or $is_env_mc_opt_incl_fname_lname or
109
+ $is_env_mc_opt_req_fname_lname or $is_env_mc_signup_email_subject or $is_env_mc_signup_email_content or
110
+ $is_env_mc_signup_confirmation_page ) : ?>
111
+ <tr valign="top">
112
+ <td colspan="2">
113
+ <p>
114
+ <?php _e('Disabled fields are already configured in the config file.'); ?>
115
+ </p>
116
+ </td>
117
+ </tr>
118
+ <?php endif; ?>
119
+
120
+ </tbody>
121
+ </table>
122
+ <br />
123
+ <p class="submit">
124
+ <input class="button button-primary" type="submit" name="Submit" value="<?php _e('Update Settings') ?>" />
125
+ </p>
126
+ <input type="hidden" name="mc_settings" value="true"/>
127
+ <?php
128
+ if ( $is_env_mc_api_key ) {
129
+ echo '<input type="hidden" name="mc_api_key_defined_in_env" id="mc_api_key_defined_in_env" value="true"/>';
130
+ }
131
+
132
+ if ( $is_env_mc_list_id ) {
133
+ echo '<input type="hidden" name="mc_list_id_defined_in_env" id="mc_list_id_defined_in_env" value="true"/>';
134
+ }
135
+
136
+ if ( $is_env_mc_signup_confirmation_page ) {
137
+ echo '<input type="hidden" name="mc_signup_page_defined_in_env" id="mc_signup_page_defined_in_env" value="true"/>';
138
+ }
139
+
140
+ if ( $is_mc_api_key_valid ) {
141
+ echo '<input type="hidden" name="mc_api_key_is_valid" id="mc_api_key_is_valid" value="true"/>';
142
+ }
143
+ ?>
144
+ </form>
145
+ <br />
146
+ <?php endif; ?>
view/sendgrid_settings_test_contact.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if ( $active_tab == 'marketing' ): ?>
2
+ <?php if ( ( $is_mc_api_key_valid and $contact_list_id_is_valid ) or ( 'error' == $status and isset( $error_type ) and 'upload' == $error_type ) ): ?>
3
+ <form class="form-table" name="sendgrid_form" method="POST" action="<?php echo str_replace( '%7E', '~', $_SERVER['REQUEST_URI'] ); ?>">
4
+ <table class="form-table">
5
+ <tbody>
6
+ <tr valign="top">
7
+ <td colspan="2">
8
+ <h2><?php _e('SendGrid Test - Subscription') ?></h2>
9
+ </td>
10
+ </tr>
11
+ <tr valign="top" class="mc_test_email">
12
+ <th scope="row"><?php _e("Email: "); ?></th>
13
+ <td>
14
+ <input type="text" id="mc_test_email" name="sendgrid_test_email" value="" size="50">
15
+ <p class="description"><?php _e('An email will be send to this address to confirm the subscription as it does for users that subscribe using the widget.') ?></p>
16
+ </td>
17
+ </tr>
18
+ <input type="hidden" name="contact_upload_test" value="true"/>
19
+ <tr valign="top" class="mc_test_email">
20
+ <th scope="row" colspan="2">
21
+ <input class="button button-primary" type="submit" name="Submit" value="<?php _e('Test') ?>" />
22
+ </th>
23
+ </tr>
24
+ </tbody>
25
+ </table>
26
+ </form>
27
+ <?php endif; ?>
28
+ <?php endif; ?>
view/sendgrid_settings_test_email.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if ( $active_tab == 'general' ): ?>
2
+ <?php if ( ! isset($status) or ( 'updated' == $status ) or ( 'valid_auth' == $status) or ( 'error' == $status and isset( $error_type ) and 'sending' == $error_type ) ): ?>
3
+ <form name="sendgrid_test" method="POST" action="<?php echo str_replace('%7E', '~', $_SERVER['REQUEST_URI']); ?>">
4
+ <table class="form-table">
5
+ <tbody>
6
+ <tr valign="top">
7
+ <td colspan="2">
8
+ <h3><?php echo _e('SendGrid Test - Send a test email with these settings') ?></h3>
9
+ </td>
10
+ </tr>
11
+ <tr valign="top">
12
+ <th scope="row"><?php _e("To: "); ?></th>
13
+ <td>
14
+ <input type="text" name="sendgrid_to" required="true" value="<?php echo isset($success) ? '' : isset($to) ? $to : '' ; ?>" size="20" class="regular-text">
15
+ </td>
16
+ </tr>
17
+ <tr valign="top">
18
+ <th scope="row"><?php _e("Subject: "); ?></th>
19
+ <td>
20
+ <input type="text" name="sendgrid_subj" required="true" value="<?php echo isset($success) ? '' : isset($subject) ? $subject : '' ; ?>" size="20" class="regular-text">
21
+ </td>
22
+ </tr>
23
+ <tr valign="top">
24
+ <th scope="row"><?php _e("Body: "); ?></th>
25
+ <td>
26
+ <textarea name="sendgrid_body" rows="5" class="large-text"><?php echo isset($success) ? '' : isset($body) ? $body : '' ; ?></textarea>
27
+ </td>
28
+ </tr>
29
+ <tr valign="top">
30
+ <th scope="row"><?php _e("Headers: "); ?></th>
31
+ <td>
32
+ <textarea name="sendgrid_headers" rows="3" class="large-text"><?php echo isset($success) ? '' : isset($headers) ? $headers : ''; ?></textarea>
33
+ </td>
34
+ </tr>
35
+ </table>
36
+ </tbody>
37
+ <input type="hidden" name="email_test" value="true"/>
38
+ <p class="submit">
39
+ <input class="button button-primary" type="submit" name="Submit" value="<?php _e('Send') ?>" />
40
+ </p>
41
+ </form>
42
+ <?php endif; ?>
43
+ <?php endif; ?>
wpsendgrid.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: SendGrid
4
  Plugin URI: http://wordpress.org/plugins/sendgrid-email-delivery-simplified/
5
  Description: Email Delivery. Simplified. SendGrid's cloud-based email infrastructure relieves businesses of the cost and complexity of maintaining custom email systems. SendGrid provides reliable delivery, scalability and real-time analytics along with flexible APIs that make custom integration a breeze.
6
- Version: 1.8.2
7
  Author: SendGrid
8
  Author URI: http://sendgrid.com
9
  Text Domain: sendgrid-email-delivery-simplified
@@ -51,9 +51,31 @@ if ( function_exists('wp_mail') )
51
  return;
52
  }
53
 
 
 
54
  require_once plugin_dir_path( __FILE__ ) . 'lib/class-sendgrid-settings.php';
 
55
  require_once plugin_dir_path( __FILE__ ) . 'lib/class-sendgrid-statistics.php';
56
  require_once plugin_dir_path( __FILE__ ) . 'lib/sendgrid/sendgrid-wp-mail.php';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
  // Initialize SendGrid Settings
59
  new Sendgrid_Settings( plugin_basename( __FILE__ ) );
3
  Plugin Name: SendGrid
4
  Plugin URI: http://wordpress.org/plugins/sendgrid-email-delivery-simplified/
5
  Description: Email Delivery. Simplified. SendGrid's cloud-based email infrastructure relieves businesses of the cost and complexity of maintaining custom email systems. SendGrid provides reliable delivery, scalability and real-time analytics along with flexible APIs that make custom integration a breeze.
6
+ Version: 1.9.0
7
  Author: SendGrid
8
  Author URI: http://sendgrid.com
9
  Text Domain: sendgrid-email-delivery-simplified
51
  return;
52
  }
53
 
54
+ // Load plugin files
55
+ require_once plugin_dir_path( __FILE__ ) . 'lib/class-sendgrid-tools.php';
56
  require_once plugin_dir_path( __FILE__ ) . 'lib/class-sendgrid-settings.php';
57
+ require_once plugin_dir_path( __FILE__ ) . 'lib/class-sendgrid-mc-optin.php';
58
  require_once plugin_dir_path( __FILE__ ) . 'lib/class-sendgrid-statistics.php';
59
  require_once plugin_dir_path( __FILE__ ) . 'lib/sendgrid/sendgrid-wp-mail.php';
60
+ require_once plugin_dir_path( __FILE__ ) . 'lib/class-sendgrid-nlvx-widget.php';
61
+ require_once plugin_dir_path( __FILE__ ) . 'lib/class-sendgrid-virtual-pages.php';
62
+
63
+ // Widget Registration
64
+ if ( 'true' == Sendgrid_Tools::get_mc_auth_valid() ) {
65
+ add_action( 'widgets_init', 'register_sendgrid_widgets' );
66
+ } else {
67
+ add_action( 'widgets_init', 'unregister_sendgrid_widgets' );
68
+ }
69
+
70
+ // Widget notice dismissed
71
+ if ( isset( $_POST['sg_dismiss_widget_notice'] ) ) {
72
+ Sendgrid_Tools::set_mc_widget_notice_dismissed( 'true' );
73
+ }
74
+
75
+ // Display widget notice
76
+ if ( 'true' != Sendgrid_Tools::get_mc_widget_notice_dismissed() ) {
77
+ add_action( 'admin_notices', 'sg_subscription_widget_admin_notice' );
78
+ }
79
 
80
  // Initialize SendGrid Settings
81
  new Sendgrid_Settings( plugin_basename( __FILE__ ) );