Completed
Push — master ( c38d6c...15bc19 )
by Zack
14:07 queued 10:41
created

GravityView_Field_Notes::maybe_add_note()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 13
nc 5
nop 0
dl 0
loc 24
ccs 0
cts 13
cp 0
crap 30
rs 8.5125
c 0
b 0
f 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 17 and the first side effect is on line 826.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $file.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
24
25
	/**
26
	 * @var string plugin_dir_path() of the current field file
27
	 * @since 1.17
28
	 */
29
	static $path;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $path.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
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';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $name.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
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 );
0 ignored issues
show
introduced by
No space before closing parenthesis of array is bad style
Loading history...
67
		add_action( 'wp_ajax_nopriv_gv_delete_notes', array( $this, 'maybe_delete_notes') );
0 ignored issues
show
introduced by
No space before closing parenthesis of array is bad style
Loading history...
68
		add_action( 'wp_ajax_gv_delete_notes', array( $this, 'maybe_delete_notes') );
0 ignored issues
show
introduced by
No space before closing parenthesis of array is bad style
Loading history...
69
70
		add_action( 'wp', array( $this, 'maybe_add_note'), 1000 );
0 ignored issues
show
introduced by
No space before closing parenthesis of array is bad style
Loading history...
71
		add_action( 'wp_ajax_nopriv_gv_note_add', array( $this, 'maybe_add_note') );
0 ignored issues
show
introduced by
No space before closing parenthesis of array is bad style
Loading history...
72
		add_action( 'wp_ajax_gv_note_add', array( $this, 'maybe_add_note') );
0 ignored issues
show
introduced by
No space before closing parenthesis of array is bad style
Loading history...
73
74
		// add template path to check for field
75
		add_filter( 'gravityview_template_paths', array( $this, 'add_template_path' ) );
76
77
		add_action( 'wp_enqueue_scripts', array( $this, 'register_scripts') );
0 ignored issues
show
introduced by
No space before closing parenthesis of array is bad style
Loading history...
78
		add_action( 'gravityview/field/notes/scripts', array( $this, 'enqueue_scripts' ) );
79
		
80
		add_filter( 'gravityview_entry_default_fields', array( $this, 'add_entry_default_field' ), 10, 3 );
81
	}
82
83
84
	/**
85
	 * Add Entry Notes to the Add Field picker in Edit View
86
	 *
87
	 * @see GravityView_Admin_Views::get_entry_default_fields()
88
	 *
89
	 * @since 1.17
90
	 *
91
	 * @param array $entry_default_fields Fields configured to show in the picker
92
	 * @param array $form Gravity Forms form array
93
	 * @param string $zone Current context: `directory`, `single`, `edit`
94
	 *
95
	 * @return array Fields array with notes added, if in Multiple Entries or Single Entry context
96
	 */
97
	public function add_entry_default_field( $entry_default_fields, $form, $zone ) {
98
99
		if( in_array( $zone, array( 'directory', 'single' ) ) ) {
100
			$entry_default_fields['notes'] = array(
101
				'label' => __( 'Entry Notes', 'gravityview' ),
102
				'type'  => 'notes',
103
				'desc'  => __( 'Display, add, and delete notes for an entry.', 'gravityview' ),
104
			);
105
		}
106
107
		return $entry_default_fields;
108
	}
109
110
	/**
111
	 * Register scripts and styles used by the Notes field
112
	 *
113
	 * @since 1.17
114
	 *
115
	 * @return void
116
	 */
117
	public function register_scripts() {
118
		$css_file = gravityview_css_url( 'entry-notes.css', GravityView_Field_Notes::$path . 'assets/css/' );
119
		wp_register_style( 'gravityview-notes', $css_file, array(), GravityView_Plugin::version );
120
		wp_register_script( 'gravityview-notes', plugins_url( '/assets/js/entry-notes.js', GravityView_Field_Notes::$file ), array( 'jquery' ), GravityView_Plugin::version, true );
121
	}
122
123
	/**
124
	 * Enqueue, localize field scripts and styles
125
	 * 
126
	 * @since 1.17
127
	 * 
128
	 * @return void
129
	 */
130
	public function enqueue_scripts() {
131
		global $wp_actions;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
132
133
		if( ! wp_script_is( 'gravityview-notes', 'enqueued' ) ) {
134
			wp_enqueue_style( 'gravityview-notes' );
135
			wp_enqueue_script( 'gravityview-notes' );
136
		}
137
138
		if( ! wp_script_is( 'gravityview-notes', 'done' ) ) {
139
140
			$strings = self::strings();
141
142
			wp_localize_script( 'gravityview-notes', 'GVNotes', array(
143
				'ajaxurl' => admin_url( 'admin-ajax.php' ),
144
				'text' => array(
145
					'processing' => $strings['processing'],
146
					'delete_confirm' => $strings['delete-confirm'],
147
					'note_added' => $strings['added-note'],
148
					'error_invalid' => $strings['error-invalid'],
149
					'error_empty_note' => $strings['error-empty-note'],
150
				),
151
			) );
152
		}
153
	}
154
155
	/**
156
	 * Verify permissions, check if $_POST is set and as expected. If so, use process_add_note
157
	 *
158
	 * @since 1.17
159
	 *
160
	 * @see process_add_note
161
	 *
162
	 * @return void
163
	 */
164
	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...
165
166
		if( ! GVCommon::has_cap( 'gravityview_add_entry_notes' ) ) {
167
			do_action( 'gravityview_log_error', __METHOD__ . ': The user isnt allowed to add entry notes.' );
168
			return;
169
		}
170
171
		if( ! isset( $_POST['action'] ) ) {
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
172
			return;
173
		}
174
175
		if( 'gv_note_add' === $_POST['action'] ) {
176
177
			$post = wp_unslash( $_POST );
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
178
179
			if( $this->doing_ajax ) {
180
				parse_str( $post['data'], $data );
181
			} else {
182
				$data = $post;
183
			}
184
185
			$this->process_add_note( (array) $data );
186
		}
187
	}
188
189
	/**
190
	 * Handle adding a note.
191
	 *
192
	 * Verify the request. If valid, add the note. If AJAX request, send response JSON.
193
	 *
194
	 * @since 1.17
195
	 *
196
	 * @var array $data {
197
	 *  @type string $action "gv_note_add"
198
	 *  @type string $entry-slug Entry slug or ID to add note to
199
	 *  @type string $gv_note_add Nonce with action "gv_note_add_{entry slug}" and name "gv_note_add"
200
	 *  @type string $_wp_http_referer Relative URL to submitting page ('/view/example/entry/123/')
201
	 *  @type string $gv-note-content Note content
202
	 *  @type string $add_note Submit button value ('Add Note')
203
	 * }
204
	 *
205
	 * @return void
206
	 */
207
	private function process_add_note( $data ) {
208
209
		$error = false;
210
		$success = false;
211
212
		if( empty( $data['entry-slug'] ) ) {
213
214
			$error = self::strings('error-invalid');
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
215
			do_action( 'gravityview_log_error', __METHOD__ . ': The note is missing an Entry ID.' );
216
217
		} else {
218
219
			$valid = wp_verify_nonce( $data['gv_note_add'], 'gv_note_add_' . $data['entry-slug'] );
220
			
221
			$has_cap = GVCommon::has_cap( 'gravityview_add_entry_notes' );
222
223
			if( ! $has_cap ) {
224
				$error = self::strings( 'error-cap-add' );
225
				do_action( 'gravityview_log_error', __METHOD__ . ': Adding a note failed: the user does not have the "gravityview_add_entry_notes" capability.' );
226
			} elseif ( $valid ) {
227
228
				$entry = gravityview_get_entry( $data['entry-slug'], true, false );
229
230
				$added = $this->add_note( $entry, $data );
231
232
				// Error adding note
233
				if ( is_wp_error( $added ) ) {
234
235
					$error = $added->get_error_message();
236
237
				} else {
238
239
					// Confirm the note was added, because GF doesn't return note ID on success
240
					$note = GravityView_Entry_Notes::get_note( $added );
241
242
					// Possibly email peeps about this great new note
243
					$this->maybe_send_entry_notes( $note, $entry, $data );
244
245
					if ( $note ) {
246
						$success = self::display_note( $note, ! empty( $data['show-delete'] ) );
247
						do_action( 'gravityview_log_debug', __METHOD__ . ': The note was successfully created', compact('note', 'data') );
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
248
					} else {
249
						$error = self::strings('error-add-note');
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
250
						do_action( 'gravityview_log_error', __METHOD__ . ': The note was not successfully created', compact('note', 'data') );
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
251
					}
252
				}
253
			} else {
254
				$error = self::strings('error-invalid');
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
255
				do_action( 'gravityview_log_error', __METHOD__ . ': Nonce validation failed; the note was not created' );
256
			}
257
		}
258
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
259
260
		if( $this->doing_ajax ) {
261
			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...
262
				wp_send_json_success( array( 'html' => $success ) );
263
			} else {
264
				$error = $error ? $error : self::strings( 'error-invalid' );
265
				wp_send_json_error( array( 'error' => esc_html( $error ) ) );
266
			}
267
		}
268
	}
269
270
	/**
271
	 * Possibly delete notes, if request is proper.
272
	 *
273
	 * Verify permissions. Check expected $_POST. Parse args, then send to process_delete_notes
274
	 *
275
  	 * @since 1.17
276
	 *
277
	 * @see process_delete_notes
278
	 *
279
	 * @return void
280
	 */
281
	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...
282
283
		if ( ! GVCommon::has_cap( 'gravityview_delete_entry_notes' ) ) {
284
			return;
285
		}
286
287
		if ( isset( $_POST['action'] ) && 'gv_delete_notes' === $_POST['action'] ) {
288
289
			$post = wp_unslash( $_POST );
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
290
			if ( $this->doing_ajax ) {
291
				parse_str( $post['data'], $data );
292
			} else {
293
				$data = $post;
294
			}
295
296
			$required_args = array(
297
				'gv_delete_notes' => '',
298
				'entry-slug' => '',
299
			);
300
301
			$data = wp_parse_args( $data, $required_args );
302
303
			$this->process_delete_notes( $data );
304
		}
305
	}
306
307
	/**
308
	 * Handle deleting notes
309
	 *
310
	 * @var array $data {
311
	 *  @type string $action "gv_delete_notes"
312
	 *  @type string $entry-slug Entry slug or ID to add note to
313
	 *  @type string $gv_delete_notes Nonce with action "gv_delete_notes_{entry slug}" and name "gv_delete_notes"
314
	 *  @type string $_wp_http_referer Relative URL to submitting page ('/view/example/entry/123/')
315
	 *  @type int[]  $note  Array of Note IDs to be deleted
316
	 * }
317
	 *
318
	 * @return void
319
	 */
320
	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...
321
322
		$valid = wp_verify_nonce( $data['gv_delete_notes'], 'gv_delete_notes_' . $data['entry-slug'] );
323
		$has_cap = GVCommon::has_cap( 'gravityview_delete_entry_notes' );
324
		$success = false;
325
326
		if ( $valid && $has_cap ) {
327
			GravityView_Entry_Notes::delete_notes( $data['note'] );
328
			$success = true;
329
		}
330
331
		if( $this->doing_ajax ) {
332
333
			if( $success ) {
334
				wp_send_json_success();
335
			} else {
336
				if ( ! $valid ) {
337
					$error_message = self::strings( 'error-invalid' );
338
				} else {
339
					$error_message = self::strings( 'error-permission-delete' );
340
				}
341
342
				wp_send_json_error( array( 'error' => $error_message ) );
343
			}
344
		}
345
	}
346
347
	/**
348
	 * Include this extension templates path
349
	 *
350
	 * @since 1.17
351
	 *
352
	 * @param array $file_paths List of template paths ordered
353
	 *
354
	 * @return array File paths with `./` and `./partials/` paths added
355
	 */
356
	public function add_template_path( $file_paths ) {
357
358
		$file_paths[ 172 ] = self::$path;
0 ignored issues
show
introduced by
Array keys should NOT be surrounded by spaces if they only contain a string or an integer.
Loading history...
359
		$file_paths[ 173 ] = self::$path . 'partials/';
0 ignored issues
show
introduced by
Array keys should NOT be surrounded by spaces if they only contain a string or an integer.
Loading history...
360
361
		return $file_paths;
362
	}
363
364
	public function field_options( $field_options, $template_id, $field_id, $context, $input_type ) {
365
366
		unset( $field_options['show_as_link'] );
367
368
		$notes_options = array(
369
			'notes' => array(
370
				'type' => 'checkboxes',
371
				'label' => __('Note Settings', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
372
				'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>' ),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
373
				'options' => array(
374
					'view' => array(
375
						'label' => __( 'Display notes?', 'gravityview' ),
376
					),
377
					'view_loggedout' => array(
378
						'label' => __( 'Display notes to users who are not logged-in?', 'gravityview' ),
379
						'requires' => 'view',
380
					),
381
					'add' => array(
382
						'label' => __( 'Enable adding notes?', 'gravityview' ),
383
					),
384
					'email' => array(
385
						'label' => __( 'Allow emailing notes?', 'gravityview' ),
386
						'requires' => 'add',
387
					),
388
					'delete' => array(
389
						'label' => __( 'Allow deleting notes?', 'gravityview' ),
390
					),
391
				),
392
				'value' => array( 'view' => 1, 'add' => 1, 'email' => 1 ),
393
			),
394
		);
395
396
		return $notes_options + $field_options;
397
	}
398
399
	/**
400
	 * Get strings used by the Entry Notes field
401
	 *
402
	 * Use `gravityview/field/notes/strings` filter to modify the strings
403
	 *
404
	 * @since 1.17
405
	 *
406
	 * @param string $key If set, return the string with the key of $key
407
	 *
408
	 * @return array|string Array of strings with keys and values. If $key is set, returns string. If missing $strings[ $key ], empty string.
409
	 */
410
	static public function strings( $key = '' ) {
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
411
412
		$strings = array(
413
			'add-note' => __( 'Add Note', 'gravityview' ),
414
			'added-note' => __( 'Note added.', 'gravityview' ),
415
			'content-label' => __( 'Note Content', 'gravityview' ),
416
			'delete' => __( 'Delete', 'gravityview' ),
417
			'delete-confirm' => __( 'Are you sure you want to delete the selected notes?', 'gravityview' ),
418
			'caption' => __( 'Notes for this entry', 'gravityview' ),
419
			'toggle-notes' => __( 'Toggle all notes', 'gravityview' ),
420
			'no-notes' => __( 'There are no notes.', 'gravityview' ),
421
			'processing' => __( 'Processing&hellip;', 'gravityview' ),
422
			'other-email' => __( 'Other email address', 'gravityview' ),
423
			'email-label' => __( 'Email address', 'gravityview' ),
424
			'email-placeholder' => _x('[email protected]', 'Example email address used as a placeholder', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
425
			'subject-label' => __( 'Subject', 'gravityview' ),
426
			'subject' => __( 'Email subject', 'gravityview' ),
427
			'default-email-subject' => __( 'New entry note', 'gravityview' ),
428
            'email-footer' => __( 'This note was sent from {url}', 'gravityview' ),
429
			'also-email' => __( 'Also email this note to', 'gravityview' ),
430
			'error-add-note' => __( 'There was an error adding the note.', 'gravityview' ),
431
			'error-invalid' => __( 'The request was invalid. Refresh the page and try again.', 'gravityview' ),
432
			'error-empty-note' => _x( 'Note cannot be blank.', 'Message to display when submitting a note without content.', 'gravityview' ),
433
			'error-cap-delete' => __( 'You don\'t have the ability to delete notes.', 'gravityview' ),
434
			'error-cap-add' => __( 'You don\'t have the ability to add notes.', 'gravityview' ),
435
		);
436
437
		/**
438
		 * @filter `gravityview/field/notes/strings` Modify the text used in the Entry Notes field. Sanitized by `esc_html` after return.
439
		 * @since 1.17
440
		 * @param array $strings Text in key => value pairs
441
		 */
442
		$strings = gv_map_deep( apply_filters( 'gravityview/field/notes/strings', $strings ), 'esc_html' );
443
444
		if( $key ) {
445
			return isset( $strings[ $key ] ) ? $strings[ $key ] : '';
446
		}
447
448
		return $strings;
449
	}
450
451
	/**
452
	 * Generate HTML output for a single note
453
	 *
454
	 * @since 1.17
455
	 *
456
	 * @param object $note Note object with id, user_id, date_created, value, note_type, user_name, user_email vars
457
	 * @param bool $show_delete Whether to show the bulk delete inputs
458
	 *
459
	 * @return string HTML
460
	 */
461
	static public function display_note( $note, $show_delete = false ) {
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
462
463
		if( ! is_object( $note ) ) {
464
			return '';
465
		}
466
467
		$note_content = array(
468
			'avatar'                 => get_avatar( $note->user_id, 48 ),
469
			'user_name'              => $note->user_name,
470
			'user_email'             => $note->user_email,
471
			'added_on'               => esc_html__( 'added on {date_created_formatted}', 'gravityview' ),
472
			'value'                  => wpautop( esc_html( $note->value ) ),
473
			'date_created'           => $note->date_created,
474
			'date_created_formatted' => GFCommon::format_date( $note->date_created, false ),
475
			'user_id'                => intval( $note->user_id ),
476
			'note_type'              => $note->note_type,
477
			'note_id'                => intval( $note->id ),
478
		);
479
480
		/**
481
		 * @filter `gravityview/field/notes/content` Modify the note content before rendering in the template
482
		 * @since 1.17
483
		 * @param array $note_content Array of note content that will be replaced in template files
484
		 * @param object $note Note object with id, user_id, date_created, value, note_type, user_name, user_email vars
485
		 * @param boolean $show_delete True: Notes are editable. False: no editing notes.
486
		 */
487
		$note_content = apply_filters( 'gravityview/field/notes/content', $note_content, $note, $show_delete );
488
489
		ob_start();
490
		GravityView_View::getInstance()->get_template_part( 'note', 'detail' );
491
		$note_detail_html = ob_get_clean();
492
493
		foreach ( $note_content as $tag => $value ) {
494
			$note_detail_html = str_replace( '{' . $tag . '}', $value, $note_detail_html );
495
		}
496
497
		$note_row_template = ( $show_delete && GVCommon::has_cap( 'gravityview_delete_entry_notes' ) ) ? 'row-editable' : 'row';
498
499
		ob_start();
500
		GravityView_View::getInstance()->get_template_part( 'note', $note_row_template );
501
		$note_row = ob_get_clean();
502
503
		$replacements = array(
504
			'{note_id}' => $note_content['note_id'],
505
			'{row_class}' => 'gv-note',
506
			'{note_detail}' => $note_detail_html
0 ignored issues
show
introduced by
Each line in an array declaration must end in a comma
Loading history...
507
		);
508
509
		// Strip extra whitespace in template
510
		$output = gravityview_strip_whitespace( $note_row );
511
512
		foreach ( $replacements as $tag => $replacement ) {
513
			$output = str_replace( $tag, $replacement, $output );
514
		}
515
516
		return $output;
517
	}
518
519
	/**
520
	 * Add a note.
521
	 *
522
	 * @since 1.17
523
	 *
524
	 * @see GravityView_Entry_Notes::add_note This method is mostly a wrapper
525
	 *
526
	 * @param array $entry
527
	 * @param array $data Note details array
528
	 *
529
	 * @return int|WP_Error
530
	 */
531
	private function add_note( $entry, $data ) {
532
		global $current_user, $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
533
534
		$user_data = get_userdata( $current_user->ID );
535
536
		$note_content = trim( $data['gv-note-content'] );
537
538
		if( empty( $note_content ) ) {
539
			return new WP_Error( 'gv-add-note-empty', __( 'The note is empty.', 'gravityview' ) );
540
		}
541
542
		$return = GravityView_Entry_Notes::add_note( $entry['id'], $user_data->ID, $user_data->display_name, $note_content, 'gravityview/field/notes' );
543
544
		return $return;
545
	}
546
547
	/**
548
	 * Get the Add Note form HTML
549
	 *
550
	 * @todo Allow passing entry_id as a shortcode parameter to set entry from shortcode
551
	 *
552
	 * @since 1.17
553
	 *
554
	 * @return string HTML of the Add Note form, or empty string if the user doesn't have the `gravityview_add_entry_notes` cap
555
	 */
556
	public static function get_add_note_part() {
557
558
		if( ! GVCommon::has_cap( 'gravityview_add_entry_notes' ) ) {
559
			do_action( 'gravityview_log_error', __METHOD__ . ': User does not have permission to add entry notes ("gravityview_add_entry_notes").' );
560
			return '';
561
		}
562
563
		$gravityview_view = GravityView_View::getInstance();
564
565
		ob_start();
566
		$gravityview_view->get_template_part( 'note', 'add-note' );
567
		$add_note_html = ob_get_clean();
568
569
		// Strip extra whitespace in template
570
		$add_note_html = gravityview_strip_whitespace( $add_note_html );
571
572
		$visibility_settings = $gravityview_view->getCurrentFieldSetting( 'notes' );
573
		$entry = $gravityview_view->getCurrentEntry();
574
		$entry_slug = GravityView_API::get_entry_slug( $entry['id'], $entry );
575
		$nonce_field = wp_nonce_field( 'gv_note_add_' . $entry_slug, 'gv_note_add', false, false );
576
577
		// Only generate the dropdown if the field settings allow it
578
		$email_fields = '';
579
		if( ! empty( $visibility_settings['email'] ) ) {
580
			$email_fields = self::get_note_email_fields( $entry_slug );
581
		}
582
583
		$add_note_html = str_replace( '{entry_slug}', $entry_slug, $add_note_html );
584
		$add_note_html = str_replace( '{nonce_field}', $nonce_field, $add_note_html );
585
		$add_note_html = str_replace( '{show_delete}', intval( $visibility_settings['delete'] ), $add_note_html );
586
		$add_note_html   = str_replace( '{email_fields}', $email_fields, $add_note_html );
587
		$add_note_html = str_replace( '{url}', esc_url_raw( add_query_arg( array() ) ), $add_note_html );
588
589
		return $add_note_html;
590
	}
591
592
	/**
593
	 * Get array of emails addresses from the stored entry
594
	 *
595
	 * @since 1.17
596
	 *
597
	 * @return array Array of email addresses connected to the entry
598
	 */
599
	private static function get_note_emails_array() {
600
601
		$gravityview_view = GravityView_View::getInstance();
602
603
		//getting email values
604
		$email_fields = GFCommon::get_email_fields( $gravityview_view->getForm() );
605
606
		$entry = $gravityview_view->getCurrentEntry();
607
608
		$note_emails = array();
609
610
		foreach ( $email_fields as $email_field ) {
611
			if ( ! empty( $entry["{$email_field->id}"] ) && is_email( $entry["{$email_field->id}"] ) ) {
0 ignored issues
show
introduced by
Array keys should be surrounded by spaces unless they contain a string or an integer.
Loading history...
612
				$note_emails[] = $entry["{$email_field->id}"];
0 ignored issues
show
introduced by
Array keys should be surrounded by spaces unless they contain a string or an integer.
Loading history...
613
			}
614
		}
615
616
		/**
617
		 * @filter `gravityview/field/notes/emails` Modify the dropdown values displayed in the "Also email note to" dropdown
618
		 * @since 1.17
619
		 * @param array $note_emails Array of email addresses connected to the entry
620
		 * @param array $entry Current entry
621
		 */
622
		$note_emails = apply_filters( 'gravityview/field/notes/emails', $note_emails, $entry );
623
624
		return (array) $note_emails;
625
	}
626
627
	/**
628
	 * Generate a HTML dropdown of email values based on email fields from the current form
629
	 *
630
	 * @uses get_note_emails_array
631
	 *
632
	 * @since 1.17
633
	 *
634
	 * @param int|string $entry_slug Current entry unique ID
635
	 *
636
	 * @return string HTML output
637
	 */
638
	private static function get_note_email_fields( $entry_slug = '' ) {
639
640
		if( ! GVCommon::has_cap( 'gravityview_email_entry_notes' ) ) {
641
			do_action( 'gravityview_log_error', __METHOD__ . ': User does not have permission to email entry notes ("gravityview_email_entry_notes").' );
642
			return '';
643
		}
644
645
		$entry_slug_esc = esc_attr( $entry_slug );
646
647
		$note_emails = self::get_note_emails_array();
648
649
		$strings = self::strings();
650
651
		/**
652
		 * @filter `gravityview/field/notes/custom-email` Whether to include a Custom Email option for users to define a custom email to mail notes to
653
		 * @since 1.17
654
		 * @param bool $include_custom Default: true
655
		 */
656
		$include_custom = apply_filters( 'gravityview/field/notes/custom-email', true );
657
658
		ob_start();
659
660
		if ( ! empty( $note_emails ) || $include_custom ) { ?>
661
			<div class="gv-note-email-container">
662
				<label for="gv-note-email-to-<?php echo $entry_slug_esc; ?>" class="screen-reader-text"><?php echo $strings['also-email'];  ?></label>
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$entry_slug_esc'
Loading history...
introduced by
Expected next thing to be a escaping function, not '$strings'
Loading history...
663
				<select class="gv-note-email-to" name="gv-note-to" id="gv-note-email-to-<?php echo $entry_slug_esc; ?>">
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$entry_slug_esc'
Loading history...
664
					<option value=""><?php echo $strings['also-email'];  ?></option>
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$strings'
Loading history...
665
					<?php foreach ( $note_emails as  $email ) {
666
						?>
667
						<option value="<?php echo esc_attr( $email ); ?>"><?php echo esc_html( $email ); ?></option>
668
					<?php }
669
					if( $include_custom ) { ?>
670
					<option value="custom"><?php echo self::strings('other-email'); ?></option>
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not 'self'
Loading history...
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
671
					<?php } ?>
672
				</select>
673
				<fieldset class="gv-note-to-container">
674
					<?php if( $include_custom ) { ?>
675
					<div class='gv-note-to-custom-container'>
676
						<label for="gv-note-email-to-custom-<?php echo $entry_slug_esc; ?>"><?php echo $strings['email-label']; ?></label>
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$entry_slug_esc'
Loading history...
introduced by
Expected next thing to be a escaping function, not '$strings'
Loading history...
677
						<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="" />
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$strings'
Loading history...
introduced by
Expected next thing to be a escaping function, not '$entry_slug_esc'
Loading history...
678
					</div>
679
					<?php } ?>
680
		            <div class='gv-note-subject-container'>
681
		                <label for="gv-note-subject-<?php echo $entry_slug_esc; ?>"><?php echo $strings['subject-label']; ?></label>
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$entry_slug_esc'
Loading history...
introduced by
Expected next thing to be a escaping function, not '$strings'
Loading history...
682
		                <input type="text" name="gv-note-subject" placeholder="<?php echo $strings['subject']; ?>" id="gv-note-subject-<?php echo $entry_slug_esc; ?>" value="" />
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$strings'
Loading history...
introduced by
Expected next thing to be a escaping function, not '$entry_slug_esc'
Loading history...
683
		            </div>
684
				</fieldset>
685
			</div>
686
		<?php }
687
688
		// TODO: Add a filter
689
		return ob_get_clean();
690
	}
691
692
	/**
693
	 * If note has an email to send, and the user has the right caps, send it
694
	 *
695
	 * @since 1.17
696
	 *
697
	 * @param false|object $note If note was created, object. Otherwise, false.
698
	 * @param array $entry Entry data
699
	 * @param array $data $_POST data
700
	 *
701
	 * @return void Tap in to Gravity Forms' `gform_after_email` action if you want a return result from sending the email.
702
	 */
703
	private function maybe_send_entry_notes( $note = false, $entry, $data ) {
704
705
		if( ! $note || ! GVCommon::has_cap('gravityview_email_entry_notes') ) {
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
706
			do_action( 'gravityview_log_debug', __METHOD__ . ': User doesnt have "gravityview_email_entry_notes" cap, or $note is empty', $note );
707
			return;
708
		}
709
710
		do_action( 'gravityview_log_debug', __METHOD__ . ': $data', $data );
711
712
		//emailing notes if configured
713
		if ( ! empty( $data['gv-note-to'] ) ) {
714
715
			$default_data = array(
716
				'gv-note-to' => '',
717
				'gv-note-to-custom' => '',
718
				'gv-note-subject' => '',
719
				'gv-note-content' => '',
720
                'current-url' => '',
721
			);
722
723
			$current_user  = wp_get_current_user();
0 ignored issues
show
introduced by
Overridding WordPress globals is prohibited
Loading history...
724
			$email_data = wp_parse_args( $data, $default_data );
725
726
			$from    = $current_user->user_email;
727
			$to = $email_data['gv-note-to'];
728
729
			/**
730
			 * Documented in get_note_email_fields
731
			 * @see get_note_email_fields
732
			 */
733
			$include_custom = apply_filters( 'gravityview/field/notes/custom-email', true );
734
735
			if( 'custom' === $to && $include_custom ) {
736
				$to = $email_data['gv-note-to-custom'];
737
				do_action( 'gravityview_log_debug', __METHOD__ . ': Sending note to a custom email address: ' . $to );
738
			}
739
740
			if ( ! GFCommon::is_valid_email_list( $to ) ) {
741
				do_action( 'gravityview_log_error', __METHOD__ . ': $to not a valid email or email list (CSV of emails): ' . print_r( $to, true ), $email_data );
0 ignored issues
show
introduced by
The use of function print_r() is discouraged
Loading history...
742
				return;
743
			}
744
745
			$bcc = false;
746
			$reply_to = $from;
747
			$subject = trim( $email_data['gv-note-subject'] );
748
749
			// We use empty() here because GF uses empty to check against, too. `0` isn't a valid subject to GF
750
			$subject = empty( $subject ) ? self::strings( 'default-email-subject' ) : $subject;
751
			$message = $email_data['gv-note-content'];
752
			$email_footer = self::strings( 'email-footer' );
753
			$from_name     = $current_user->display_name;
754
			$message_format = 'html';
755
756
			/**
757
			 * @filter `gravityview/field/notes/email_content` Modify the values passed when sending a note email
758
			 * @see GVCommon::send_email
759
			 * @since 1.17
760
			 * @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'
761
			 */
762
			$email_content = apply_filters( 'gravityview/field/notes/email_content', compact( 'from', 'to', 'bcc', 'reply_to', 'subject', 'message', 'from_name', 'message_format', 'entry', 'email_footer' ) );
763
764
			extract( $email_content );
0 ignored issues
show
introduced by
extract() usage is highly discouraged, due to the complexity and unintended issues it might cause.
Loading history...
765
766
			$is_html = ( 'html' === $message_format );
767
768
			// Add the message footer
769
			$message .= $this->get_email_footer( $email_footer, $is_html, $email_data );
770
771
			/**
772
             * @filter `gravityview/field/notes/wpautop_email` Should the message content have paragraphs added automatically, if using HTML message format
773
			 * @since 1.18
774
             * @param bool $wpautop_email True: Apply wpautop() to the email message if using; False: Leave as entered (Default: true)
775
			 */
776
			$wpautop_email = apply_filters( 'gravityview/field/notes/wpautop_email', true );
777
778
			if ( $is_html && $wpautop_email ) {
779
				$message = wpautop( $message );
780
			}
781
782
			GVCommon::send_email( $from, $to, $bcc, $reply_to, $subject, $message, $from_name, $message_format, '', $entry, false );
783
784
			$form  = isset( $entry['form_id'] ) ? GFAPI::get_form( $entry['form_id'] ) : array();
785
786
			/**
787
			 * @see https://www.gravityhelp.com/documentation/article/10146-2/ It's here for compatibility with Gravity Forms
788
			 */
789
			do_action( 'gform_post_send_entry_note', __METHOD__, $to, $from, $subject, $message, $form, $entry );
790
		}
791
	}
792
793
	/**
794
     * Get the footer for Entry Note emails
795
     *
796
     * `{url}` is replaced by the URL of the page where the note form was embedded
797
     *
798
     * @since 1.18
799
     * @see GravityView_Field_Notes::strings The default value of $message_footer is set here, with the key 'email-footer'
800
	 *
801
	 * @param string $email_footer The message footer value
802
	 * @param bool $is_html True: Email is being sent as HTML; False: sent as text
803
	 *
804
	 * @return string If email footer is not empty, return the message with placeholders replaced with dynamic values
805
	 */
806
	private function get_email_footer( $email_footer = '', $is_html = true, $email_data = array() ) {
807
808
	    $output = '';
809
810
		if( ! empty( $email_footer ) ) {
811
		    $url = rgar( $email_data, 'current-url' );
812
			$url = html_entity_decode( $url );
813
			$url = site_url( $url );
814
815
			$content = $is_html ? "<a href='{$url}'>{$url}</a>" : $url;
816
817
			$email_footer = str_replace( '{url}', $content, $email_footer );
818
819
			$output .= "\n\n$email_footer";
820
		}
821
822
		return $output;
823
	}
824
}
825
826
new GravityView_Field_Notes;
827