Completed
Pull Request — master (#1131)
by Gennady
24:11 queued 19:32
created

Widget::render_frontend()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 3
dl 0
loc 2
ccs 0
cts 2
cp 0
crap 2
rs 10
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 10
	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 10
		$this->shortcode_name = empty( $this->shortcode_name ) ? strtolower( get_called_class() ) : $this->shortcode_name;
105
106 10
		if ( $id ) {
107 5
			$this->widget_id = $id;
108
		}
109
110 10
		$this->widget_label = $label;
111 10
		$this->defaults = array_merge( array( 'header' => 0, 'footer' => 0 ), $defaults );
112
113
		// Make sure every widget has a title, even if empty
114 10
		$this->settings = wp_parse_args( $settings, $this->get_default_settings() );
115
116
		// Hook once per unique ID
117 10
		if ( $this->is_registered() ) {
118 9
			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 10
	protected function get_default_settings() {
144 10
		$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 10
		$enable_custom_class = apply_filters( 'gravityview/widget/enable_custom_class', false, $this );
152
153 10
		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 10
		return $settings;
164
	}
165
166
    /**
167
	 * Get the Widget ID.
168
	 *
169
     * @return string The Widget ID.
170
     */
171 10
    public function get_widget_id() {
172 10
        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 Use gravityview/widget/active_areas
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
		 * @param array $default_areas Definition for default widget areas
220
		 */
221 6
		return apply_filters( 'gravityview/widget/active_areas', $default_areas );
222
	}
223
224
	/**
225
	 * Register widget to become available in admin. And for lookup.
226
	 *
227
	 * @param  array $widgets Usually just empty. Used to gather them all up.
228
	 *
229
	 * @return array $widgets
230
	 */
231 10
	public function register_widget( $widgets ) {
232 10
		if ( ! is_array( $widgets ) ) {
233
			$widgets = array();
234
		}
235
236 10
		$widgets[ $this->get_widget_id() ] = array(
237 10
			'label' => $this->widget_label ,
0 ignored issues
show
introduced by
Expected 0 spaces between "widget_label" and comma; 1 found
Loading history...
238 10
			'description' => $this->widget_description,
239 10
			'subtitle' => $this->widget_subtitle,
240 10
			'class' => get_called_class(),
241
		);
242
243 10
		return $widgets;
244
	}
245
246
	/**
247
	 * Assign template specific widget options
248
	 *
249
	 * @access protected
250
	 *
251
	 * @param array $options (default: array())
252
	 * @param string $template (default: '')
253
	 *
254
	 * @return array
255
	 */
256 1
	public function assign_widget_options( $options = array(), $template = '', $widget = '' ) {
257 1
		if ( $this->get_widget_id() === $widget ) {
258 1
			if( $settings = $this->get_settings() ) {
259 1
				$options = array_merge( $options, $settings );
260
			}
261
		}
262 1
		return $options;
263
	}
264
265
	/**
266
	 * Do shortcode if the Widget's shortcode exists.
267
	 *
268
	 * @param  string $text   Widget text to check
269
	 * @param  null|WP_Widget Empty if not called by WP_Widget, or a WP_Widget instance
270
	 *
271
	 * @return string         Widget text
272
	 */
273 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...
274 1
		if ( ! empty( $this->shortcode_name ) && has_shortcode( $text, $this->shortcode_name ) ) {
275 1
			return do_shortcode( $text );
276
		}
277
		return $text;
278
	}
279
280
	/**
281
	 * Add $this->shortcode_name shortcode to output self::render_frontend()
282
	 *
283
	 * @return void
284
	 */
285 1
	public function add_shortcode() {
286 1
		if ( empty( $this->shortcode_name ) ) {
287
			return;
288
		}
289
290 1
		if ( gravityview()->request->is_admin() ) {
291
			return;
292
		}
293
294
		// If the widget shouldn't output on single entries, don't show it
295 1
		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...
296
			gravityview()->log->debug( 'Skipping; set to not run on single entry.' );
297
			add_shortcode( $this->shortcode_name, '__return_null' );
298
			return;
299
		}
300
301 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...
302
303 1
		if ( ! is_object( $post ) || empty( $post->post_content ) || ! Shortcode::parse( $post->post_content ) ) {
304
			gravityview()->log->debug( 'No shortcode present; not adding render_frontend shortcode.' );
305
			add_shortcode( $this->shortcode_name, '__return_null' );
306
			return;
307
		}
308
309 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...
310 1
	}
311
312
	/**
313
	 * Frontend logic.
314
	 *
315
	 * Override in child class.
316
	 *
317
	 * @param array $widget_args The Widget shortcode args.
318
	 * @param string $content The content.
319
	 * @param string|\GV\Template_Context $context The context, if available.
320
	 *
321
	 * @return void
322
	 */
323
	public function render_frontend( $widget_args, $content = '', $context = '' ) {
324
	}
325
326
	/**
327
	 * General validations when rendering the widget
328
	 *
329
	 * Always call this from your `render_frontend()` override!
330
	 *
331
	 * @return boolean True: render frontend; False: don't render frontend
332
	 */
333 4
	public function pre_render_frontend() {
334
		/**
335
		 * Assume shown regardless of hide_until_search setting.
336
		 */
337
		$whitelist = array(
338 4
			'custom_content',
339
		);
340
341
		/**
342
		 * @filter `gravityview/widget/hide_until_searched/whitelist` Some widgets have got to stay shown.
343
		 * @param[in,out] string[] $whitelist The widget IDs that have to be shown by default.
344
		 */
345 4
		$whitelist = apply_filters( 'gravityview/widget/hide_until_searched/whitelist', $whitelist );
346
347 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...
348 4
			$hide_until_searched = $view->settings->get( 'hide_until_searched' );
349
		} else {
350 2
			$hide_until_searched = false;
351
		}
352
353
		/**
354
		 * @filter `gravityview/widget/hide_until_searched` Modify whether to hide content until search
355
		 * @param boolean $hide_until_searched Hide until search?
356
		 * @param \GV\Widget $this Widget instance
357
		 */
358 4
		$hide_until_search = apply_filters( 'gravityview/widget/hide_until_searched', $hide_until_searched, $this );
359
360 4
		if ( $hide_until_search ) {
361 1
			gravityview()->log->debug( 'Hide View data until search is performed' );
362 1
			return false;
363
		}
364
365 4
		return true;
366
	}
367
368
	/**
369
	 * Shortcode.
370
	 *
371
	 * @param array $atts The Widget shortcode args.
372
	 * @param string $content The content.
373
	 * @param string $context The context, if available.
374
	 *
375
	 * @return string Whatever the widget echoed.
376
	 */
377 1
	public function render_shortcode( $atts, $content = '', $context = '' ) {
378 1
		ob_start();
379 1
		$this->render_frontend( $atts, $content, $context );
380 1
		return ob_get_clean();
381
	}
382
383
	/**
384
	 * Create the needed widget from a configuration array.
385
	 *
386
	 * @param array $configuration The configuration array.
387
	 * @see \GV\Widget::as_configuration()
388
	 * @internal
389
	 * @since 2.0
390
	 *
391
	 * @return \GV\Widget|null The widget implementation from configuration or none.
392
	 */
393 9
	public static function from_configuration( $configuration ) {
394 9
		$registered_widgets = self::registered();
395
396 9
		if ( ! $id = Utils::get( $configuration, 'id' ) ) {
397
			return null;
398
		}
399
400 9
		if ( ! $widget = Utils::get( $registered_widgets, $id ) ) {
401
			return null;
402
		}
403
404 9
		if ( ! class_exists( $class = Utils::get( $widget, 'class' ) ) ) {
405
			return null;
406
		}
407
408 9
		$w = new $class( Utils::get( $widget, 'label' ), $id );
409 9
		$w->configuration = new Settings( $configuration );
410
411 9
		return $w;
412
	}
413
414
	/**
415
	 * Return an array of the old format.
416
	 *
417
	 *  		'id' => string
418
	 *			+ whatever else specific fields may have
419
	 *
420
	 * @internal
421
	 * @since 2.0
422
	 *
423
	 * @return array
424
	 */
425 6
	public function as_configuration() {
426 6
		return array_merge( array(
427 6
			'id' => $this->get_widget_id(),
428 6
		), $this->configuration->all() );
429
	}
430
431
	/**
432
	 * Return all registered widgets.
433
	 *
434
	 * @api
435
	 * @since 2.0
436
	 *
437
	 * @return array
438
	 */
439 10
	public static function registered() {
440
		/**
441
		 * @filter `gravityview_register_directory_widgets` Get the list of registered widgets. Each item is used to instantiate a GravityView_Admin_View_Widget object
442
		 * @deprecated Use `gravityview/widgets/register`
443
		 * @param array $registered_widgets Empty array
444
		 */
445 10
		$registered_widgets = apply_filters( 'gravityview_register_directory_widgets', array() );
446
447
		/**
448
		 * @filter `gravityview/widgets/register` Each item is used to instantiate a GravityView_Admin_View_Widget object
449
		 * @param array $registered_widgets Empty array
450
		 */
451 10
		return apply_filters( 'gravityview/widgets/register', $registered_widgets );
452
	}
453
454
	/**
455
	 * Whether this Widget's been registered already or not.
456
	 *
457
	 * @api
458
	 * @since 2.0
459
	 *
460
	 * @return bool
461
	 */
462 10
	public function is_registered() {
463 10
		if ( ! $widget_id = $this->get_widget_id() ) {
464
			gravityview()->log->warning( 'Widget ID not set before calling Widget::is_registered', array( 'data' => $this ) );
465
			return false;
466
		}
467 10
		return in_array( $widget_id, array_keys( self::registered() ) );
468
	}
469
}
470