1<?php
2/**
3*
4* This file is part of the phpBB Forum Software package.
5*
6* @copyright (c) phpBB Limited <https://www.phpbb.com>
7* @license GNU General Public License, version 2 (GPL-2.0)
8*
9* For full copyright and license information, please see
10* the docs/CREDITS.txt file.
11*
12*/
13
14namespace phpbb\avatar;
15
16class manager
17{
18	/**
19	* phpBB configuration
20	* @var \phpbb\config\config
21	*/
22	protected $config;
23
24	/**
25	* phpBB event dispatcher
26	* @var \phpbb\event\dispatcher_interface
27	*/
28	protected $phpbb_dispatcher;
29
30	/**
31	* Array that contains a list of enabled drivers
32	* @var array
33	*/
34	static protected $enabled_drivers = false;
35
36	/**
37	* Array that contains all available avatar drivers which are passed via the
38	* service container
39	* @var array
40	*/
41	protected $avatar_drivers;
42
43	/**
44	* Default avatar data row
45	* @var array
46	*/
47	static protected $default_row = array(
48		'avatar'		=> '',
49		'avatar_type'	=> '',
50		'avatar_width'	=> 0,
51		'avatar_height'	=> 0,
52	);
53
54	/**
55	* Construct an avatar manager object
56	*
57	* @param \phpbb\config\config $config phpBB configuration
58	* @param \phpbb\event\dispatcher_interface $phpbb_dispatcher phpBB event dispatcher
59	* @param array $avatar_drivers Avatar drivers passed via the service container
60	*/
61	public function __construct(\phpbb\config\config $config, \phpbb\event\dispatcher_interface $phpbb_dispatcher, $avatar_drivers)
62	{
63		$this->config = $config;
64		$this->phpbb_dispatcher = $phpbb_dispatcher;
65		$this->register_avatar_drivers($avatar_drivers);
66	}
67
68	/**
69	* Register avatar drivers
70	*
71	* @param array $avatar_drivers Service collection of avatar drivers
72	*/
73	protected function register_avatar_drivers($avatar_drivers)
74	{
75		if (!empty($avatar_drivers))
76		{
77			foreach ($avatar_drivers as $driver)
78			{
79				$this->avatar_drivers[$driver->get_name()] = $driver;
80			}
81		}
82	}
83
84	/**
85	* Get the driver object specified by the avatar type
86	*
87	* @param string $avatar_type Avatar type; by default an avatar's service container name
88	* @param bool $load_enabled Load only enabled avatars
89	*
90	* @return object Avatar driver object
91	*/
92	public function get_driver($avatar_type, $load_enabled = true)
93	{
94		if (self::$enabled_drivers === false)
95		{
96			$this->load_enabled_drivers();
97		}
98
99		$avatar_drivers = ($load_enabled) ? self::$enabled_drivers : $this->get_all_drivers();
100
101		// Legacy stuff...
102		switch ($avatar_type)
103		{
104			case AVATAR_GALLERY:
105				$avatar_type = 'avatar.driver.local';
106			break;
107			case AVATAR_UPLOAD:
108				$avatar_type = 'avatar.driver.upload';
109			break;
110			case AVATAR_REMOTE:
111				$avatar_type = 'avatar.driver.remote';
112			break;
113		}
114
115		if (!isset($avatar_drivers[$avatar_type]))
116		{
117			return null;
118		}
119
120		/*
121		* There is no need to handle invalid avatar types as the following code
122		* will cause a ServiceNotFoundException if the type does not exist
123		*/
124		$driver = $this->avatar_drivers[$avatar_type];
125
126		return $driver;
127	}
128
129	/**
130	* Load the list of enabled drivers
131	* This is executed once and fills self::$enabled_drivers
132	*/
133	protected function load_enabled_drivers()
134	{
135		if (!empty($this->avatar_drivers))
136		{
137			self::$enabled_drivers = array();
138			foreach ($this->avatar_drivers as $driver)
139			{
140				if ($this->is_enabled($driver))
141				{
142					self::$enabled_drivers[$driver->get_name()] = $driver->get_name();
143				}
144			}
145			asort(self::$enabled_drivers);
146		}
147	}
148
149	/**
150	* Get a list of all avatar drivers
151	*
152	* As this function will only be called in the ACP avatar settings page, it
153	* doesn't make much sense to cache the list of all avatar drivers like the
154	* list of the enabled drivers.
155	*
156	* @return array Array containing a list of all avatar drivers
157	*/
158	public function get_all_drivers()
159	{
160		$drivers = array();
161
162		if (!empty($this->avatar_drivers))
163		{
164			foreach ($this->avatar_drivers as $driver)
165			{
166				$drivers[$driver->get_name()] = $driver->get_name();
167			}
168			asort($drivers);
169		}
170
171		return $drivers;
172	}
173
174	/**
175	* Get a list of enabled avatar drivers
176	*
177	* @return array Array containing a list of the enabled avatar drivers
178	*/
179	public function get_enabled_drivers()
180	{
181		if (self::$enabled_drivers === false)
182		{
183			$this->load_enabled_drivers();
184		}
185
186		return self::$enabled_drivers;
187	}
188
189	/**
190	* Strip out user_, group_, or other prefixes from array keys
191	*
192	* @param array	$row			User data or group data
193	* @param string $prefix			Prefix of data keys (e.g. user), should not include the trailing underscore
194	*
195	* @return array	User or group data with keys that have been
196	*			stripped from the preceding "user_" or "group_"
197	*			Also the group id is prefixed with g, when the prefix group is removed.
198	*/
199	static public function clean_row($row, $prefix = '')
200	{
201		// Upon creation of a user/group $row might be empty
202		if (empty($row))
203		{
204			return self::$default_row;
205		}
206
207		$output = array();
208		foreach ($row as $key => $value)
209		{
210			$key = preg_replace("#^(?:{$prefix}_)#", '', $key);
211			$output[$key] = $value;
212		}
213
214		if ($prefix === 'group' && isset($output['id']))
215		{
216			$output['id'] = 'g' . $output['id'];
217		}
218
219		return $output;
220	}
221
222	/**
223	* Clean driver names that are returned from template files
224	* Underscores are replaced with dots
225	*
226	* @param string $name Driver name
227	*
228	* @return string Cleaned driver name
229	*/
230	static public function clean_driver_name($name)
231	{
232		return str_replace(array('\\', '_'), '.', $name);
233	}
234
235	/**
236	* Prepare driver names for use in template files
237	* Dots are replaced with underscores
238	*
239	* @param string $name Clean driver name
240	*
241	* @return string Prepared driver name
242	*/
243	static public function prepare_driver_name($name)
244	{
245		return str_replace('.', '_', $name);
246	}
247
248	/**
249	* Check if avatar is enabled
250	*
251	* @param object $driver Avatar driver object
252	*
253	* @return bool True if avatar is enabled, false if it's disabled
254	*/
255	public function is_enabled($driver)
256	{
257		$config_name = $driver->get_config_name();
258
259		return $this->config["allow_avatar_{$config_name}"];
260	}
261
262	/**
263	* Get the settings array for enabling/disabling an avatar driver
264	*
265	* @param object $driver Avatar driver object
266	*
267	* @return array Array of configuration options as consumed by acp_board
268	*/
269	public function get_avatar_settings($driver)
270	{
271		$config_name = $driver->get_config_name();
272
273		return array(
274			'allow_avatar_' . $config_name	=> array('lang' => 'ALLOW_' . strtoupper(str_replace('\\', '_', $config_name)),		'validate' => 'bool',	'type' => 'radio:yes_no', 'explain' => true),
275		);
276	}
277
278	/**
279	* Replace "error" strings with their real, localized form
280	*
281	* @param \phpbb\user phpBB User object
282	* @param array	$error Array containing error strings
283	*        Key values can either be a string with a language key or an array
284	*        that will be passed to vsprintf() with the language key in the
285	*        first array key.
286	*
287	* @return array Array containing the localized error strings
288	*/
289	public function localize_errors(\phpbb\user $user, $error)
290	{
291		foreach ($error as $key => $lang)
292		{
293			if (is_array($lang))
294			{
295				$lang_key = array_shift($lang);
296				$error[$key] = vsprintf($user->lang($lang_key), $lang);
297			}
298			else
299			{
300				$error[$key] = $user->lang("$lang");
301			}
302		}
303
304		return $error;
305	}
306
307	/**
308	* Handle deleting avatars
309	*
310	* @param \phpbb\db\driver\driver_interface $db phpBB dbal
311	* @param \phpbb\user    $user phpBB user object
312	* @param array          $avatar_data Cleaned user data containing the user's
313	*                               avatar data
314	* @param string         $table Database table from which the avatar should be deleted
315	* @param string         $prefix Prefix of user data columns in database
316	* @return null
317	*/
318	public function handle_avatar_delete(\phpbb\db\driver\driver_interface $db, \phpbb\user $user, $avatar_data, $table, $prefix)
319	{
320		if ($driver = $this->get_driver($avatar_data['avatar_type']))
321		{
322			$driver->delete($avatar_data);
323		}
324
325		$result = $this->prefix_avatar_columns($prefix, self::$default_row);
326
327		$sql = 'UPDATE ' . $table . '
328			SET ' . $db->sql_build_array('UPDATE', $result) . '
329			WHERE ' . $prefix . 'id = ' . (int) $avatar_data['id'];
330		$db->sql_query($sql);
331
332		// Make sure we also delete this avatar from the users
333		if ($prefix === 'group_')
334		{
335			$result = $this->prefix_avatar_columns('user_', self::$default_row);
336
337			$sql = 'UPDATE ' . USERS_TABLE . '
338				SET ' . $db->sql_build_array('UPDATE', $result) . "
339				WHERE user_avatar = '" . $db->sql_escape($avatar_data['avatar']) . "'";
340			$db->sql_query($sql);
341		}
342
343		/**
344		* Event is triggered after user avatar has been deleted
345		*
346		* @event core.avatar_manager_avatar_delete_after
347		* @var	\phpbb\user	user		phpBB user object
348		* @var	array		avatar_data	Normalised avatar-related user data
349		* @var	string		table		Table to delete avatar from
350		* @var	string		prefix		Column prefix to delete avatar from
351		* @since 3.2.4-RC1
352		*/
353		$vars = array('user', 'avatar_data', 'table', 'prefix');
354		extract($this->phpbb_dispatcher->trigger_event('core.avatar_manager_avatar_delete_after', compact($vars)));
355	}
356
357	/**
358	 * Prefix avatar columns
359	 *
360	 * @param string $prefix Column prefix
361	 * @param array $data Column data
362	 *
363	 * @return array Column data with prefixed column names
364	 */
365	public function prefix_avatar_columns($prefix, $data)
366	{
367		foreach ($data as $key => $value)
368		{
369			$data[$prefix . $key] = $value;
370			unset($data[$key]);
371		}
372
373		return $data;
374	}
375}
376