Completed
Push — master ( 1d71fa...5b2818 )
by J.D.
03:48
created

functions.php ➔ wordpoints_hooks_api_admin_menu()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 37
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 3
eloc 19
c 2
b 1
f 0
nc 4
nop 0
dl 0
loc 37
rs 8.8571
1
<?php
2
3
/**
4
 * Administration side functions.
5
 *
6
 * @package wordpoints-hooks-api
7
 * @since 1.0.0
8
 */
9
10
/**
11
 * Register the admin apps when the main app is initialized.
12
 *
13
 * @since 1.0.0
14
 *
15
 * @WordPress\action wordpoints_init_app-apps
16
 *
17
 * @param WordPoints_App $app The main WordPoints app.
18
 */
19
function wordpoints_hooks_register_admin_apps( $app ) {
20
21
	$apps = $app->sub_apps();
22
23
	$apps->register( 'admin', 'WordPoints_App' );
24
25
	/** @var WordPoints_App $admin */
26
	$admin = $apps->get( 'admin' );
27
28
	$admin->sub_apps()->register( 'screen', 'WordPoints_Admin_Screens' );
29
}
30
31
/**
32
 * Register the admin screens.
33
 *
34
 * @since 1.0.0
35
 *
36
 * @WordPress\action admin_menu
37
 * @WordPress\action network_admin_menu
38
 */
39
function wordpoints_hooks_api_admin_menu() {
40
41
	$wordpoints_menu = wordpoints_get_main_admin_menu();
42
43
	/** @var WordPoints_Admin_Screens $admin_screens */
44
	$admin_screens = wordpoints_apps()->get_sub_app( 'admin' )->get_sub_app(
45
		'screen'
46
	);
47
48
	// Hooks page.
49
	$id = add_submenu_page(
50
		$wordpoints_menu
51
		, __( 'WordPoints — Points Types', 'wordpoints' )
52
		, __( 'Points Types', 'wordpoints' )
53
		, 'manage_options'
54
		, 'wordpoints_points_types'
55
		, array( $admin_screens, 'display' )
56
	);
57
58
	if ( $id ) {
59
		$admin_screens->register( $id, 'WordPoints_Admin_Screen_Points_Types' );
60
	}
61
62
	// Remove the old hooks screen if not needed.
63
	$disabled_hooks = wordpoints_get_maybe_network_array_option(
64
		'wordpoints_legacy_points_hooks_disabled'
65
		, is_network_admin()
66
	);
67
68
	$hooks = WordPoints_Points_Hooks::get_handlers();
69
70
	// If all of the registered hooks have been imported and disabled, then there is
71
	// no need to keep the old hooks screen.
72
	if ( ! array_diff_key( $hooks, $disabled_hooks ) ) {
73
		remove_submenu_page( $wordpoints_menu, 'wordpoints_points_hooks' );
74
	}
75
}
76
77
/**
78
 * Register module admin scripts.
79
 *
80
 * @since 1.0.0
81
 *
82
 * @WordPress\action admin_init
83
 */
84
function wordpoints_hooks_admin_register_scripts() {
85
86
	$assets_url = wordpoints_modules_url( '/assets', dirname( __FILE__ ) );
87
88
	// CSS
89
90
	wp_register_style(
91
		'wordpoints-hooks-admin'
92
		, $assets_url . '/css/hooks.css'
93
		, array( 'dashicons', 'wp-jquery-ui-dialog' )
94
		, WORDPOINTS_VERSION
95
	);
96
97
	// JS
98
99
	wp_register_script(
100
		'wordpoints-admin-points-types'
101
		, $assets_url . '/js/points-types.js'
102
		, array( 'backbone', 'jquery-ui-dialog', 'wp-util' )
103
		, WORDPOINTS_VERSION
104
	);
105
106
	wp_register_script(
107
		'wordpoints-hooks-models'
108
		, $assets_url . '/js/hooks/models.js'
109
		, array( 'backbone', 'jquery-ui-dialog', 'wp-util' )
110
		, WORDPOINTS_VERSION
111
	);
112
113
	wp_register_script(
114
		'wordpoints-hooks-views'
115
		, $assets_url . '/js/hooks/views.js'
116
		, array( 'wordpoints-hooks-models' )
117
		, WORDPOINTS_VERSION
118
	);
119
120
	wp_localize_script(
121
		'wordpoints-admin-points-types'
122
		, 'WordPointsPointsTypesL10n'
123
		, array(
124
			'confirmDelete' => esc_html__( 'Are you sure that you want to delete this points type? This will delete all related logs and event hooks.', 'wordpoints' )
125
			                   . ' ' . esc_html__( 'Once a points type has been deleted, you cannot bring it back.', 'wordpoints' ),
126
			'confirmTitle'  => esc_html__( 'Are you sure?', 'wordpoints' ),
127
			'deleteText'    => esc_html__( 'Delete', 'wordpoints' ),
128
			'cancelText'    => esc_html__( 'Cancel', 'wordpoints' ),
129
		)
130
	);
131
132
	wp_localize_script(
133
		'wordpoints-hooks-views'
134
		, 'WordPointsHooksAdminL10n'
135
		, array(
136
			'unexpectedError' => __( 'There was an unexpected error. Try reloading the page.', 'wordpoints' ),
137
			'changesSaved'    => __( 'Your changes have been saved.', 'wordpoints' ),
138
			/* translators: the name of the field that cannot be empty */
139
			'emptyField'      => sprintf( __( '%s cannot be empty.', 'wordpoints' ), '{{ data.label }}' ),
140
			'confirmDelete'   => __( 'Are you sure that you want to delete this reaction? This action cannot be undone.', 'wordpoints' ),
141
			'confirmTitle'    => __( 'Are you sure?', 'wordpoints' ),
142
			'deleteText'      => __( 'Delete', 'wordpoints' ),
143
			'cancelText'      => __( 'Cancel', 'wordpoints' ),
144
			'separator'       => __( ' » ', 'wordpoints' ),
145
			'target_label'    => __( 'Target', 'wordpoints' ),
146
			// translators: form field
147
			'cannotBeChanged' => __( '(cannot be changed)', 'wordpoints' ),
148
			'fieldsInvalid'   => __( 'Error: the values of some fields are invalid. Please correct these and then try again.', 'wordpoints' ),
149
		)
150
	);
151
152
	wp_script_add_data(
153
		'wordpoints-hooks-views'
154
		, 'wordpoints-templates'
155
		, '
156
		<script type="text/template" id="tmpl-wordpoints-hook-reaction">
157
			<div class="view">
158
				<div class="title"></div>
159
				<button type="button" class="edit button-secondary">
160
					' . esc_html__( 'Edit', 'wordpoints' ) . '
161
				</button>
162
				<button type="button" class="close button-secondary">
163
					' . esc_html__( 'Close', 'wordpoints' ) . '
164
				</button>
165
			</div>
166
			<div class="form">
167
				<form>
168
					<div class="fields">
169
						<div class="settings"></div>
170
						<div class="target"></div>
171
					</div>
172
					<div class="messages">
173
						<div class="success"></div>
174
						<div class="err"></div>
175
					</div>
176
					<div class="actions">
177
						<div class="spinner-overlay">
178
							<span class="spinner is-active"></span>
179
						</div>
180
						<div class="action-buttons">
181
							<button type="button" class="save button-primary" disabled>
182
								' . esc_html__( 'Save', 'wordpoints' ) . '
183
							</button>
184
							<button type="button" class="cancel button-secondary">
185
								' . esc_html__( 'Cancel', 'wordpoints' ) . '
186
							</button>
187
							<button type="button" class="close button-secondary">
188
								' . esc_html__( 'Close', 'wordpoints' ) . '
189
							</button>
190
							<button type="button" class="delete button-secondary">
191
								' . esc_html__( 'Delete', 'wordpoints' ) . '
192
							</button>
193
						</div>
194
					</div>
195
				</form>
196
			</div>
197
		</script>
198
199
		<script type="text/template" id="tmpl-wordpoints-hook-arg-selector">
200
			<div class="arg-selector">
201
				<label>
202
					{{ data.label }}
203
					<select name="{{ data.name }}"></select>
204
				</label>
205
			</div>
206
		</script>
207
208
		<script type="text/template" id="tmpl-wordpoints-hook-arg-option">
209
			<option value="{{ data.slug }}">{{ data.title }}</option>
210
		</script>
211
212
		<script type="text/template" id="tmpl-wordpoints-hook-reaction-field">
213
			<p class="description description-thin">
214
				<label>
215
					{{ data.label }}
216
					<input type="{{ data.type }}" class="widefat" name="{{ data.name }}"
217
					       value="{{ data.value }}"/>
218
				</label>
219
			</p>
220
		</script>
221
222
		<script type="text/template" id="tmpl-wordpoints-hook-reaction-select-field">
223
			<p class="description description-thin">
224
				<label>
225
					{{ data.label }}
226
					<select name="{{ data.name }}" class="widefat"></select>
227
				</label>
228
			</p>
229
		</script>
230
231
		<script type="text/template" id="tmpl-wordpoints-hook-reaction-hidden-field">
232
			<input type="hidden" name="{{ data.name }}" value="{{ data.value }}"/>
233
		</script>
234
		'
235
	);
236
237
	wp_register_script(
238
		'wordpoints-hooks-reactor-points'
239
		, $assets_url . '/js/hooks/reactors/points.js'
240
		, array( 'wordpoints-hooks-views' )
241
		, WORDPOINTS_VERSION
242
	);
243
244
	wp_register_script(
245
		'wordpoints-hooks-extension-conditions'
246
		, $assets_url . '/js/hooks/extensions/conditions.js'
247
		, array( 'wordpoints-hooks-views' )
248
		, WORDPOINTS_VERSION
249
	);
250
251
	wp_script_add_data(
252
		'wordpoints-hooks-extension-conditions'
253
		, 'wordpoints-templates'
254
		, '
255
			<script type="text/template" id="tmpl-wordpoints-hook-condition-groups">
256
				<div class="conditions-title section-title">
257
					<h4>' . esc_html__( 'Conditions', 'wordpoints' ) . '</h4>
258
					<button type="button" class="add-new button-secondary button-link">
259
						<span class="screen-reader-text">' . esc_html__( 'Add New Condition', 'wordpoints' ) . '</span>
260
						<span class="dashicons dashicons-plus"></span>
261
					</button>
262
				</div>
263
				<div class="add-condition-form hidden">
264
					<div class="no-conditions hidden">
265
						' . esc_html__( 'No conditions available.', 'wordpoints' ) . '
266
					</div>
267
					<div class="condition-selectors">
268
						<div class="arg-selectors"></div>
269
						<div class="condition-selector"></div>
270
					</div>
271
					<button type="button" class="confirm-add-new button-secondary" disabled aria-label="' . esc_attr__( 'Add Condition', 'wordpoints' ) . '">
272
						' . esc_html_x( 'Add', 'reaction condition', 'wordpoints' ) . '
273
					</button>
274
					<button type="button" class="cancel-add-new button-secondary" aria-label="' . esc_attr__( 'Cancel Adding New Condition', 'wordpoints' ) . '">
275
						' . esc_html_x( 'Cancel', 'reaction condition', 'wordpoints' ) . '
276
					</button>
277
				</div>
278
				<div class="condition-groups section-content"></div>
279
			</script>
280
281
			<script type="text/template" id="tmpl-wordpoints-hook-reaction-condition-group">
282
				<div class="condition-group-title"></div>
283
			</script>
284
285
			<script type="text/template" id="tmpl-wordpoints-hook-reaction-condition">
286
				<div class="condition-controls">
287
					<div class="condition-title"></div>
288
					<button type="button" class="delete button-secondary button-link">
289
						<span class="screen-reader-text">' . esc_html__( 'Remove Condition', 'wordpoints' ) . '</span>
290
						<span class="dashicons dashicons-no"></span>
291
					</button>
292
				</div>
293
				<div class="condition-settings"></div>
294
			</script>
295
296
			<script type="text/template" id="tmpl-wordpoints-hook-condition-selector">
297
				<label>
298
					{{ data.label }}
299
					<select name="{{ data.name }}"></select>
300
				</label>
301
			</script>
302
		'
303
	);
304
305
	wp_register_script(
306
		'wordpoints-hooks-extension-periods'
307
		, $assets_url . '/js/hooks/extensions/periods.js'
308
		, array( 'wordpoints-hooks-views' )
309
		, WORDPOINTS_VERSION
310
	);
311
312
	wp_script_add_data(
313
		'wordpoints-hooks-extension-periods'
314
		, 'wordpoints-templates'
315
		, '
316
			<script type="text/template" id="tmpl-wordpoints-hook-periods">
317
				<div class="periods-title section-title">
318
					<h4>' . esc_html__( 'Rate Limit', 'wordpoints' ) . '</h4>
319
				</div>
320
				<div class="periods section-content"></div>
321
			</script>
322
		'
323
	);
324
}
325
326
/**
327
 * Export the data for the scripts needed to make the hooks UI work.
328
 *
329
 * @since 1.0.0
330
 */
331
function wordpoints_hooks_ui_setup_script_data() {
332
333
	$hooks = wordpoints_hooks();
334
335
	$extensions_data = wordpoints_hooks_ui_get_script_data_from_objects(
336
		$hooks->get_sub_app( 'extensions' )->get_all()
337
		, 'extension'
338
	);
339
340
	$reactor_data = wordpoints_hooks_ui_get_script_data_from_objects(
341
		$hooks->get_sub_app( 'reactors' )->get_all()
342
		, 'reactor'
343
	);
344
345
	$event_action_types = wordpoints_hooks_ui_get_script_data_event_action_types();
346
	$entities_data = wordpoints_hooks_ui_get_script_data_entities();
347
348
	$data = array(
349
		'fields'     => (object) array(),
350
		'reactions'  => (object) array(),
351
		'events'     => (object) array(),
352
		'extensions' => $extensions_data,
353
		'entities'   => $entities_data,
354
		'reactors'   => $reactor_data,
355
		'event_action_types' => $event_action_types,
356
	);
357
358
	/**
359
	 * Filter the hooks data used to provide the UI.
360
	 *
361
	 * This is currently exported as JSON to the Backbone.js powered UI. But
362
	 * that could change in the future. The important thing is that the data is
363
	 * bing exported and will be used by something somehow.
364
	 *
365
	 * @param array $data The data.
366
	 */
367
	$data = apply_filters( 'wordpoints_hooks_ui_data', $data );
368
369
	wp_localize_script(
370
		'wordpoints-hooks-models'
371
		, 'WordPointsHooksAdminData'
372
		, $data
373
	);
374
}
375
376
/**
377
 * Get the UI script data from a bunch of objects.
378
 *
379
 * @since 1.0.0
380
 *
381
 * @param object[] $objects Objects that might provide script UI data.
382
 * @param string   $type    The type of objects. Used to automatically enqueue
383
 *                          scripts for the objects.
384
 *
385
 * @return array The data extracted from the objects.
386
 */
387
function wordpoints_hooks_ui_get_script_data_from_objects( $objects, $type ) {
388
389
	$data = array();
390
391
	foreach ( $objects as $slug => $object ) {
392
393
		if ( $object instanceof WordPoints_Hook_UI_Script_Data_ProviderI ) {
394
			$data[ $slug ] = $object->get_ui_script_data();
395
		}
396
397
		if ( wp_script_is( "wordpoints-hooks-{$type}-{$slug}", 'registered' ) ) {
398
			wp_enqueue_script( "wordpoints-hooks-{$type}-{$slug}" );
399
		}
400
	}
401
402
	return $data;
403
}
404
405
/**
406
 * Get the entities data for use in the hooks UI.
407
 *
408
 * @since 1.0.0
409
 *
410
 * @return array The entities data for use in the hooks UI.
411
 */
412
function wordpoints_hooks_ui_get_script_data_entities() {
413
414
	$entities = wordpoints_entities();
415
416
	$entities_data = array();
417
418
	/** @var WordPoints_Class_Registry_Children $entity_children */
419
	$entity_children = $entities->get_sub_app( 'children' );
420
421
	/** @var WordPoints_Entity $entity */
422
	foreach ( $entities->get_all() as $slug => $entity ) {
423
424
		$child_data = array();
425
426
		/** @var WordPoints_EntityishI $child */
427
		foreach ( $entity_children->get_children( $slug ) as $child_slug => $child ) {
428
429
			$child_data[ $child_slug ] = array(
430
				'slug'  => $child_slug,
431
				'title' => $child->get_title(),
432
			);
433
434
			if ( $child instanceof WordPoints_Entity_Attr ) {
435
436
				$child_data[ $child_slug ]['_type']     = 'attr';
437
				$child_data[ $child_slug ]['data_type'] = $child->get_data_type();
438
439
			} elseif ( $child instanceof WordPoints_Entity_Relationship ) {
440
441
				$child_data[ $child_slug ]['_type']     = 'relationship';
442
				$child_data[ $child_slug ]['primary']   = $child->get_primary_entity_slug();
443
				$child_data[ $child_slug ]['secondary'] = $child->get_related_entity_slug();
444
			}
445
446
			/**
447
			 * Filter the data for an entity child.
448
			 *
449
			 * Entity children include attributes and relationships.
450
			 *
451
			 * @param array                $data  The data for the entity child.
452
			 * @param WordPoints_Entityish $child The child's object.
453
			 */
454
			$child_data[ $child_slug ] = apply_filters(
455
				'wordpoints_hooks_ui_data_entity_child'
456
				, $child_data[ $child_slug ]
457
				, $child
458
			);
459
		}
460
461
		$entities_data[ $slug ] = array(
462
			'slug'     => $slug,
463
			'title'    => $entity->get_title(),
464
			'children' => $child_data,
465
			'id_field' => $entity->get_id_field(),
466
			'_type'    => 'entity',
467
		);
468
469
		if ( $entity instanceof WordPoints_Entity_EnumerableI ) {
470
471
			$values = array();
472
473
			foreach ( $entity->get_enumerated_values() as $value ) {
474
				if ( $entity->set_the_value( $value ) ) {
475
					$values[] = array(
476
						'value' => $entity->get_the_id(),
477
						'label' => $entity->get_the_human_id(),
478
					);
479
				}
480
			}
481
482
			$entities_data[ $slug ]['values'] = $values;
483
		}
484
485
		/**
486
		 * Filter the data for an entity.
487
		 *
488
		 * @param array             $data   The data for the entity.
489
		 * @param WordPoints_Entity $entity The entity object.
490
		 */
491
		$entities_data[ $slug ] = apply_filters(
492
			'wordpoints_hooks_ui_data_entity'
493
			, $entities_data[ $slug ]
494
			, $entity
495
		);
496
	}
497
498
	return $entities_data;
499
}
500
501
/**
502
 * Get a list of action types for each event for the hooks UI script data.
503
 *
504
 * @since 1.0.0
505
 *
506
 * @return array The event action types.
507
 */
508
function wordpoints_hooks_ui_get_script_data_event_action_types() {
509
510
	// We want a list of the action types for each event. We can start with this list
511
	// but it is indexed by action slug and then action type and then event slug, so
512
	// we ned to do some processing.
513
	$event_index = wordpoints_hooks()->get_sub_app( 'router' )->get_event_index();
514
515
	// We don't care about the action slugs, so first we get rid of that bottom level
516
	// of the array.
517
	$event_index = call_user_func_array( 'array_merge_recursive', $event_index );
518
519
	$event_action_types = array();
520
521
	// This leaves us the event indexed by action type. But we actually need to flip
522
	// this, so that we have the action types indexed by event slug.
523
	foreach ( $event_index as $action_type => $events ) {
524
		foreach ( $events as $event => $unused ) {
525
			$event_action_types[ $event ][ $action_type ] = true;
526
		}
527
	}
528
529
	return $event_action_types;
530
}
531
532
/**
533
 * Append templates registered in wordpoints-templates script data to scripts.
534
 *
535
 * One day templates will probably be stored in separate files instead.
536
 *
537
 * @link https://core.trac.wordpress.org/ticket/31281
538
 *
539
 * @since 1.0.0
540
 *
541
 * @WordPress\filter script_loader_tag
542
 *
543
 * @param string $html   The HTML for the script.
544
 * @param string $handle The handle of the script.
545
 *
546
 * @return string The HTML with templates appended.
547
 */
548
function wordpoints_script_templates_filter( $html, $handle ) {
549
550
	global $wp_scripts;
551
552
	$templates = $wp_scripts->get_data( $handle, 'wordpoints-templates' );
553
554
	if ( $templates ) {
555
		$html .= $templates;
556
	}
557
558
	return $html;
559
}
560
561
/**
562
 * Initialize the Ajax actions.
563
 *
564
 * @since 1.0.0
565
 *
566
 * @WordPress\action admin_init
567
 */
568
function wordpoints_hooks_admin_ajax() {
569
570
	if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
571
		new WordPoints_Admin_Ajax_Hooks;
572
	}
573
}
574
575
// EOF
576