Completed
Push — develop ( 959baa...316eab )
by Gennady
23:18 queued 04:51
created

Widget::add_shortcode()   B

Complexity

Conditions 8
Paths 5

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 9.953

Importance

Changes 0
Metric Value
cc 8
nc 5
nop 0
dl 0
loc 26
ccs 11
cts 16
cp 0.6875
crap 9.953
rs 8.4444
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 14 and the first side effect is on line 6.

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
namespace GV;
3
4
/** If this file is called directly, abort. */
5
if ( ! defined( 'GRAVITYVIEW_DIR' ) ) {
6
	die();
7
}
8
9
/**
10
 * The \GV\Widget class.
11
 *
12
 * An interface that most GravityView widgets would want to adhere to and inherit from.
13
 */
14
abstract class Widget {
15
	/**
16
	 * Widget admin label
17
	 * @var string
18
	 */
19
	protected $widget_label = '';
20
21
	/**
22
	 * Widget description, shown on the "+ Add Widget" picker
23
	 * @var  string
24
	 */
25
	protected $widget_description = '';
26
27
	/**
28
	 * Widget details, shown in the widget lightbox
29
	 * @since 1.8
30
	 * @var  string
31
	 */
32
	protected $widget_subtitle = '';
33
34
	/**
35
	 * Widget admin ID
36
	 * @var string
37
	 */
38
	protected $widget_id = '';
39
40
	/**
41
	 * Default configuration for header and footer
42
	 * @var array
43
	 */
44
	protected $defaults = array();
45
46
	/**
47
	 * Widget admin advanced settings
48
	 * @var array
49
	 */
50
	protected $settings = array();
51
52
	/**
53
	 * Allow class to automatically add widget_text filter for you in shortcode
54
	 * @var string
55
	 */
56
	protected $shortcode_name;
57
58
	/**
59
	 * Hold the widget options.
60
	 * @var array()
61
	 */
62
	private $widget_options = array();
0 ignored issues
show
Unused Code introduced by
The property $widget_options is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
63
64
	/**
65
	 * The position of the widget.
66
	 * @api
67
	 * @since 2.0
68
	 * @var string
69
	 */
70
	public $position = '';
71
72
	/**
73
	 * A unique ID for this widget.
74
	 * @api
75
	 * @since 2.0
76
	 * @var string
77
	 */
78
	public $UID = '';
79
80
	/**
81
	 * The actual configuration for this widget instance.
82
	 *
83
	 * @api
84
	 * @since 2.0
85
	 * @var \GV\Settings
86
	 */
87
	public $configuration;
88
89
	/**
90
	 * Constructor.
91
	 *
92
	 * @param string $label The Widget label as shown in the admin.
93
	 * @param string $id The Widget ID, make this something unique.
94
	 * @param array $defaults Default footer/header Widget configuration.
95
	 * @param array $settings Advanced Widget settings.
96
	 *
97
	 * @return \GV\Widget
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
98
	 */
99 36
	public function __construct( $label, $id, $defaults = array(), $settings = array() ) {
100
		/**
101
		 * The shortcode name is set to the lowercase name of the widget class, unless overridden by the class specifying a different value for $shortcode_name
102
		 * @var string
103
		 */
104 36
		$this->shortcode_name = empty( $this->shortcode_name ) ? strtolower( get_called_class() ) : $this->shortcode_name;
105
106 36
		if ( $id ) {
107 5
			$this->widget_id = $id;
108
		}
109
110 36
		$this->widget_label = $label;
111 36
		$this->defaults = array_merge( array( 'header' => 0, 'footer' => 0 ), $defaults );
112
113
		// Make sure every widget has a title, even if empty
114 36
		$this->settings = wp_parse_args( $settings, $this->get_default_settings() );
115
116
		// Hook once per unique ID
117 36
		if ( $this->is_registered() ) {
118 35
			return;
119
		}
120
121
		// widget options
122 2
		add_filter( 'gravityview_template_widget_options', array( $this, 'assign_widget_options' ), 10, 3 );
123
124
		// frontend logic
125 2
		add_action( sprintf( 'gravityview/widgets/%s/render', $this->get_widget_id() ), array( $this, 'render_frontend' ), 10, 3 );
126
127
		// register shortcodes
128 2
		add_action( 'wp', array( $this, 'add_shortcode' ) );
129
130
		// Use shortcodes in text widgets.
131 2
		add_filter( 'widget_text', array( $this, 'maybe_do_shortcode' ) );
132
133
		// register widgets to be listed in the View Configuration
134
		// Important: this has to be the last filter/action added in the constructor.
135 2
		add_filter( 'gravityview/widgets/register', array( $this, 'register_widget' ) );
136 2
	}
137
138
	/**
139
	 * Define general widget settings
140
	 * @since 1.5.4
141
	 * @return array $settings Default settings
142
	 */
143 36
	protected function get_default_settings() {
144 36
		$settings = array();
145
146
		/**
147
		 * @filter `gravityview/widget/enable_custom_class` Enable custom CSS class settings for widgets
148
		 * @param boolean $enable_custom_class False by default. Return true if you want to enable.
149
		 * @param \GV\Widget $this Current instance of \GV\Widget.
150
		 */
151 36
		$enable_custom_class = apply_filters( 'gravityview/widget/enable_custom_class', false, $this );
152
153 36
		if ( $enable_custom_class ) {
154 1
			$settings['custom_class'] = array(
155 1
				'type' => 'text',
156 1
				'label' => __( 'Custom CSS Class:', 'gravityview' ),
157 1
				'desc' => __( 'This class will be added to the widget container', 'gravityview' ),
158 1
				'value' => '',
159
				'merge_tags' => true,
160
			);
161
		}
162
163 36
		return $settings;
164
	}
165
166
    /**
167
	 * Get the Widget ID.
168
	 *
169
     * @return string The Widget ID.
170
     */
171 38
    public function get_widget_id() {
172 38
        return $this->widget_id;
173
    }
174
175
	/**
176
	 * Get the widget settings
177
	 *
178
	 * @return array|null Settings array; NULL if not set for some reason.
179
	 */
180 1
	public function get_settings() {
181 1
		return empty( $this->settings ) ? null : $this->settings;
182
	}
183
184
	/**
185
	 * Get a setting by the setting key.
186
	 *
187
	 * @param  string $key Key for the setting
188
	 *
189
	 * @todo Use the \GV\Settings class later. For now subclasses may still expect and array instead.
190
	 *
191
	 * @return mixed|null Value of the setting; NULL if not set
192
	 */
193 1
	public function get_setting( $key ) {
194 1
		return Utils::get( $this->settings, $key, null );
195
	}
196
197
	/**
198
	 * Default widget areas.
199
	 *
200
	 * Usually overridden by the selected template.
201
	 *
202
	 * @return array The default areas where widgets can be rendered.
203
	 */
204 6
	public static function get_default_widget_areas() {
205
		$default_areas = array(
206 6
			array( '1-1' => array( array( 'areaid' => 'top', 'title' => __( 'Top', 'gravityview' ) , 'subtitle' => '' ) ) ),
0 ignored issues
show
introduced by
Expected 0 spaces between ")" and comma; 1 found
Loading history...
207 6
			array( '1-2' => array( array( 'areaid' => 'left', 'title' => __( 'Left', 'gravityview' ) , 'subtitle' => '' ) ), '2-2' => array( array( 'areaid' => 'right', 'title' => __( 'Right', 'gravityview' ) , 'subtitle' => '' ) ) ),
0 ignored issues
show
introduced by
Expected 0 spaces between ")" and comma; 1 found
Loading history...
208
		);
209
210
		/**
211
		 * @filter `gravityview_widget_active_areas` Array of zones available for widgets to be dropped into
212
		 * @deprecated 2.0: Use gravityview/widget/active_areas instead
213
		 * @param array $default_areas Definition for default widget areas
214
		 */
215 6
		$default_areas = apply_filters( 'gravityview_widget_active_areas', $default_areas );
216
217
		/**
218
		 * @filter `gravityview/widget/active_areas` Array of zones available for widgets to be dropped into
219
		 * @since 2.0
220
		 * @param array $default_areas Definition for default widget areas
221
		 */
222 6
		return apply_filters( 'gravityview/widget/active_areas', $default_areas );
223
	}
224
225
	/**
226
	 * Register widget to become available in admin. And for lookup.
227
	 *
228
	 * @param  array $widgets Usually just empty. Used to gather them all up.
229
	 *
230
	 * @return array $widgets
231
	 */
232 36
	public function register_widget( $widgets ) {
233 36
		if ( ! is_array( $widgets ) ) {
234
			$widgets = array();
235
		}
236
237 36
		$widgets[ $this->get_widget_id() ] = array(
238 36
			'label' => $this->widget_label ,
0 ignored issues
show
introduced by
Expected 0 spaces between "widget_label" and comma; 1 found
Loading history...
239 36
			'description' => $this->widget_description,
240 36
			'subtitle' => $this->widget_subtitle,
241 36
			'class' => get_called_class(),
242
		);
243
244 36
		return $widgets;
245
	}
246
247
	/**
248
	 * Assign template specific widget options
249
	 *
250
	 * @access protected
251
	 *
252
	 * @param array $options (default: array())
253
	 * @param string $template (default: '')
254
	 *
255
	 * @return array
256
	 */
257 1
	public function assign_widget_options( $options = array(), $template = '', $widget = '' ) {
258 1
		if ( $this->get_widget_id() === $widget ) {
259 1
			if( $settings = $this->get_settings() ) {
260 1
				$options = array_merge( $options, $settings );
261
			}
262
		}
263 1
		return $options;
264
	}
265
266
	/**
267
	 * Do shortcode if the Widget's shortcode exists.
268
	 *
269
	 * @param  string $text   Widget text to check
270
	 * @param  null|\WP_Widget Empty if not called by WP_Widget, or a WP_Widget instance
271
	 *
272
	 * @return string         Widget text
273
	 */
274 1
	public function maybe_do_shortcode( $text, $widget = null ) {
0 ignored issues
show
Unused Code introduced by
The parameter $widget is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
275 1
		if ( ! empty( $this->shortcode_name ) && has_shortcode( $text, $this->shortcode_name ) ) {
276 1
			return do_shortcode( $text );
277
		}
278
		return $text;
279
	}
280
281
	/**
282
	 * Add $this->shortcode_name shortcode to output self::render_frontend()
283
	 *
284
	 * @return void
285
	 */
286 2
	public function add_shortcode() {
287 2
		if ( empty( $this->shortcode_name ) ) {
288
			return;
289
		}
290
291 2
		if ( gravityview()->request->is_admin() ) {
292
			return;
293
		}
294
295
		// If the widget shouldn't output on single entries, don't show it
296 2
		if ( empty( $this->show_on_single ) && gravityview()->request->is_entry() ) {
0 ignored issues
show
Bug introduced by
The property show_on_single does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
297 1
			gravityview()->log->debug( 'Skipping; set to not run on single entry.' );
298 1
			add_shortcode( $this->shortcode_name, '__return_null' );
299 1
			return;
300
		}
301
302 1
		global $post;
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...
303
304 1
		if ( ! is_object( $post ) || empty( $post->post_content ) || ! Shortcode::parse( $post->post_content ) ) {
305
			gravityview()->log->debug( 'No shortcode present; not adding render_frontend shortcode.' );
306
			add_shortcode( $this->shortcode_name, '__return_null' );
307
			return;
308
		}
309
310 1
		add_shortcode( $this->shortcode_name, array( $this, 'render_shortcode') );
0 ignored issues
show
introduced by
No space before closing parenthesis of array is bad style
Loading history...
311 1
	}
312
313
	/**
314
	 * Frontend logic.
315
	 *
316
	 * Override in child class.
317
	 *
318
	 * @param array $widget_args The Widget shortcode args.
319
	 * @param string $content The content.
320
	 * @param string|\GV\Template_Context $context The context, if available.
321
	 *
322
	 * @return void
323
	 */
324
	public function render_frontend( $widget_args, $content = '', $context = '' ) {
325
	}
326
327
	/**
328
	 * General validations when rendering the widget
329
	 *
330
	 * Always call this from your `render_frontend()` override!
331
	 *
332
	 * @return boolean True: render frontend; False: don't render frontend
333
	 */
334 4
	public function pre_render_frontend() {
335
		/**
336
		 * Assume shown regardless of hide_until_search setting.
337
		 */
338
		$whitelist = array(
339 4
			'custom_content',
340
		);
341
342
		/**
343
		 * @filter `gravityview/widget/hide_until_searched/whitelist` Some widgets have got to stay shown.
344
		 * @param[in,out] string[] $whitelist The widget IDs that have to be shown by default.
345
		 */
346 4
		$whitelist = apply_filters( 'gravityview/widget/hide_until_searched/whitelist', $whitelist );
347
348 4
		if ( ( $view = gravityview()->views->get() ) && ! in_array( $this->get_widget_id(), $whitelist ) ) {
0 ignored issues
show
Documentation introduced by
The property views does not exist on object<GV\Core>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
349 4
			$hide_until_searched = $view->settings->get( 'hide_until_searched' );
350
		} else {
351 2
			$hide_until_searched = false;
352
		}
353
354
		/**
355
		 * @filter `gravityview/widget/hide_until_searched` Modify whether to hide content until search
356
		 * @param boolean $hide_until_searched Hide until search?
357
		 * @param \GV\Widget $this Widget instance
358
		 */
359 4
		$hide_until_searched = apply_filters( 'gravityview/widget/hide_until_searched', $hide_until_searched, $this );
360
361 4
		if ( $hide_until_searched && ! gravityview()->request->is_search() ) {
362 1
			gravityview()->log->debug( 'Hide View data until search is performed' );
363 1
			return false;
364
		}
365
366 4
		return true;
367
	}
368
369
	/**
370
	 * Shortcode.
371
	 *
372
	 * @param array $atts The Widget shortcode args.
373
	 * @param string $content The content.
374
	 * @param string $context The context, if available.
375
	 *
376
	 * @return string Whatever the widget echoed.
377
	 */
378 1
	public function render_shortcode( $atts, $content = '', $context = '' ) {
379 1
		ob_start();
380 1
		$this->render_frontend( $atts, $content, $context );
381 1
		return ob_get_clean();
382
	}
383
384
	/**
385
	 * Create the needed widget from a configuration array.
386
	 *
387
	 * @param array $configuration The configuration array.
388
	 * @see \GV\Widget::as_configuration()
389
	 * @internal
390
	 * @since 2.0
391
	 *
392
	 * @return \GV\Widget|null The widget implementation from configuration or none.
393
	 */
394 35
	public static function from_configuration( $configuration ) {
395 35
		$registered_widgets = self::registered();
396
397 35
		if ( ! $id = Utils::get( $configuration, 'id' ) ) {
398
			return null;
399
		}
400
401 35
		if ( ! $widget = Utils::get( $registered_widgets, $id ) ) {
402
			return null;
403
		}
404
405 35
		if ( ! class_exists( $class = Utils::get( $widget, 'class' ) ) ) {
406
			return null;
407
		}
408
409 35
		$w = new $class( Utils::get( $widget, 'label' ), $id );
410 35
		$w->configuration = new Settings( $configuration );
411
412 35
		return $w;
413
	}
414
415
	/**
416
	 * Return an array of the old format.
417
	 *
418
	 *  		'id' => string
419
	 *			+ whatever else specific fields may have
420
	 *
421
	 * @internal
422
	 * @since 2.0
423
	 *
424
	 * @return array
425
	 */
426 6
	public function as_configuration() {
427 6
		return array_merge( array(
428 6
			'id' => $this->get_widget_id(),
429 6
		), $this->configuration->all() );
430
	}
431
432
	/**
433
	 * Return all registered widgets.
434
	 *
435
	 * @api
436
	 * @since 2.0
437
	 *
438
	 * @return array
439
	 */
440 36
	public static function registered() {
441
		/**
442
		 * @filter `gravityview_register_directory_widgets` Get the list of registered widgets. Each item is used to instantiate a GravityView_Admin_View_Widget object
443
		 * @deprecated Use `gravityview/widgets/register`
444
		 * @param array $registered_widgets Empty array
445
		 */
446 36
		$registered_widgets = apply_filters( 'gravityview_register_directory_widgets', array() );
447
448
		/**
449
		 * @filter `gravityview/widgets/register` Each item is used to instantiate a GravityView_Admin_View_Widget object
450
		 * @param array $registered_widgets Empty array
451
		 */
452 36
		return apply_filters( 'gravityview/widgets/register', $registered_widgets );
453
	}
454
455
	/**
456
	 * Whether this Widget's been registered already or not.
457
	 *
458
	 * @api
459
	 * @since 2.0
460
	 *
461
	 * @return bool
462
	 */
463 36
	public function is_registered() {
464 36
		if ( ! $widget_id = $this->get_widget_id() ) {
465
			gravityview()->log->warning( 'Widget ID not set before calling Widget::is_registered', array( 'data' => $this ) );
466
			return false;
467
		}
468 36
		return in_array( $widget_id, array_keys( self::registered() ), true );
469
	}
470
}
471