Completed
Push — develop ( 72e688...d5488d )
by Zack
06:52
created

GravityView_Field_Notes::add_note()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 2
dl 0
loc 15
ccs 0
cts 8
cp 0
crap 6
rs 9.7666
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
167 2
		if( ! GVCommon::has_cap( 'gravityview_add_entry_notes' ) ) {
168 2
			gravityview()->log->error( 'The user isnt allowed to add entry notes.' );
169 2
			return;
170
		}
171
172 1
		if( ! isset( $_POST['action'] ) ) {
173 1
			return;
174
		}
175
176
		if( 'gv_note_add' === $_POST['action'] ) {
177
178
            if( ! GVCommon::has_cap( 'gravityview_add_entry_notes' ) ) {
179
                do_action( 'gravityview_log_error', __METHOD__ . ': The user isnt allowed to add entry notes.' );
180
                return;
181
            }
182
183
			$post = wp_unslash( $_POST );
184
185
			if( $this->doing_ajax ) {
186
				parse_str( $post['data'], $data );
187
			} else {
188
				$data = $post;
189
			}
190
191
			$this->process_add_note( (array) $data );
192
		}
193
	}
194
195
	/**
196
	 * Handle adding a note.
197
	 *
198
	 * Verify the request. If valid, add the note. If AJAX request, send response JSON.
199
	 *
200
	 * @since 1.17
201
	 *
202
	 * @var array $data {
203
	 *  @type string $action "gv_note_add"
204
	 *  @type string $entry-slug Entry slug or ID to add note to
205
	 *  @type string $gv_note_add Nonce with action "gv_note_add_{entry slug}" and name "gv_note_add"
206
	 *  @type string $_wp_http_referer Relative URL to submitting page ('/view/example/entry/123/')
207
	 *  @type string $gv-note-content Note content
208
	 *  @type string $add_note Submit button value ('Add Note')
209
	 * }
210
	 *
211
	 * @return void
212
	 */
213
	private function process_add_note( $data ) {
214
215
		$error = false;
216
		$success = false;
217
218
		if( empty( $data['entry-slug'] ) ) {
219
220
			$error = self::strings('error-invalid');
221
			gravityview()->log->error( 'The note is missing an Entry ID.' );
222
223
		} else {
224
225
			$valid = wp_verify_nonce( $data['gv_note_add'], 'gv_note_add_' . $data['entry-slug'] );
226
227
			$has_cap = GVCommon::has_cap( 'gravityview_add_entry_notes' );
228
229
			if( ! $has_cap ) {
230
				$error = self::strings( 'error-cap-add' );
231
				gravityview()->log->error( 'Adding a note failed: the user does not have the "gravityview_add_entry_notes" capability.' );
232
			} elseif ( $valid ) {
233
234
				$entry = gravityview_get_entry( $data['entry-slug'], true, false );
235
236
				$added = $this->add_note( $entry, $data );
237
238
				// Error adding note
239
				if ( is_wp_error( $added ) ) {
240
241
					$error = $added->get_error_message();
242
243
				} else {
244
245
					// Confirm the note was added, because GF doesn't return note ID on success
246
					$note = GravityView_Entry_Notes::get_note( $added );
247
248
					// Possibly email peeps about this great new note
249
					$this->maybe_send_entry_notes( $note, $entry, $data );
250
251
					if ( $note ) {
252
						$success = self::display_note( $note, ! empty( $data['show-delete'] ) );
253
						gravityview()->log->debug( 'The note was successfully created', array( 'data' => compact( 'note', 'data' ) ) );
254
					} else {
255
						$error = self::strings('error-add-note');
256
						gravityview()->log->error( 'The note was not successfully created', array( 'data' => compact( 'note', 'data' ) ) );
257
					}
258
				}
259
			} else {
260
				$error = self::strings('error-invalid');
261
				gravityview()->log->error( 'Nonce validation failed; the note was not created' );
262
			}
263
		}
264
265
266
		if( $this->doing_ajax ) {
267
			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...
268
				wp_send_json_success( array( 'html' => $success ) );
269
			} else {
270
				$error = $error ? $error : self::strings( 'error-invalid' );
271
				wp_send_json_error( array( 'error' => esc_html( $error ) ) );
272
			}
273
		}
274
	}
275
276
	/**
277
	 * Possibly delete notes, if request is proper.
278
	 *
279
	 * Verify permissions. Check expected $_POST. Parse args, then send to process_delete_notes
280
	 *
281
  	 * @since 1.17
282
	 *
283
	 * @see process_delete_notes
284
	 *
285
	 * @return void
286
	 */
287 2
	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...
288
289 2
		if ( ! GVCommon::has_cap( 'gravityview_delete_entry_notes' ) ) {
290 2
			return;
291
		}
292
293 1
		if ( isset( $_POST['action'] ) && 'gv_delete_notes' === $_POST['action'] ) {
294
295
			$post = wp_unslash( $_POST );
296
			if ( $this->doing_ajax ) {
297
				parse_str( $post['data'], $data );
298
			} else {
299
				$data = $post;
300
			}
301
302
			$required_args = array(
303
				'gv_delete_notes' => '',
304
				'entry-slug' => '',
305
			);
306
307
			$data = wp_parse_args( $data, $required_args );
308
309
			$this->process_delete_notes( $data );
310
		}
311 1
	}
312
313
	/**
314
	 * Handle deleting notes
315
	 *
316
	 * @var array $data {
317
	 *  @type string $action "gv_delete_notes"
318
	 *  @type string $entry-slug Entry slug or ID to add note to
319
	 *  @type string $gv_delete_notes Nonce with action "gv_delete_notes_{entry slug}" and name "gv_delete_notes"
320
	 *  @type string $_wp_http_referer Relative URL to submitting page ('/view/example/entry/123/')
321
	 *  @type int[]  $note  Array of Note IDs to be deleted
322
	 * }
323
	 *
324
	 * @return void
325
	 */
326
	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...
327
328
		$valid = wp_verify_nonce( $data['gv_delete_notes'], 'gv_delete_notes_' . $data['entry-slug'] );
329
		$has_cap = GVCommon::has_cap( 'gravityview_delete_entry_notes' );
330
		$success = false;
331
332
		if ( $valid && $has_cap ) {
333
			GravityView_Entry_Notes::delete_notes( $data['note'] );
334
			$success = true;
335
		}
336
337
		if( $this->doing_ajax ) {
338
339
			if( $success ) {
340
				wp_send_json_success();
341
			} else {
342
				if ( ! $valid ) {
343
					$error_message = self::strings( 'error-invalid' );
344
				} else {
345
					$error_message = self::strings( 'error-permission-delete' );
346
				}
347
348
				wp_send_json_error( array( 'error' => $error_message ) );
349
			}
350
		}
351
	}
352
353
	/**
354
	 * Include this extension templates path
355
	 *
356
	 * @since 1.17
357
	 *
358
	 * @param array $file_paths List of template paths ordered
359
	 *
360
	 * @return array File paths with `./` and `./partials/` paths added
361
	 */
362 2
	public function add_template_path( $file_paths ) {
363
364 2
		$file_paths[ 172 ] = self::$path;
365 2
		$file_paths[ 173 ] = self::$path . 'partials/';
366
367 2
		return $file_paths;
368
	}
369
370
	public function field_options( $field_options, $template_id, $field_id, $context, $input_type, $form_id ) {
371
372
		unset( $field_options['show_as_link'] );
373
374
		$notes_options = array(
375
			'notes' => array(
376
				'type' => 'checkboxes',
377
				'label' => __('Note Settings', 'gravityview'),
378
				'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>' ),
379
				'options' => array(
380
					'view' => array(
381
						'label' => __( 'Display notes?', 'gravityview' ),
382
					),
383
					'view_loggedout' => array(
384
						'label' => __( 'Display notes to users who are not logged-in?', 'gravityview' ),
385
						'requires' => 'view',
386
					),
387
					'add' => array(
388
						'label' => __( 'Enable adding notes?', 'gravityview' ),
389
					),
390
					'email' => array(
391
						'label' => __( 'Allow emailing notes?', 'gravityview' ),
392
						'requires' => 'add',
393
					),
394
					'delete' => array(
395
						'label' => __( 'Allow deleting notes?', 'gravityview' ),
396
					),
397
				),
398
				'value' => array( 'view' => 1, 'add' => 1, 'email' => 1 ),
399
			),
400
		);
401
402
		return $notes_options + $field_options;
403
	}
404
405
	/**
406
	 * Get strings used by the Entry Notes field
407
	 *
408
	 * Use `gravityview/field/notes/strings` filter to modify the strings
409
	 *
410
	 * @since 1.17
411
	 *
412
	 * @param string $key If set, return the string with the key of $key
413
	 *
414
	 * @return array|string Array of strings with keys and values. If $key is set, returns string. If missing $strings[ $key ], empty string.
415
	 */
416 1
	static public function strings( $key = '' ) {
417
418
		$strings = array(
419 1
			'add-note' => __( 'Add Note', 'gravityview' ),
420 1
			'added-note' => __( 'Note added.', 'gravityview' ),
421 1
			'content-label' => __( 'Note Content', 'gravityview' ),
422 1
			'delete' => __( 'Delete', 'gravityview' ),
423 1
			'delete-confirm' => __( 'Are you sure you want to delete the selected notes?', 'gravityview' ),
424 1
			'caption' => __( 'Notes for this entry', 'gravityview' ),
425 1
			'toggle-notes' => __( 'Toggle all notes', 'gravityview' ),
426 1
			'no-notes' => __( 'There are no notes.', 'gravityview' ),
427 1
			'processing' => __( 'Processing&hellip;', 'gravityview' ),
428 1
			'other-email' => __( 'Other email address', 'gravityview' ),
429 1
			'email-label' => __( 'Email address', 'gravityview' ),
430 1
			'email-placeholder' => _x('[email protected]', 'Example email address used as a placeholder', 'gravityview'),
431 1
			'subject-label' => __( 'Subject', 'gravityview' ),
432 1
			'subject' => __( 'Email subject', 'gravityview' ),
433 1
			'default-email-subject' => __( 'New entry note', 'gravityview' ),
434 1
            'email-footer' => __( 'This note was sent from {url}', 'gravityview' ),
435 1
			'also-email' => __( 'Also email this note to', 'gravityview' ),
436 1
			'error-add-note' => __( 'There was an error adding the note.', 'gravityview' ),
437 1
			'error-invalid' => __( 'The request was invalid. Refresh the page and try again.', 'gravityview' ),
438 1
			'error-empty-note' => _x( 'Note cannot be blank.', 'Message to display when submitting a note without content.', 'gravityview' ),
439 1
			'error-cap-delete' => __( 'You don\'t have the ability to delete notes.', 'gravityview' ),
440 1
			'error-cap-add' => __( 'You don\'t have the ability to add notes.', 'gravityview' ),
441
		);
442
443
		/**
444
		 * @filter `gravityview/field/notes/strings` Modify the text used in the Entry Notes field. Sanitized by `esc_html` after return.
445
		 * @since 1.17
446
		 * @param array $strings Text in key => value pairs
447
		 */
448 1
		$strings = gv_map_deep( apply_filters( 'gravityview/field/notes/strings', $strings ), 'esc_html' );
449
450 1
		if( $key ) {
451 1
			return isset( $strings[ $key ] ) ? $strings[ $key ] : '';
452
		}
453
454 1
		return $strings;
455
	}
456
457
	/**
458
	 * Generate HTML output for a single note
459
	 *
460
	 * @since 1.17
461
	 *
462
	 * @param object $note Note object with id, user_id, date_created, value, note_type, user_name, user_email vars
463
	 * @param bool $show_delete Whether to show the bulk delete inputs
464
	 *
465
	 * @since 2.0
466
	 * @param \GV\Template_Context $context The context.
467
	 *
468
	 * @return string HTML
469
	 */
470 1
	static public function display_note( $note, $show_delete = false, $context = null ) {
471
472 1
		if( ! is_object( $note ) ) {
473
			return '';
474
		}
475
476
		$note_content = array(
477 1
			'avatar'                 => get_avatar( $note->user_id, 48 ),
478 1
			'user_name'              => $note->user_name,
479 1
			'user_email'             => $note->user_email,
480 1
			'added_on'               => esc_html__( 'added on {date_created_formatted}', 'gravityview' ),
481 1
			'value'                  => wpautop( esc_html( $note->value ) ),
482 1
			'date_created'           => $note->date_created,
483 1
			'date_created_formatted' => GFCommon::format_date( $note->date_created, false ),
484 1
			'user_id'                => intval( $note->user_id ),
485 1
			'note_type'              => $note->note_type,
486 1
			'note_id'                => intval( $note->id ),
487
		);
488
489
		/**
490
		 * @filter `gravityview/field/notes/content` Modify the note content before rendering in the template
491
		 * @since 1.17
492
		 * @param array $note_content Array of note content that will be replaced in template files
493
		 * @param object $note Note object with id, user_id, date_created, value, note_type, user_name, user_email vars
494
		 * @param boolean $show_delete True: Notes are editable. False: no editing notes.
495
		 * @since 2.0
496
		 * @param \GV\Template_Context $context The context.
497
		 */
498 1
		$note_content = apply_filters( 'gravityview/field/notes/content', $note_content, $note, $show_delete, $context );
499
500 1
		$note_row_template = ( $show_delete && GVCommon::has_cap( 'gravityview_delete_entry_notes' ) ) ? 'row-editable' : 'row';
501
502 1
		if ( $context instanceof \GV\Template_Context ) {
503
504 1
		    ob_start();
505 1
		    $context->template->get_template_part( 'note', 'detail', true );
506 1
            $note_detail_html = ob_get_clean();
507
508 1
            ob_start();
509 1
			$context->template->get_template_part( 'note', $note_row_template, true );
510 1
			$note_row = ob_get_clean();
511
		} else {
512
			/** @deprecated path */
513
			ob_start();
514
			GravityView_View::getInstance()->get_template_part( 'note', 'detail' );
515
			$note_detail_html = ob_get_clean();
516
517
			ob_start();
518
			GravityView_View::getInstance()->get_template_part( 'note', $note_row_template );
519
			$note_row = ob_get_clean();
520
		}
521
522 1
		foreach ( $note_content as $tag => $value ) {
523 1
			$note_detail_html = str_replace( '{' . $tag . '}', $value, $note_detail_html );
524
		}
525
526
		$replacements = array(
527 1
			'{note_id}' => $note_content['note_id'],
528 1
			'{row_class}' => 'gv-note',
529 1
			'{note_detail}' => $note_detail_html
530
		);
531
532
		// Strip extra whitespace in template
533 1
		$output = gravityview_strip_whitespace( $note_row );
534
535 1
		foreach ( $replacements as $tag => $replacement ) {
536 1
			$output = str_replace( $tag, $replacement, $output );
537
		}
538
539 1
		return $output;
540
	}
541
542
	/**
543
	 * Add a note.
544
	 *
545
	 * @since 1.17
546
	 *
547
	 * @see GravityView_Entry_Notes::add_note This method is mostly a wrapper
548
	 *
549
	 * @param array $entry
550
	 * @param array $data Note details array
551
	 *
552
	 * @return int|WP_Error
553
	 */
554
	private function add_note( $entry, $data ) {
555
		global $current_user, $wpdb;
556
557
		$user_data = get_userdata( $current_user->ID );
558
559
		$note_content = trim( $data['gv-note-content'] );
560
561
		if( empty( $note_content ) ) {
562
			return new WP_Error( 'gv-add-note-empty', __( 'The note is empty.', 'gravityview' ) );
563
		}
564
565
		$return = GravityView_Entry_Notes::add_note( $entry['id'], $user_data->ID, $user_data->display_name, $note_content, 'gravityview/field/notes' );
566
567
		return $return;
568
	}
569
570
	/**
571
	 * Get the Add Note form HTML
572
	 *
573
	 * @since 1.17
574
	 *
575
	 * @since 2.0
576
	 * @param array $atts Shortcode attributes for entry ID
577
	 * @param \GV\Template_Context $context The context, when called outside of a shortcode
578
	 *
579
	 * @return string HTML of the Add Note form, or empty string if the user doesn't have the `gravityview_add_entry_notes` cap
580
	 */
581 1
	public static function get_add_note_part( $atts, $context = null ) {
582
583 1
		$atts = shortcode_atts( array( 'entry' => null ), $atts );
584
585 1
		if( ! GVCommon::has_cap( 'gravityview_add_entry_notes' ) ) {
586
			gravityview()->log->error( 'User does not have permission to add entry notes ("gravityview_add_entry_notes").' );
587
			return '';
588
		}
589
590 1
		if ( $context instanceof \GV\Template_Context ) {
591
592 1
			ob_start();
593 1
			$context->template->get_template_part( 'note', 'add-note', true );
594 1
			$add_note_html = ob_get_clean();
595
596 1
			$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...
597 1
			$gv_entry = $context->entry;
598
		} else {
599
			$gravityview_view = GravityView_View::getInstance();
600
601
			ob_start();
602
			$gravityview_view->get_template_part( 'note', 'add-note' );
603
			$add_note_html = ob_get_clean();
604
605
			$visibility_settings = $gravityview_view->getCurrentFieldSetting( 'notes' );
606
607
			if ( $atts['entry'] ) {
608
				$entry = GFAPI::get_entry( $atts['entry'] );
609
			}
610
611
			if ( ! isset( $entry ) || ! $entry ) {
612
				$entry = $gravityview_view->getCurrentEntry();
613
			}
614
615
			$gv_entry = \GV\GF_Entry::from_entry( $entry );
616
		}
617
618
619
		// Strip extra whitespace in template
620 1
		$add_note_html = gravityview_strip_whitespace( $add_note_html );
621 1
		$entry_slug = $gv_entry->get_slug();
622 1
		$nonce_field = wp_nonce_field( 'gv_note_add_' . $entry_slug, 'gv_note_add', false, false );
623
624
		// Only generate the dropdown if the field settings allow it
625 1
		$email_fields = '';
626 1
		if( ! empty( $visibility_settings['email'] ) ) {
627
			$email_fields = self::get_note_email_fields( $entry_slug );
628
		}
629
630 1
		$add_note_html = str_replace( '{entry_slug}', $entry_slug, $add_note_html );
631 1
		$add_note_html = str_replace( '{nonce_field}', $nonce_field, $add_note_html );
632 1
		$add_note_html = str_replace( '{show_delete}', intval( empty( $visibility_settings['delete'] ) ? 0 : $visibility_settings['delete'] ), $add_note_html );
633 1
		$add_note_html   = str_replace( '{email_fields}', $email_fields, $add_note_html );
634 1
		$add_note_html = str_replace( '{url}', esc_url_raw( add_query_arg( array() ) ), $add_note_html );
635
636 1
		return $add_note_html;
637
	}
638
639
	/**
640
	 * Get array of emails addresses from the stored entry
641
	 *
642
	 * @since 1.17
643
	 *
644
	 * @return array Array of email addresses connected to the entry
645
	 */
646
	private static function get_note_emails_array() {
647
648
		$gravityview_view = GravityView_View::getInstance();
649
650
		//getting email values
651
		$email_fields = GFCommon::get_email_fields( $gravityview_view->getForm() );
652
653
		$entry = $gravityview_view->getCurrentEntry();
654
655
		$note_emails = array();
656
657
		foreach ( $email_fields as $email_field ) {
658
			if ( ! empty( $entry["{$email_field->id}"] ) && is_email( $entry["{$email_field->id}"] ) ) {
659
				$note_emails[] = $entry["{$email_field->id}"];
660
			}
661
		}
662
663
		/**
664
		 * @filter `gravityview/field/notes/emails` Modify the dropdown values displayed in the "Also email note to" dropdown
665
		 * @since 1.17
666
		 * @param array $note_emails Array of email addresses connected to the entry
667
		 * @param array $entry Current entry
668
		 */
669
		$note_emails = apply_filters( 'gravityview/field/notes/emails', $note_emails, $entry );
670
671
		return (array) $note_emails;
672
	}
673
674
	/**
675
	 * Generate a HTML dropdown of email values based on email fields from the current form
676
	 *
677
	 * @uses get_note_emails_array
678
	 *
679
	 * @since 1.17
680
	 *
681
	 * @param int|string $entry_slug Current entry unique ID
682
	 *
683
	 * @return string HTML output
684
	 */
685
	private static function get_note_email_fields( $entry_slug = '' ) {
686
687
		if( ! GVCommon::has_cap( 'gravityview_email_entry_notes' ) ) {
688
			gravityview()->log->error( 'User does not have permission to email entry notes ("gravityview_email_entry_notes").' );
689
			return '';
690
		}
691
692
		$entry_slug_esc = esc_attr( $entry_slug );
693
694
		$note_emails = self::get_note_emails_array();
695
696
		$strings = self::strings();
697
698
		/**
699
		 * @filter `gravityview/field/notes/custom-email` Whether to include a Custom Email option for users to define a custom email to mail notes to
700
		 * @since 1.17
701
		 * @param bool $include_custom Default: true
702
		 */
703
		$include_custom = apply_filters( 'gravityview/field/notes/custom-email', true );
704
705
		ob_start();
706
707
		if ( ! empty( $note_emails ) || $include_custom ) { ?>
708
			<div class="gv-note-email-container">
709
				<label for="gv-note-email-to-<?php echo $entry_slug_esc; ?>" class="screen-reader-text"><?php echo $strings['also-email'];  ?></label>
710
				<select class="gv-note-email-to" name="gv-note-to" id="gv-note-email-to-<?php echo $entry_slug_esc; ?>">
711
					<option value=""><?php echo $strings['also-email'];  ?></option>
712
					<?php foreach ( $note_emails as  $email ) {
713
						?>
714
						<option value="<?php echo esc_attr( $email ); ?>"><?php echo esc_html( $email ); ?></option>
715
					<?php }
716
					if( $include_custom ) { ?>
717
					<option value="custom"><?php echo self::strings('other-email'); ?></option>
718
					<?php } ?>
719
				</select>
720
				<fieldset class="gv-note-to-container">
721
					<?php if( $include_custom ) { ?>
722
					<div class='gv-note-to-custom-container'>
723
						<label for="gv-note-email-to-custom-<?php echo $entry_slug_esc; ?>"><?php echo $strings['email-label']; ?></label>
724
						<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="" />
725
					</div>
726
					<?php } ?>
727
		            <div class='gv-note-subject-container'>
728
		                <label for="gv-note-subject-<?php echo $entry_slug_esc; ?>"><?php echo $strings['subject-label']; ?></label>
729
		                <input type="text" name="gv-note-subject" placeholder="<?php echo $strings['subject']; ?>" id="gv-note-subject-<?php echo $entry_slug_esc; ?>" value="" />
730
		            </div>
731
				</fieldset>
732
			</div>
733
		<?php }
734
735
		// TODO: Add a filter
736
		return ob_get_clean();
737
	}
738
739
	/**
740
	 * If note has an email to send, and the user has the right caps, send it
741
	 *
742
	 * @since 1.17
743
	 *
744
	 * @param false|object $note If note was created, object. Otherwise, false.
745
	 * @param array $entry Entry data
746
	 * @param array $data $_POST data
747
	 *
748
	 * @return void Tap in to Gravity Forms' `gform_after_email` action if you want a return result from sending the email.
749
	 */
750
	private function maybe_send_entry_notes( $note = false, $entry, $data ) {
751
752
		if( ! $note || ! GVCommon::has_cap('gravityview_email_entry_notes') ) {
753
			gravityview()->log->debug( 'User doesn\'t have "gravityview_email_entry_notes" cap, or $note is empty', array( 'data' => $note ) );
754
			return;
755
		}
756
757
		gravityview()->log->debug( '$data', array( 'data' => $data ) );
758
759
		//emailing notes if configured
760
		if ( ! empty( $data['gv-note-to'] ) ) {
761
762
			$default_data = array(
763
				'gv-note-to' => '',
764
				'gv-note-to-custom' => '',
765
				'gv-note-subject' => '',
766
				'gv-note-content' => '',
767
                'current-url' => '',
768
			);
769
770
			$current_user  = wp_get_current_user();
771
			$email_data = wp_parse_args( $data, $default_data );
772
773
			$from    = $current_user->user_email;
774
			$to = $email_data['gv-note-to'];
775
776
			/**
777
			 * Documented in get_note_email_fields
778
			 * @see get_note_email_fields
779
			 */
780
			$include_custom = apply_filters( 'gravityview/field/notes/custom-email', true );
781
782
			if( 'custom' === $to && $include_custom ) {
783
				$to = $email_data['gv-note-to-custom'];
784
				gravityview()->log->debug( 'Sending note to a custom email address: {to}' . array( 'to' => $to ) );
785
			}
786
787
			if ( ! GFCommon::is_valid_email_list( $to ) ) {
788
				gravityview()->log->error( '$to not a valid email or email list (CSV of emails): {to}', array( 'to' => print_r( $to, true ), 'data' => $email_data ) );
789
				return;
790
			}
791
792
			$bcc = false;
793
			$reply_to = $from;
794
			$subject = trim( $email_data['gv-note-subject'] );
795
796
			// We use empty() here because GF uses empty to check against, too. `0` isn't a valid subject to GF
797
			$subject = empty( $subject ) ? self::strings( 'default-email-subject' ) : $subject;
798
			$message = $email_data['gv-note-content'];
799
			$email_footer = self::strings( 'email-footer' );
800
			$from_name     = $current_user->display_name;
801
			$message_format = 'html';
802
803
			/**
804
			 * @filter `gravityview/field/notes/email_content` Modify the values passed when sending a note email
805
			 * @see GVCommon::send_email
806
			 * @since 1.17
807
			 * @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'
808
			 */
809
			$email_content = apply_filters( 'gravityview/field/notes/email_content', compact( 'from', 'to', 'bcc', 'reply_to', 'subject', 'message', 'from_name', 'message_format', 'entry', 'email_footer' ) );
810
811
			extract( $email_content );
812
813
			$is_html = ( 'html' === $message_format );
814
815
			// Add the message footer
816
			$message .= $this->get_email_footer( $email_footer, $is_html, $email_data );
817
818
			/**
819
             * @filter `gravityview/field/notes/wpautop_email` Should the message content have paragraphs added automatically, if using HTML message format
820
			 * @since 1.18
821
             * @param bool $wpautop_email True: Apply wpautop() to the email message if using; False: Leave as entered (Default: true)
822
			 */
823
			$wpautop_email = apply_filters( 'gravityview/field/notes/wpautop_email', true );
824
825
			if ( $is_html && $wpautop_email ) {
826
				$message = wpautop( $message );
827
			}
828
829
			GVCommon::send_email( $from, $to, $bcc, $reply_to, $subject, $message, $from_name, $message_format, '', $entry, false );
830
831
			$form  = isset( $entry['form_id'] ) ? GFAPI::get_form( $entry['form_id'] ) : array();
832
833
			/**
834
			 * @see https://www.gravityhelp.com/documentation/article/10146-2/ It's here for compatibility with Gravity Forms
835
			 */
836
			do_action( 'gform_post_send_entry_note', __METHOD__, $to, $from, $subject, $message, $form, $entry );
837
		}
838
	}
839
840
	/**
841
     * Get the footer for Entry Note emails
842
     *
843
     * `{url}` is replaced by the URL of the page where the note form was embedded
844
     *
845
     * @since 1.18
846
     * @see GravityView_Field_Notes::strings The default value of $message_footer is set here, with the key 'email-footer'
847
	 *
848
	 * @param string $email_footer The message footer value
849
	 * @param bool $is_html True: Email is being sent as HTML; False: sent as text
850
	 *
851
	 * @return string If email footer is not empty, return the message with placeholders replaced with dynamic values
852
	 */
853
	private function get_email_footer( $email_footer = '', $is_html = true, $email_data = array() ) {
854
855
	    $output = '';
856
857
		if( ! empty( $email_footer ) ) {
858
		    $url = \GV\Utils::get( $email_data, 'current-url' );
859
			$url = html_entity_decode( $url );
860
			$url = site_url( $url );
861
862
			$content = $is_html ? "<a href='{$url}'>{$url}</a>" : $url;
863
864
			$email_footer = str_replace( '{url}', $content, $email_footer );
865
866
			$output .= "\n\n$email_footer";
867
		}
868
869
		return $output;
870
	}
871
}
872
873
new GravityView_Field_Notes;
874