Completed
Push — master ( 0f8df5...d685dc )
by Matt
11:31
created

admin_controller::list_ads()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 7

Importance

Changes 10
Bugs 0 Features 1
Metric Value
c 10
b 0
f 1
dl 0
loc 27
ccs 22
cts 22
cp 1
rs 6.7272
cc 7
eloc 17
nc 5
nop 0
crap 7
1
<?php
2
/**
3
 *
4
 * Advertisement management. An extension for the phpBB Forum Software package.
5
 *
6
 * @copyright (c) 2017 phpBB Limited <https://www.phpbb.com>
7
 * @license GNU General Public License, version 2 (GPL-2.0)
8
 *
9
 */
10
11
namespace phpbb\admanagement\controller;
12
13
/**
14
* Admin controller
15
*/
16
class admin_controller
17
{
18
	const MAX_NAME_LENGTH = 255;
19
	const DATE_FORMAT = 'Y-m-d';
20
21
	/** @var \phpbb\template\template */
22
	protected $template;
23
24
	/** @var \phpbb\user */
25
	protected $user;
26
27
	/** @var \phpbb\request\request */
28
	protected $request;
29
30
	/** @var \phpbb\admanagement\ad\manager */
31
	protected $manager;
32
33
	/** @var \phpbb\admanagement\location\manager */
34
	protected $location_manager;
35
36
	/** @var \phpbb\log\log */
37
	protected $log;
38
39
	/** @var string php_ext */
40
	protected $php_ext;
41
42
	/** @var string ext_path */
43
	protected $ext_path;
44
45
	/** @var string Custom form action */
46
	protected $u_action;
47
48
	/** @var array Form validation errors */
49
	protected $errors = array();
50
51
	/**
52
	* Constructor
53
	*
54
	* @param \phpbb\template\template				$template			Template object
55
	* @param \phpbb\user							$user				User object
56
	* @param \phpbb\request\request					$request			Request object
57
	* @param \phpbb\admanagement\ad\manager			$manager			Advertisement manager object
58
	* @param \phpbb\admanagement\location\manager	$location_manager	Template location manager object
59
	* @param \phpbb\log\log							$log				The phpBB log system
60
	* @param string									$php_ext			PHP extension
61
	* @param string									$ext_path			Path to this extension
62
	*/
63 31
	public function __construct(\phpbb\template\template $template, \phpbb\user $user, \phpbb\request\request $request, \phpbb\admanagement\ad\manager $manager, \phpbb\admanagement\location\manager $location_manager, \phpbb\log\log $log, $php_ext, $ext_path)
64
	{
65 31
		$this->template = $template;
66 31
		$this->user = $user;
67 31
		$this->request = $request;
68 31
		$this->manager = $manager;
69 31
		$this->location_manager = $location_manager;
70 31
		$this->log = $log;
71 31
		$this->php_ext = $php_ext;
72 31
		$this->ext_path = $ext_path;
73 31
	}
74
75
	/**
76
	* Process user request
77
	*
78
	* @return void
79
	*/
80 6
	public function main()
81
	{
82 6
		$this->user->add_lang_ext('phpbb/admanagement', 'acp');
83
84 6
		$this->template->assign_var('S_PHPBB_ADMANAGEMENT', true);
85
86
		// Trigger specific action
87 6
		$action = $this->request->variable('action', '');
88 6
		if (in_array($action, array('add', 'edit', 'enable', 'disable', 'delete')))
89 6
		{
90 5
			$this->{'action_' . $action}();
91 5
		}
92
93
		// Otherwise default to this
94 6
		$this->list_ads();
95 6
	}
96
97
	/**
98
	* Set page url
99
	*
100
	* @param string $u_action Custom form action
101
	* @return void
102
	*/
103 25
	public function set_page_url($u_action)
104
	{
105 25
		$this->u_action = $u_action;
106 25
	}
107
108
	/**
109
	* Get ACP page title for Ads module
110
	*
111
	* @return string	Language string for Ads ACP module
112
	*/
113 1
	public function get_page_title()
114
	{
115 1
		return $this->user->lang('ACP_ADMANAGEMENT_TITLE');
116
	}
117
118
	/**
119
	* Add an advertisement
120
	*
121
	* @return void
122
	*/
123 7
	public function action_add()
124
	{
125 7
		$preview = $this->request->is_set_post('preview');
126 7
		$submit = $this->request->is_set_post('submit');
127
128 7
		add_form_key('phpbb/admanagement/add');
129 7
		if ($preview || $submit)
130 7
		{
131 6
			$data = $this->get_form_data('phpbb/admanagement/add');
132
133 View Code Duplication
			if ($preview)
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
134 6
			{
135 1
				$this->ad_preview($data['ad_code']);
136 1
			}
137
			else if (empty($this->errors))
138 5
			{
139 1
				$ad_id = $this->manager->insert_ad($data);
140 1
				$this->manager->insert_ad_locations($ad_id, $data['ad_locations']);
141
142 1
				$this->log('ADD', $data['ad_name']);
143
144 1
				$this->success('ACP_AD_ADD_SUCCESS');
145
			}
146
147 5
			$this->assign_locations($data);
148 5
			$this->assign_form_data($data);
149 5
		}
150
		else
151
		{
152 1
			$this->assign_locations();
153
		}
154
155
		// Set output vars for display in the template
156 6
		$this->template->assign_vars(array(
157 6
			'S_ADD_AD'				=> true,
158 6
			'U_BACK'				=> $this->u_action,
159 6
			'PICKER_DATE_FORMAT'	=> self::DATE_FORMAT,
160 6
		));
161 6
	}
162
163
	/**
164
	* Edit an advertisement
165
	*
166
	* @return void
167
	*/
168 10
	public function action_edit()
169
	{
170 9
		$ad_id = $this->request->variable('id', 0);
171 9
		$preview = $this->request->is_set_post('preview');
172 9
		$submit = $this->request->is_set_post('submit');
173
174 9
		add_form_key('phpbb/admanagement/edit/' . $ad_id);
175 9
		if ($preview || $submit)
176 9
		{
177 7
			$data = $this->get_form_data('phpbb/admanagement/edit/' . $ad_id);
178
179
			if ($preview)
180 7
			{
181 1
				$this->ad_preview($data['ad_code']);
182 1
			}
183 View Code Duplication
			else if (empty($this->errors))
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
184 6
			{
185 2
				$success = $this->manager->update_ad($ad_id, $data);
186
187
				if ($success)
188 2
				{
189
					// Only insert new ad locations to DB when ad exists
190 1
					$this->manager->delete_ad_locations($ad_id);
191 1
					$this->manager->insert_ad_locations($ad_id, $data['ad_locations']);
192
193 1
					$this->log('EDIT', $data['ad_name']);
194
195 1
					$this->success('ACP_AD_EDIT_SUCCESS');
196 10
				}
197 1
				$this->error('ACP_AD_DOES_NOT_EXIST');
198
			}
199 5
		}
200
		else
201
		{
202
			// Load ad data
203 2
			$data = $this->manager->get_ad($ad_id);
204 2
			if (empty($data))
205 2
			{
206 1
				$this->error('ACP_AD_DOES_NOT_EXIST');
207
			}
208
209
			// Load ad template locations
210 1
			$data['ad_locations'] = $this->manager->get_ad_locations($ad_id);
211
		}
212
213
		// Set output vars for display in the template
214 6
		$this->template->assign_vars(array(
215 6
			'S_EDIT_AD'				=> true,
216 6
			'EDIT_ID'				=> $ad_id,
217 6
			'U_BACK'				=> $this->u_action,
218 6
			'PICKER_DATE_FORMAT'	=> self::DATE_FORMAT,
219 6
		));
220 6
		$this->assign_locations($data);
221 6
		$this->assign_form_data($data);
222 6
	}
223
224
	/**
225
	* Enable an advertisement
226
	*
227
	* @return void
228
	*/
229 3
	public function action_enable()
230
	{
231 3
		$this->ad_enable(true);
232 1
	}
233
234
	/**
235
	* Disable an advertisement
236
	*
237
	* @return void
238
	*/
239 3
	public function action_disable()
240
	{
241 3
		$this->ad_enable(false);
242 1
	}
243
244
	/**
245
	* Delete an advertisement
246
	*
247
	* @return void
248
	*/
249 3
	public function action_delete()
250
	{
251 3
		$ad_id = $this->request->variable('id', 0);
252
		if ($ad_id)
253 3
		{
254 3
			if (confirm_box(true))
255 3
			{
256
				// Get ad data so that we can log ad name
257 2
				$ad_data = $this->manager->get_ad($ad_id);
258
259
				// Delete ad and it's template locations
260 2
				$this->manager->delete_ad_locations($ad_id);
261 2
				$success = $this->manager->delete_ad($ad_id);
262
263
				// Only notify user on error or if not ajax
264 2
				if (!$success)
265 2
				{
266 1
					$this->error('ACP_AD_DELETE_ERRORED');
267
				}
268
				else
269
				{
270 1
					$this->log('DELETE', $ad_data['ad_name']);
271
272 1
					if (!$this->request->is_ajax())
273 1
					{
274 1
						$this->success('ACP_AD_DELETE_SUCCESS');
275
					}
276
				}
277
			}
278
			else
279
			{
280 1
				confirm_box(false, $this->user->lang('CONFIRM_OPERATION'), build_hidden_fields(array(
281 1
					'id'		=> $ad_id,
282 1
					'i'			=> $this->request->variable('i', ''),
283 1
					'mode'		=> $this->request->variable('mode', ''),
284
					'action'	=> 'delete'
285 1
				)));
286
			}
287 1
		}
288 1
	}
289
290
	/**
291
	* Display the ads
292
	*
293
	* @return void
294
	*/
295 1
	public function list_ads()
296
	{
297 1
		foreach ($this->manager->get_all_ads() as $row)
298
		{
299 1
			$ad_enabled = (int) $row['ad_enabled'];
300 1
			$ad_end_date = (int) $row['ad_end_date'];
301 1
			$ad_expired = $ad_end_date > 0 && $ad_end_date < time();
302 1
			if ($ad_expired && $ad_enabled)
303 1
			{
304 1
				$ad_enabled = 0;
305 1
				$this->manager->update_ad($row['ad_id'], array('ad_enabled' => 0));
306 1
			}
307
308 1
			$this->template->assign_block_vars('ads', array(
309 1
				'NAME'					=> $row['ad_name'],
310 1
				'END_DATE'				=> $ad_end_date ? $this->user->format_date($ad_end_date, self::DATE_FORMAT) : '',
311 1
				'S_END_DATE_EXPIRED'	=> $ad_expired,
312 1
				'S_ENABLED'				=> $ad_enabled,
313 1
				'U_ENABLE'				=> $this->u_action . '&amp;action=' . ($ad_enabled ? 'disable' : 'enable') . '&amp;id=' . $row['ad_id'],
314 1
				'U_EDIT'				=> $this->u_action . '&amp;action=edit&amp;id=' . $row['ad_id'],
315 1
				'U_DELETE'				=> $this->u_action . '&amp;action=delete&amp;id=' . $row['ad_id'],
316 1
			));
317 1
		}
318
319
		// Set output vars for display in the template
320 1
		$this->template->assign_var('U_ACTION_ADD', $this->u_action . '&amp;action=add');
321 1
	}
322
323
	/**
324
	* Enable/disable an advertisement
325
	*
326
	* @param	bool	$enable	Enable or disable the advertisement?
327
	* @return void
328
	*/
329 4
	protected function ad_enable($enable)
330
	{
331 4
		$ad_id = $this->request->variable('id', 0);
332
333 4
		$success = $this->manager->update_ad($ad_id, array(
334 4
			'ad_enabled'	=> (int) $enable,
335 4
		));
336
337
		// If AJAX was used, show user a result message
338 4
		if ($this->request->is_ajax())
339 4
		{
340
			$json_response = new \phpbb\json_response;
341
			$json_response->send(array(
342
				'text'	=> $this->user->lang($enable ? 'ENABLED' : 'DISABLED'),
343
				'title'	=> $this->user->lang('AD_ENABLE_TITLE', (int) $enable),
344
			));
345
		}
346
347
		// Otherwise, show traditional infobox
348
		if ($success)
349 4
		{
350 2
			$this->success($enable ? 'ACP_AD_ENABLE_SUCCESS' : 'ACP_AD_DISABLE_SUCCESS');
351
		}
352
		else
353
		{
354 2
			$this->error($enable ? 'ACP_AD_ENABLE_ERRORED' : 'ACP_AD_DISABLE_ERRORED');
355
		}
356
	}
357
358
	/**
359
	* Get admin form data.
360
	*
361
	* @param	string	$form_name	The form name.
362
	* @return	array	Form data
363
	*/
364 13
	protected function get_form_data($form_name)
365
	{
366
		$data = array(
367 13
			'ad_name'		=> $this->request->variable('ad_name', '', true),
368 13
			'ad_note'		=> $this->request->variable('ad_note', '', true),
369 13
			'ad_code'		=> $this->request->variable('ad_code', '', true),
370 13
			'ad_enabled'	=> $this->request->variable('ad_enabled', 0),
371 13
			'ad_locations'	=> $this->request->variable('ad_locations', array('')),
372 13
			'ad_end_date'	=> $this->request->variable('ad_end_date', ''),
373 13
		);
374
375
		// Validate form key
376 13
		if (!check_form_key($form_name))
377 13
		{
378 2
			$this->errors[] = $this->user->lang('FORM_INVALID');
379 2
		}
380
381
		// Validate ad name
382 13
		if ($data['ad_name'] === '')
383 13
		{
384 2
			$this->errors[] = $this->user->lang('AD_NAME_REQUIRED');
385 2
		}
386 13
		if (truncate_string($data['ad_name'], self::MAX_NAME_LENGTH) !== $data['ad_name'])
387 13
		{
388 2
			$this->errors[] = $this->user->lang('AD_NAME_TOO_LONG', self::MAX_NAME_LENGTH);
389 2
		}
390
391
		// Validate ad end date
392 13
		if (preg_match('#^\d{4}\-\d{2}\-\d{2}$#', $data['ad_end_date']))
393 13
		{
394 4
			$data['ad_end_date'] = (int) $this->user->get_timestamp_from_format(self::DATE_FORMAT, $data['ad_end_date']);
395
396 4
			if ($data['ad_end_date'] < time())
397 4
			{
398 2
				$this->errors[] = $this->user->lang('AD_END_DATE_INVALID');
399 2
			}
400 4
		}
401 9
		else if ($data['ad_end_date'] !== '')
402 9
		{
403
			$this->errors[] = $this->user->lang('AD_END_DATE_INVALID');
404
		}
405
		else
406
		{
407 9
			$data['ad_end_date'] = 0;
408
		}
409
410 13
		return $data;
411
	}
412
413
	/**
414
	* Assign form data to the template.
415
	*
416
	* @param	array	$data	The form data.
417
	* @return void
418
	*/
419 11
	protected function assign_form_data($data)
420
	{
421 11
		$this->template->assign_vars(array(
422 11
			'S_ERROR'		=> (bool) count($this->errors),
423 11
			'ERROR_MSG'		=> count($this->errors) ? implode('<br />', $this->errors) : '',
424
425 11
			'AD_NAME'		=> $data['ad_name'],
426 11
			'AD_NOTE'		=> $data['ad_note'],
427 11
			'AD_CODE'		=> $data['ad_code'],
428 11
			'AD_ENABLED'	=> $data['ad_enabled'],
429 11
			'AD_END_DATE'	=> $this->prepare_end_date($data['ad_end_date']),
430 11
		));
431 11
	}
432
	/**
433
	* Prepare end date for display
434
	*
435
	* @param	mixed	$end_date	End date.
436
	* @return	string	End date prepared for display.
437
	*/
438 11
	protected function prepare_end_date($end_date)
439
	{
440 11
		if (empty($end_date))
441 11
		{
442 8
			return '';
443
		}
444 3
		else if (is_numeric($end_date))
445 3
		{
446 3
			return $this->user->format_date($end_date, self::DATE_FORMAT);
447
		}
448
449
		return $end_date;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $end_date; (object|string|null|array|boolean) is incompatible with the return type documented by phpbb\admanagement\contr...oller::prepare_end_date of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
450
	}
451
452
	/**
453
	* Assign template locations data to the template.
454
	*
455
	* @param	mixed	$data	The form data or nothing.
456
	* @return	void
457
	*/
458 12
	protected function assign_locations($data = false)
459
	{
460 12
		foreach ($this->location_manager->get_all_locations() as $location_id => $location_data)
461
		{
462 12
			$this->template->assign_block_vars('ad_locations', array(
463 12
				'LOCATION_ID'	=> $location_id,
464 12
				'LOCATION_DESC'	=> $location_data['desc'],
465 12
				'LOCATION_NAME'	=> $location_data['name'],
466 12
				'S_SELECTED'	=> $data ? in_array($location_id, $data['ad_locations']) : false,
467 12
			));
468 12
		}
469 12
	}
470
471
	/**
472
	* Prepare advertisement preview
473
	*
474
	* @param	string	$code	Ad code to preview
475
	* @return	void
476
	*/
477 2
	protected function ad_preview($code)
478
	{
479 2
		$this->template->assign_var('PREVIEW', htmlspecialchars_decode($code));
480 2
	}
481
482
	/**
483
	* Print success message.
484
	*
485
	* It takes arguments in the form of a language key, followed by language substitution values.
486
	*/
487 5
	protected function success()
488
	{
489 5
		trigger_error(call_user_func_array(array($this->user, 'lang'), func_get_args()) . adm_back_link($this->u_action));
490
	}
491
492
	/**
493
	* Print error message.
494
	*
495
	* It takes arguments in the form of a language key, followed by language substitution values.
496
	*/
497 5
	protected function error()
498
	{
499 5
		trigger_error(call_user_func_array(array($this->user, 'lang'), func_get_args()) . adm_back_link($this->u_action), E_USER_WARNING);
500
	}
501
502
	/**
503
	* Log action
504
	*
505
	* @param	string	$action		Performed action in uppercase
506
	* @param	string	$ad_name	Advertisement name
507
	* @return	void
508
	*/
509 3
	protected function log($action, $ad_name)
510
	{
511 3
		$this->log->add('admin', $this->user->data['user_id'], $this->user->ip, 'ACP_ADMANAGEMENT_' . $action . '_LOG', time(), array($ad_name));
512 3
	}
513
}
514