Completed
Push — develop ( 325502...f35e99 )
by
unknown
18:09
created

GravityView_Field_Notes::get_email_footer()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 3
dl 0
loc 18
ccs 0
cts 7
cp 0
crap 12
rs 9.6666
c 0
b 0
f 0
1
<?php
2
/**
3
 * Notes Field
4
 *
5
 * @package     GravityView
6
 * @license     GPL2+
7
 * @since       1.17
8
 * @author      Katz Web Services, Inc.
9
 * @link        https://gravityview.co
10
 * @copyright   Copyright 2016, Katz Web Services, Inc.
11
 */
12
13
/**
14
 * Add Entry Notes
15
 * @since 1.17
16
 */
17
class GravityView_Field_Notes extends GravityView_Field {
18
19
	/**
20
	 * @var string Current __FILE__
21
	 * @since 1.17
22
	 */
23
	static $file;
24
25
	/**
26
	 * @var string plugin_dir_path() of the current field file
27
	 * @since 1.17
28
	 */
29
	static $path;
30
31
	/**
32
	 * @var bool Are we doing an AJAX request?
33
	 * @since 1.17
34
	 */
35
	private $doing_ajax = false;
36
37
	/**
38
	 * The name of the GravityView field type
39
	 * @var string
40
	 */
41
	var $name = 'notes';
42
43
	function __construct() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
44
45
		self::$path = plugin_dir_path( __FILE__ );
46
		self::$file = __FILE__;
47
48
		$this->doing_ajax = defined( 'DOING_AJAX' ) && DOING_AJAX;
49
50
		$this->add_hooks();
51
52
		parent::__construct();
53
	}
54
55
	/**
56
	 * Add AJAX hooks, [gv_note_add] shortcode, and template loading paths
57
	 *
58
	 * @since 1.17
59
	 *
60
	 * @return void
61
	 */
62
	private function add_hooks() {
63
64
		add_shortcode( 'gv_note_add', array( 'GravityView_Field_Notes', 'get_add_note_part' ) );
65
66
		add_action( 'wp', array( $this, 'maybe_delete_notes'), 1000 );
67
		add_action( 'wp_ajax_nopriv_gv_delete_notes', array( $this, 'maybe_delete_notes') );
68
		add_action( 'wp_ajax_gv_delete_notes', array( $this, 'maybe_delete_notes') );
69
70
		add_action( 'wp', array( $this, 'maybe_add_note'), 1000 );
71
		add_action( 'wp_ajax_nopriv_gv_note_add', array( $this, 'maybe_add_note') );
72
		add_action( 'wp_ajax_gv_note_add', array( $this, 'maybe_add_note') );
73
74
		// add template path to check for field
75
		add_filter( 'gravityview_template_paths', array( $this, 'add_template_path' ) );
76
		add_filter( 'gravityview/template/fields_template_paths', array( $this, 'add_template_path' ) );
77
78
		add_action( 'wp_enqueue_scripts', array( $this, 'register_scripts') );
79
		add_action( 'gravityview/field/notes/scripts', array( $this, 'enqueue_scripts' ) );
80
81
		add_filter( 'gravityview_entry_default_fields', array( $this, 'add_entry_default_field' ), 10, 3 );
82
	}
83
84
85
	/**
86
	 * Add Entry Notes to the Add Field picker in Edit View
87
	 *
88
	 * @see GravityView_Admin_Views::get_entry_default_fields()
89
	 *
90
	 * @since 1.17
91
	 *
92
	 * @param array $entry_default_fields Fields configured to show in the picker
93
	 * @param array $form Gravity Forms form array
94
	 * @param string $zone Current context: `directory`, `single`, `edit`
95
	 *
96
	 * @return array Fields array with notes added, if in Multiple Entries or Single Entry context
97
	 */
98
	public function add_entry_default_field( $entry_default_fields, $form, $zone ) {
99
100
		if( in_array( $zone, array( 'directory', 'single' ) ) ) {
101
			$entry_default_fields['notes'] = array(
102
				'label' => __( 'Entry Notes', 'gravityview' ),
103
				'type'  => 'notes',
104
				'desc'  => __( 'Display, add, and delete notes for an entry.', 'gravityview' ),
105
			);
106
		}
107
108
		return $entry_default_fields;
109
	}
110
111
	/**
112
	 * Register scripts and styles used by the Notes field
113
	 *
114
	 * @since 1.17
115
	 *
116
	 * @return void
117
	 */
118
	public function register_scripts() {
119
		$css_file = gravityview_css_url( 'entry-notes.css', GravityView_Field_Notes::$path . 'assets/css/' );
120
		wp_register_style( 'gravityview-notes', $css_file, array(), GravityView_Plugin::version );
0 ignored issues
show
Deprecated Code introduced by
The constant GravityView_Plugin::version has been deprecated with message: Use \GV\Plugin::$version

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
121
		wp_register_script( 'gravityview-notes', plugins_url( '/assets/js/entry-notes.js', GravityView_Field_Notes::$file ), array( 'jquery' ), GravityView_Plugin::version, true );
0 ignored issues
show
Deprecated Code introduced by
The constant GravityView_Plugin::version has been deprecated with message: Use \GV\Plugin::$version

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
122
	}
123
124
	/**
125
	 * Enqueue, localize field scripts and styles
126
	 *
127
	 * @since 1.17
128
	 *
129
	 * @return void
130
	 */
131 1
	public function enqueue_scripts() {
132 1
		global $wp_actions;
133
134 1
		if( ! wp_script_is( 'gravityview-notes', 'enqueued' ) ) {
135 1
			wp_enqueue_style( 'gravityview-notes' );
136 1
			wp_enqueue_script( 'gravityview-notes' );
137
		}
138
139 1
		if( ! wp_script_is( 'gravityview-notes', 'done' ) ) {
140
141 1
			$strings = self::strings();
142
143 1
			wp_localize_script( 'gravityview-notes', 'GVNotes', array(
144 1
				'ajaxurl' => admin_url( 'admin-ajax.php' ),
145
				'text' => array(
146 1
					'processing' => $strings['processing'],
147 1
					'delete_confirm' => $strings['delete-confirm'],
148 1
					'note_added' => $strings['added-note'],
149 1
					'error_invalid' => $strings['error-invalid'],
150 1
					'error_empty_note' => $strings['error-empty-note'],
151
				),
152
			) );
153
		}
154 1
	}
155
156
	/**
157
	 * Verify permissions, check if $_POST is set and as expected. If so, use process_add_note
158
	 *
159
	 * @since 1.17
160
	 *
161
	 * @see process_add_note
162
	 *
163
	 * @return void
164
	 */
165 2
	function maybe_add_note() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
166
		if ( ! isset( $_POST['action'] ) || 'gv_note_add' !== $_POST['action'] ) {
167 2
			return;
168 2
		}
169 2
170
		if ( ! GVCommon::has_cap( 'gravityview_add_entry_notes' ) ) {
171
			gravityview()->log->error( "The user isn't allowed to add entry notes." );
172 1
173 1
			return;
174
		}
175
176
		$post = wp_unslash( $_POST );
177
178
		if ( $this->doing_ajax ) {
179
			parse_str( $post['data'], $data );
180
		} else {
181
			$data = $post;
182
		}
183
184
		$this->process_add_note( (array) $data );
185
	}
186
187
	/**
188
	 * Handle adding a note.
189
	 *
190
	 * Verify the request. If valid, add the note. If AJAX request, send response JSON.
191
	 *
192
	 * @since 1.17
193
	 *
194
	 * @var array $data {
195
	 *  @type string $action "gv_note_add"
196
	 *  @type string $entry-slug Entry slug or ID to add note to
197
	 *  @type string $gv_note_add Nonce with action "gv_note_add_{entry slug}" and name "gv_note_add"
198
	 *  @type string $_wp_http_referer Relative URL to submitting page ('/view/example/entry/123/')
199
	 *  @type string $gv-note-content Note content
200
	 *  @type string $add_note Submit button value ('Add Note')
201
	 * }
202
	 *
203
	 * @return void
204
	 */
205
	private function process_add_note( $data ) {
206
207
		$error = false;
208
		$success = false;
209
210
		if( empty( $data['entry-slug'] ) ) {
211
212
			$error = self::strings('error-invalid');
213
			gravityview()->log->error( 'The note is missing an Entry ID.' );
214
215
		} else {
216
217
			$valid = wp_verify_nonce( $data['gv_note_add'], 'gv_note_add_' . $data['entry-slug'] );
218
219
			$has_cap = GVCommon::has_cap( 'gravityview_add_entry_notes' );
220
221
			if( ! $has_cap ) {
222
				$error = self::strings( 'error-cap-add' );
223
				gravityview()->log->error( 'Adding a note failed: the user does not have the "gravityview_add_entry_notes" capability.' );
224
			} elseif ( $valid ) {
225
226
				$entry = gravityview_get_entry( $data['entry-slug'], true, false );
227
228
				$added = $this->add_note( $entry, $data );
229
230
				// Error adding note
231
				if ( is_wp_error( $added ) ) {
232
233
					$error = $added->get_error_message();
234
235
				} else {
236
237
					// Confirm the note was added, because GF doesn't return note ID on success
238
					$note = GravityView_Entry_Notes::get_note( $added );
239
240
					// Possibly email peeps about this great new note
241
					$this->maybe_send_entry_notes( $note, $entry, $data );
242
243
					if ( $note ) {
244
						$success = self::display_note( $note, ! empty( $data['show-delete'] ) );
245
						gravityview()->log->debug( 'The note was successfully created', array( 'data' => compact( 'note', 'data' ) ) );
246
					} else {
247
						$error = self::strings('error-add-note');
248
						gravityview()->log->error( 'The note was not successfully created', array( 'data' => compact( 'note', 'data' ) ) );
249
					}
250
				}
251
			} else {
252
				$error = self::strings('error-invalid');
253
				gravityview()->log->error( 'Nonce validation failed; the note was not created' );
254
			}
255
		}
256
257
258
		if( $this->doing_ajax ) {
259
			if( $success ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $success of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
260
				wp_send_json_success( array( 'html' => $success ) );
261
			} else {
262
				$error = $error ? $error : self::strings( 'error-invalid' );
263
				wp_send_json_error( array( 'error' => esc_html( $error ) ) );
264
			}
265
		}
266
	}
267
268
	/**
269
	 * Possibly delete notes, if request is proper.
270
	 *
271
	 * Verify permissions. Check expected $_POST. Parse args, then send to process_delete_notes
272
	 *
273
  	 * @since 1.17
274
	 *
275
	 * @see process_delete_notes
276
	 *
277
	 * @return void
278
	 */
279
	function maybe_delete_notes() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
280
281
		if ( ! GVCommon::has_cap( 'gravityview_delete_entry_notes' ) ) {
282
			return;
283
		}
284
285
		if ( isset( $_POST['action'] ) && 'gv_delete_notes' === $_POST['action'] ) {
286
287 2
			$post = wp_unslash( $_POST );
288
			if ( $this->doing_ajax ) {
289 2
				parse_str( $post['data'], $data );
290 2
			} else {
291
				$data = $post;
292
			}
293 1
294
			$required_args = array(
295
				'gv_delete_notes' => '',
296
				'entry-slug' => '',
297
			);
298
299
			$data = wp_parse_args( $data, $required_args );
300
301
			$this->process_delete_notes( $data );
302
		}
303
	}
304
305
	/**
306
	 * Handle deleting notes
307
	 *
308
	 * @var array $data {
309
	 *  @type string $action "gv_delete_notes"
310
	 *  @type string $entry-slug Entry slug or ID to add note to
311 1
	 *  @type string $gv_delete_notes Nonce with action "gv_delete_notes_{entry slug}" and name "gv_delete_notes"
312
	 *  @type string $_wp_http_referer Relative URL to submitting page ('/view/example/entry/123/')
313
	 *  @type int[]  $note  Array of Note IDs to be deleted
314
	 * }
315
	 *
316
	 * @return void
317
	 */
318
	function process_delete_notes( $data ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
319
320
		$valid = wp_verify_nonce( $data['gv_delete_notes'], 'gv_delete_notes_' . $data['entry-slug'] );
321
		$has_cap = GVCommon::has_cap( 'gravityview_delete_entry_notes' );
322
		$success = false;
323
324
		if ( $valid && $has_cap ) {
325
			GravityView_Entry_Notes::delete_notes( $data['note'] );
326
			$success = true;
327
		}
328
329
		if( $this->doing_ajax ) {
330
331
			if( $success ) {
332
				wp_send_json_success();
333
			} else {
334
				if ( ! $valid ) {
335
					$error_message = self::strings( 'error-invalid' );
336
				} else {
337
					$error_message = self::strings( 'error-permission-delete' );
338
				}
339
340
				wp_send_json_error( array( 'error' => $error_message ) );
341
			}
342
		}
343
	}
344
345
	/**
346
	 * Include this extension templates path
347
	 *
348
	 * @since 1.17
349
	 *
350
	 * @param array $file_paths List of template paths ordered
351
	 *
352
	 * @return array File paths with `./` and `./partials/` paths added
353
	 */
354
	public function add_template_path( $file_paths ) {
355
356
		$file_paths[ 172 ] = self::$path;
357
		$file_paths[ 173 ] = self::$path . 'partials/';
358
359
		return $file_paths;
360
	}
361
362 3
	public function field_options( $field_options, $template_id, $field_id, $context, $input_type, $form_id ) {
363
364 3
		unset( $field_options['show_as_link'] );
365 3
366
		$notes_options = array(
367 3
			'notes' => array(
368
				'type' => 'checkboxes',
369
				'label' => __('Note Settings', 'gravityview'),
370
				'desc' => sprintf( _x('Only users with specific capabilities will be able to view, add and delete notes. %sRead more%s.', '%s is opening and closing HTML link', 'gravityview' ), '<a href="https://docs.gravityview.co/article/311-gravityview-capabilities">', '</a>' ),
371
				'options' => array(
372
					'view' => array(
373
						'label' => __( 'Display notes?', 'gravityview' ),
374
					),
375
					'view_loggedout' => array(
376
						'label' => __( 'Display notes to users who are not logged-in?', 'gravityview' ),
377
						'requires' => 'view',
378
					),
379
					'add' => array(
380
						'label' => __( 'Enable adding notes?', 'gravityview' ),
381
					),
382
					'email' => array(
383
						'label' => __( 'Allow emailing notes?', 'gravityview' ),
384
						'requires' => 'add',
385
					),
386
					'delete' => array(
387
						'label' => __( 'Allow deleting notes?', 'gravityview' ),
388
					),
389
				),
390
				'value' => array( 'view' => 1, 'add' => 1, 'email' => 1 ),
391
			),
392
		);
393
394
		return $notes_options + $field_options;
395
	}
396
397
	/**
398
	 * Get strings used by the Entry Notes field
399
	 *
400
	 * Use `gravityview/field/notes/strings` filter to modify the strings
401
	 *
402
	 * @since 1.17
403
	 *
404
	 * @param string $key If set, return the string with the key of $key
405
	 *
406
	 * @return array|string Array of strings with keys and values. If $key is set, returns string. If missing $strings[ $key ], empty string.
407
	 */
408
	static public function strings( $key = '' ) {
409
410
		$strings = array(
411
			'add-note' => __( 'Add Note', 'gravityview' ),
412
			'added-note' => __( 'Note added.', 'gravityview' ),
413
			'content-label' => __( 'Note Content', 'gravityview' ),
414
			'delete' => __( 'Delete', 'gravityview' ),
415
			'delete-confirm' => __( 'Are you sure you want to delete the selected notes?', 'gravityview' ),
416 1
			'caption' => __( 'Notes for this entry', 'gravityview' ),
417
			'toggle-notes' => __( 'Toggle all notes', 'gravityview' ),
418
			'no-notes' => __( 'There are no notes.', 'gravityview' ),
419 1
			'processing' => __( 'Processing&hellip;', 'gravityview' ),
420 1
			'other-email' => __( 'Other email address', 'gravityview' ),
421 1
			'email-label' => __( 'Email address', 'gravityview' ),
422 1
			'email-placeholder' => _x('[email protected]', 'Example email address used as a placeholder', 'gravityview'),
423 1
			'subject-label' => __( 'Subject', 'gravityview' ),
424 1
			'subject' => __( 'Email subject', 'gravityview' ),
425 1
			'default-email-subject' => __( 'New entry note', 'gravityview' ),
426 1
            'email-footer' => __( 'This note was sent from {url}', 'gravityview' ),
427 1
			'also-email' => __( 'Also email this note to', 'gravityview' ),
428 1
			'error-add-note' => __( 'There was an error adding the note.', 'gravityview' ),
429 1
			'error-invalid' => __( 'The request was invalid. Refresh the page and try again.', 'gravityview' ),
430 1
			'error-empty-note' => _x( 'Note cannot be blank.', 'Message to display when submitting a note without content.', 'gravityview' ),
431 1
			'error-cap-delete' => __( 'You don\'t have the ability to delete notes.', 'gravityview' ),
432 1
			'error-cap-add' => __( 'You don\'t have the ability to add notes.', 'gravityview' ),
433 1
		);
434 1
435 1
		/**
436 1
		 * @filter `gravityview/field/notes/strings` Modify the text used in the Entry Notes field. Sanitized by `esc_html` after return.
437 1
		 * @since 1.17
438 1
		 * @param array $strings Text in key => value pairs
439 1
		 */
440 1
		$strings = gv_map_deep( apply_filters( 'gravityview/field/notes/strings', $strings ), 'esc_html' );
441
442
		if( $key ) {
443
			return isset( $strings[ $key ] ) ? $strings[ $key ] : '';
444
		}
445
446
		return $strings;
447
	}
448 1
449
	/**
450 1
	 * Generate HTML output for a single note
451 1
	 *
452
	 * @since 1.17
453
	 *
454 1
	 * @param object $note Note object with id, user_id, date_created, value, note_type, user_name, user_email vars
455
	 * @param bool $show_delete Whether to show the bulk delete inputs
456
	 *
457
	 * @since 2.0
458
	 * @param \GV\Template_Context $context The context.
459
	 *
460
	 * @return string HTML
461
	 */
462
	static public function display_note( $note, $show_delete = false, $context = null ) {
463
464
		if( ! is_object( $note ) ) {
465
			return '';
466
		}
467
468
		$note_content = array(
469
			'avatar'                 => get_avatar( $note->user_id, 48 ),
470 1
			'user_name'              => $note->user_name,
471
			'user_email'             => $note->user_email,
472 1
			'added_on'               => esc_html__( 'added on {date_created_formatted}', 'gravityview' ),
473
			'value'                  => wpautop( esc_html( $note->value ) ),
474
			'date_created'           => $note->date_created,
475
			'date_created_formatted' => GFCommon::format_date( $note->date_created, false ),
476
			'user_id'                => intval( $note->user_id ),
477 1
			'note_type'              => $note->note_type,
478 1
			'note_id'                => intval( $note->id ),
479 1
		);
480 1
481 1
		/**
482 1
		 * @filter `gravityview/field/notes/content` Modify the note content before rendering in the template
483 1
		 * @since 1.17
484 1
		 * @param array $note_content Array of note content that will be replaced in template files
485 1
		 * @param object $note Note object with id, user_id, date_created, value, note_type, user_name, user_email vars
486 1
		 * @param boolean $show_delete True: Notes are editable. False: no editing notes.
487
		 * @since 2.0
488
		 * @param \GV\Template_Context $context The context.
489
		 */
490
		$note_content = apply_filters( 'gravityview/field/notes/content', $note_content, $note, $show_delete, $context );
491
492
		$note_row_template = ( $show_delete && GVCommon::has_cap( 'gravityview_delete_entry_notes' ) ) ? 'row-editable' : 'row';
493
494
		if ( $context instanceof \GV\Template_Context ) {
495
496
		    ob_start();
497
		    $context->template->get_template_part( 'note', 'detail', true );
498 1
            $note_detail_html = ob_get_clean();
499
500 1
            ob_start();
501
			$context->template->get_template_part( 'note', $note_row_template, true );
502 1
			$note_row = ob_get_clean();
503
		} else {
504 1
			/** @deprecated path */
505 1
			ob_start();
506 1
			GravityView_View::getInstance()->get_template_part( 'note', 'detail' );
507
			$note_detail_html = ob_get_clean();
508 1
509 1
			ob_start();
510 1
			GravityView_View::getInstance()->get_template_part( 'note', $note_row_template );
511
			$note_row = ob_get_clean();
512
		}
513
514
		foreach ( $note_content as $tag => $value ) {
515
			$note_detail_html = str_replace( '{' . $tag . '}', $value, $note_detail_html );
516
		}
517
518
		$replacements = array(
519
			'{note_id}' => $note_content['note_id'],
520
			'{row_class}' => 'gv-note',
521
			'{note_detail}' => $note_detail_html
522 1
		);
523 1
524
		// Strip extra whitespace in template
525
		$output = gravityview_strip_whitespace( $note_row );
526
527 1
		foreach ( $replacements as $tag => $replacement ) {
528 1
			$output = str_replace( $tag, $replacement, $output );
529 1
		}
530
531
		return $output;
532
	}
533 1
534
	/**
535 1
	 * Add a note.
536 1
	 *
537
	 * @since 1.17
538
	 *
539 1
	 * @see GravityView_Entry_Notes::add_note This method is mostly a wrapper
540
	 *
541
	 * @param array $entry
542
	 * @param array $data Note details array
543
	 *
544
	 * @return int|WP_Error
545
	 */
546
	private function add_note( $entry, $data ) {
547
		global $current_user, $wpdb;
548
549
		$user_data = get_userdata( $current_user->ID );
550
551
		$note_content = trim( $data['gv-note-content'] );
552
553
		if( empty( $note_content ) ) {
554
			return new WP_Error( 'gv-add-note-empty', __( 'The note is empty.', 'gravityview' ) );
555
		}
556
557
		$return = GravityView_Entry_Notes::add_note( $entry['id'], $user_data->ID, $user_data->display_name, $note_content, 'gravityview/field/notes' );
558
559
		return $return;
560
	}
561
562
	/**
563
	 * Get the Add Note form HTML
564
	 *
565
	 * @since 1.17
566
	 *
567
	 * @since 2.0
568
	 * @param array $atts Shortcode attributes for entry ID
569
	 * @param \GV\Template_Context $context The context, when called outside of a shortcode
570
	 *
571
	 * @return string HTML of the Add Note form, or empty string if the user doesn't have the `gravityview_add_entry_notes` cap
572
	 */
573
	public static function get_add_note_part( $atts, $context = null ) {
574
575
		$atts = shortcode_atts( array( 'entry' => null ), $atts );
576
577
		if( ! GVCommon::has_cap( 'gravityview_add_entry_notes' ) ) {
578
			gravityview()->log->error( 'User does not have permission to add entry notes ("gravityview_add_entry_notes").' );
579
			return '';
580
		}
581 1
582
		if ( $context instanceof \GV\Template_Context ) {
583 1
584
			ob_start();
585 1
			$context->template->get_template_part( 'note', 'add-note', true );
586
			$add_note_html = ob_get_clean();
587
588
			$visibility_settings = $context->field->notes;
0 ignored issues
show
Bug introduced by
The property notes does not seem to exist in GV\Field.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
589
			$gv_entry = $context->entry;
590 1
		} else {
591
			$gravityview_view = GravityView_View::getInstance();
592 1
593 1
			ob_start();
594 1
			$gravityview_view->get_template_part( 'note', 'add-note' );
595
			$add_note_html = ob_get_clean();
596 1
597 1
			$visibility_settings = $gravityview_view->getCurrentFieldSetting( 'notes' );
598
599
			if ( $atts['entry'] ) {
600
				$entry = GFAPI::get_entry( $atts['entry'] );
601
			}
602
603
			if ( ! isset( $entry ) || ! $entry ) {
604
				$entry = $gravityview_view->getCurrentEntry();
605
			}
606
607
			$gv_entry = \GV\GF_Entry::from_entry( $entry );
608
		}
609
610
611
		// Strip extra whitespace in template
612
		$add_note_html = gravityview_strip_whitespace( $add_note_html );
613
		$entry_slug = $gv_entry->get_slug();
614
		$nonce_field = wp_nonce_field( 'gv_note_add_' . $entry_slug, 'gv_note_add', false, false );
615
616
		// Only generate the dropdown if the field settings allow it
617
		$email_fields = '';
618
		if( ! empty( $visibility_settings['email'] ) ) {
619
			$email_fields = self::get_note_email_fields( $entry_slug );
620 1
		}
621 1
622 1
		$add_note_html = str_replace( '{entry_slug}', $entry_slug, $add_note_html );
623
		$add_note_html = str_replace( '{nonce_field}', $nonce_field, $add_note_html );
624
		$add_note_html = str_replace( '{show_delete}', intval( empty( $visibility_settings['delete'] ) ? 0 : $visibility_settings['delete'] ), $add_note_html );
625 1
		$add_note_html   = str_replace( '{email_fields}', $email_fields, $add_note_html );
626 1
		$add_note_html = str_replace( '{url}', esc_url_raw( add_query_arg( array() ) ), $add_note_html );
627
628
		return $add_note_html;
629
	}
630 1
631 1
	/**
632 1
	 * Get array of emails addresses from the stored entry
633 1
	 *
634 1
	 * @since 1.17
635
	 *
636 1
	 * @return array Array of email addresses connected to the entry
637
	 */
638
	private static function get_note_emails_array() {
639
640
		$gravityview_view = GravityView_View::getInstance();
641
642
		//getting email values
643
		$email_fields = GFCommon::get_email_fields( $gravityview_view->getForm() );
644
645
		$entry = $gravityview_view->getCurrentEntry();
646
647
		$note_emails = array();
648
649
		foreach ( $email_fields as $email_field ) {
650
			if ( ! empty( $entry["{$email_field->id}"] ) && is_email( $entry["{$email_field->id}"] ) ) {
651
				$note_emails[] = $entry["{$email_field->id}"];
652
			}
653
		}
654
655
		/**
656
		 * @filter `gravityview/field/notes/emails` Modify the dropdown values displayed in the "Also email note to" dropdown
657
		 * @since 1.17
658
		 * @param array $note_emails Array of email addresses connected to the entry
659
		 * @param array $entry Current entry
660
		 */
661
		$note_emails = apply_filters( 'gravityview/field/notes/emails', $note_emails, $entry );
662
663
		return (array) $note_emails;
664
	}
665
666
	/**
667
	 * Generate a HTML dropdown of email values based on email fields from the current form
668
	 *
669
	 * @uses get_note_emails_array
670
	 *
671
	 * @since 1.17
672
	 *
673
	 * @param int|string $entry_slug Current entry unique ID
674
	 *
675
	 * @return string HTML output
676
	 */
677
	private static function get_note_email_fields( $entry_slug = '' ) {
678
679
		if( ! GVCommon::has_cap( 'gravityview_email_entry_notes' ) ) {
680
			gravityview()->log->error( 'User does not have permission to email entry notes ("gravityview_email_entry_notes").' );
681
			return '';
682
		}
683
684
		$entry_slug_esc = esc_attr( $entry_slug );
685
686
		$note_emails = self::get_note_emails_array();
687
688
		$strings = self::strings();
689
690
		/**
691
		 * @filter `gravityview/field/notes/custom-email` Whether to include a Custom Email option for users to define a custom email to mail notes to
692
		 * @since 1.17
693
		 * @param bool $include_custom Default: true
694
		 */
695
		$include_custom = apply_filters( 'gravityview/field/notes/custom-email', true );
696
697
		ob_start();
698
699
		if ( ! empty( $note_emails ) || $include_custom ) { ?>
700
			<div class="gv-note-email-container">
701
				<label for="gv-note-email-to-<?php echo $entry_slug_esc; ?>" class="screen-reader-text"><?php echo $strings['also-email'];  ?></label>
702
				<select class="gv-note-email-to" name="gv-note-to" id="gv-note-email-to-<?php echo $entry_slug_esc; ?>">
703
					<option value=""><?php echo $strings['also-email'];  ?></option>
704
					<?php foreach ( $note_emails as  $email ) {
705
						?>
706
						<option value="<?php echo esc_attr( $email ); ?>"><?php echo esc_html( $email ); ?></option>
707
					<?php }
708
					if( $include_custom ) { ?>
709
					<option value="custom"><?php echo self::strings('other-email'); ?></option>
710
					<?php } ?>
711
				</select>
712
				<fieldset class="gv-note-to-container">
713
					<?php if( $include_custom ) { ?>
714
					<div class='gv-note-to-custom-container'>
715
						<label for="gv-note-email-to-custom-<?php echo $entry_slug_esc; ?>"><?php echo $strings['email-label']; ?></label>
716
						<input type="text" name="gv-note-to-custom" placeholder="<?php echo $strings['email-placeholder']; ?>" id="gv-note-to-custom-<?php echo $entry_slug_esc; ?>" value="" />
717
					</div>
718
					<?php } ?>
719
		            <div class='gv-note-subject-container'>
720
		                <label for="gv-note-subject-<?php echo $entry_slug_esc; ?>"><?php echo $strings['subject-label']; ?></label>
721
		                <input type="text" name="gv-note-subject" placeholder="<?php echo $strings['subject']; ?>" id="gv-note-subject-<?php echo $entry_slug_esc; ?>" value="" />
722
		            </div>
723
				</fieldset>
724
			</div>
725
		<?php }
726
727
		// TODO: Add a filter
728
		return ob_get_clean();
729
	}
730
731
	/**
732
	 * If note has an email to send, and the user has the right caps, send it
733
	 *
734
	 * @since 1.17
735
	 *
736
	 * @param false|object $note If note was created, object. Otherwise, false.
737
	 * @param array $entry Entry data
738
	 * @param array $data $_POST data
739
	 *
740
	 * @return void Tap in to Gravity Forms' `gform_after_email` action if you want a return result from sending the email.
741
	 */
742
	private function maybe_send_entry_notes( $note = false, $entry, $data ) {
743
744
		if( ! $note || ! GVCommon::has_cap('gravityview_email_entry_notes') ) {
745
			gravityview()->log->debug( 'User doesn\'t have "gravityview_email_entry_notes" cap, or $note is empty', array( 'data' => $note ) );
746
			return;
747
		}
748
749
		gravityview()->log->debug( '$data', array( 'data' => $data ) );
750
751
		//emailing notes if configured
752
		if ( ! empty( $data['gv-note-to'] ) ) {
753
754
			$default_data = array(
755
				'gv-note-to' => '',
756
				'gv-note-to-custom' => '',
757
				'gv-note-subject' => '',
758
				'gv-note-content' => '',
759
                'current-url' => '',
760
			);
761
762
			$current_user  = wp_get_current_user();
763
			$email_data = wp_parse_args( $data, $default_data );
764
765
			$from    = $current_user->user_email;
766
			$to = $email_data['gv-note-to'];
767
768
			/**
769
			 * Documented in get_note_email_fields
770
			 * @see get_note_email_fields
771
			 */
772
			$include_custom = apply_filters( 'gravityview/field/notes/custom-email', true );
773
774
			if( 'custom' === $to && $include_custom ) {
775
				$to = $email_data['gv-note-to-custom'];
776
				gravityview()->log->debug( 'Sending note to a custom email address: {to}' . array( 'to' => $to ) );
777
			}
778
779
			if ( ! GFCommon::is_valid_email_list( $to ) ) {
780
				gravityview()->log->error( '$to not a valid email or email list (CSV of emails): {to}', array( 'to' => print_r( $to, true ), 'data' => $email_data ) );
781
				return;
782
			}
783
784
			$bcc = false;
785
			$reply_to = $from;
786
			$subject = trim( $email_data['gv-note-subject'] );
787
788
			// We use empty() here because GF uses empty to check against, too. `0` isn't a valid subject to GF
789
			$subject = empty( $subject ) ? self::strings( 'default-email-subject' ) : $subject;
790
			$message = $email_data['gv-note-content'];
791
			$email_footer = self::strings( 'email-footer' );
792
			$from_name     = $current_user->display_name;
793
			$message_format = 'html';
794
795
			/**
796
			 * @filter `gravityview/field/notes/email_content` Modify the values passed when sending a note email
797
			 * @see GVCommon::send_email
798
			 * @since 1.17
799
			 * @param[in,out] array $email_settings Values being passed to the GVCommon::send_email() method: 'from', 'to', 'bcc', 'reply_to', 'subject', 'message', 'from_name', 'message_format', 'entry', 'email_footer'
800
			 */
801
			$email_content = apply_filters( 'gravityview/field/notes/email_content', compact( 'from', 'to', 'bcc', 'reply_to', 'subject', 'message', 'from_name', 'message_format', 'entry', 'email_footer' ) );
802
803
			extract( $email_content );
804
805
			$is_html = ( 'html' === $message_format );
806
807
			// Add the message footer
808
			$message .= $this->get_email_footer( $email_footer, $is_html, $email_data );
809
810
			/**
811
             * @filter `gravityview/field/notes/wpautop_email` Should the message content have paragraphs added automatically, if using HTML message format
812
			 * @since 1.18
813
             * @param bool $wpautop_email True: Apply wpautop() to the email message if using; False: Leave as entered (Default: true)
814
			 */
815
			$wpautop_email = apply_filters( 'gravityview/field/notes/wpautop_email', true );
816
817
			if ( $is_html && $wpautop_email ) {
818
				$message = wpautop( $message );
819
			}
820
821
			GVCommon::send_email( $from, $to, $bcc, $reply_to, $subject, $message, $from_name, $message_format, '', $entry, false );
822
823
			$form  = isset( $entry['form_id'] ) ? GFAPI::get_form( $entry['form_id'] ) : array();
824
825
			/**
826
			 * @see https://www.gravityhelp.com/documentation/article/10146-2/ It's here for compatibility with Gravity Forms
827
			 */
828
			do_action( 'gform_post_send_entry_note', __METHOD__, $to, $from, $subject, $message, $form, $entry );
829
		}
830
	}
831
832
	/**
833
     * Get the footer for Entry Note emails
834
     *
835
     * `{url}` is replaced by the URL of the page where the note form was embedded
836
     *
837
     * @since 1.18
838
     * @see GravityView_Field_Notes::strings The default value of $message_footer is set here, with the key 'email-footer'
839
	 *
840
	 * @param string $email_footer The message footer value
841
	 * @param bool $is_html True: Email is being sent as HTML; False: sent as text
842
	 *
843
	 * @return string If email footer is not empty, return the message with placeholders replaced with dynamic values
844
	 */
845
	private function get_email_footer( $email_footer = '', $is_html = true, $email_data = array() ) {
846
847
	    $output = '';
848
849
		if( ! empty( $email_footer ) ) {
850
		    $url = \GV\Utils::get( $email_data, 'current-url' );
851
			$url = html_entity_decode( $url );
852
			$url = site_url( $url );
853
854
			$content = $is_html ? "<a href='{$url}'>{$url}</a>" : $url;
855
856
			$email_footer = str_replace( '{url}', $content, $email_footer );
857
858
			$output .= "\n\n$email_footer";
859
		}
860
861
		return $output;
862
	}
863
}
864
865
new GravityView_Field_Notes;
866