Completed
Pull Request — master (#716)
by Zack
09:51 queued 04:52
created

GravityView_Field_Notes::strings()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 39
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 1 Features 1
Metric Value
cc 3
eloc 27
c 5
b 1
f 1
nc 3
nop 1
dl 0
loc 39
rs 8.8571
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 767.

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