1<?php
2// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
3//
4// All Rights Reserved. See copyright.txt for details and a complete list of authors.
5// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
6// $Id$
7
8//this script may only be included - so its better to die if called directly.
9if (strpos($_SERVER["SCRIPT_NAME"], basename(__FILE__)) !== false) {
10	header("location: index.php");
11	exit;
12}
13
14/*
15ThemeLib
16@uses TikiLib
17*/
18
19class ThemeLib extends TikiLib
20{
21
22	/*
23	@return array of folder names in themes directory
24	*/
25	function get_themes($theme_base_path = 'themes')
26	{
27		$themes = [];
28		$list_css = glob("{$theme_base_path}/*/css/*.css");
29		if ($list_css == false) {
30			return [];
31		}
32		foreach ($list_css as $css) {
33			$css = dirname(dirname($css));
34			$theme = basename($css);
35			$themes[$theme] = tr($theme);
36		}
37		unset($themes['base_files']); //make sure base_files directory is removed from the array
38		unset($themes['templates']); //make sure templates directory is removed from the array
39		return $themes;
40	}
41
42	/* replaces legacy list_styles() function
43	@return array of all themes offered by Tiki
44	*/
45	function list_themes()
46	{
47		//set special array values and get themes from the main themes directory
48		$themes = [
49			'default' => tr('Default Bootstrap'),
50			'custom_url' => tr('Custom theme by specifying URL'),
51		];
52		$themes = $themes + $this->get_themes(); //this way default and custom remains on the top of the array and default keeps its description
53
54		//get multidomain themes
55		$theme_base_path = $this->get_theme_path();    // knows about $tikidomain
56		if ($theme_base_path) {
57			$themes = array_unique(array_merge($themes, $this->get_themes($theme_base_path)));
58		}
59
60		return $themes;
61	}
62
63	/*
64	@return array of all theme options
65	*/
66	function get_options()
67	{
68		$options = [];
69		foreach (glob("themes/*/options/*/css/*.css") as $css) {
70			$css = dirname(dirname($css));
71			$option = basename($css);
72			$options[$option] = tr($option);
73		}
74		return $options;
75	}
76
77	/* replaces legacy list_style_options function
78	@param $theme - main theme (e.g. "fivealive")
79	@return array of options the theme's options directory (e.g. from "themes/fivealive/options/")
80	*/
81	function list_theme_options($theme)
82	{
83		$theme_options = [];
84		if (isset($theme) and $theme != 'custom_url') { //don't consider custom URL themes to have options
85			$option_base_path = $this->get_theme_path($theme);
86			$list_css = glob("{$option_base_path}/options/*/css/*.css");
87			if ($list_css == false) {
88				return [];
89			}
90			foreach ($list_css as $css) {
91				$css = dirname(dirname($css));
92				$option = basename($css);
93				$theme_options[$option] = tr($option);
94			}
95		}
96		return $theme_options;
97	}
98
99	/* the group theme setting is stored in one column, so we need an array where all themes and all options are all available
100	@return array of all themes and all options
101	*/
102	function list_themes_and_options()
103	{
104		$theme_options = [];
105		$themes = $this->list_themes();
106		unset($themes['custom_url']); //make sure Custom URL is removed from the list as it can not have options
107		foreach ($themes as $theme) {
108			$options = $this->list_theme_options($theme);
109			foreach ($options as $option) {
110				$theme_options[$theme . '/' . $option] = $theme . '/' . $option;
111			}
112		}
113		$themes_and_options = array_merge($themes, $theme_options); //merge the two array
114		natsort($themes_and_options); //sort the values
115		return $themes_and_options;
116	}
117
118	/* if theme and option is concatenated into one string (eg: group themes, theme control), than extract theme and option info from the string
119	@return theme and option name
120	*/
121	function extract_theme_and_option($themeoption)
122	{
123		$items = explode("/", $themeoption);
124		$theme = $items[0]; //theme is always there
125		if (isset($items[1])) { //check if we have option
126			$option = $items[1];
127		} else {
128			$option = '';
129		}
130		return [$theme, $option];
131	}
132
133	/* get thumbnail for theme if there is one. The thumbnail should be a png file.
134	@param $theme - theme name (e.g. fivealive)
135	@param $option - optional theme option file name
136	@return string path to thumbnail file to be used by an img element
137	*/
138	function get_thumbnail_file($theme, $option = '')
139	{
140		if (! empty($option) && $option != tr('None')) {
141			$filename = $option . '.png'; // add .png
142		} else {
143			$filename = $theme . '.png'; // add .png
144			$option = '';
145		}
146		return $this->get_theme_path($theme, $option, $filename);
147	}
148
149	/** replaces legacy get_style_path function
150	 * @param string $theme - main theme (e.g. "fivealive" - can be empty to return main themes dir)
151	 * @param string $option - optional theme option file name (e.g. "akebi")
152	 * @param string $filename - optional filename to look for (e.g. "purple.png")
153	 * @param string $subdir - optional dir to look in, e.g. 'css' etc (will guess by file extension if this not set but filename is)
154	 * @return string          - path to dir or file if found or empty if not - e.g. "themes/mydomain.tld/fivealive/options/akebi/"
155	 */
156
157	function get_theme_path($theme = '', $option = '', $filename = '', $subdir = '')
158	{
159		global $tikidomain;
160
161		$path = '';
162		$dir_base = '';
163		if ($tikidomain && is_dir("themes/$tikidomain")) {
164			$dir_base = $tikidomain . '/';
165		}
166
167		$theme_base = '';
168		if (! empty($theme)) {
169			$theme_base = $theme . '/';
170		}
171
172		if (! empty($option)) {
173			$option_base = 'options/' . $option . '/';
174		}
175
176		if (empty($subdir) && ! empty($filename)) {
177			$extension = substr($filename, strrpos($filename, '.') + 1);
178			switch ($extension) {
179				case 'css':
180					$subdir = 'css/';
181					break;
182				case 'php':
183					$subdir = 'icons/';
184					break;
185				case 'png':
186				case 'gif':
187				case 'jpg':
188				case 'jpeg':
189				case 'svg':
190					$subdir = 'images/';
191					break;
192				case 'less':
193					$subdir = 'less/';
194					break;
195				case 'js':
196					$subdir = 'js/';
197					break;
198				case 'tpl':
199					$subdir = 'templates/';
200					break;
201			}
202		}
203
204		// Why does this look in 'themes/' . $dir_base . $subdir and 'themes/' . $subdir if and only if we have a $filename? Chealer 2017-01-16
205		if (empty($filename)) {
206			if (isset($option_base) && is_dir('themes/' . $dir_base . $theme_base . $option_base . $subdir)) {
207				$path = 'themes/' . $dir_base . $theme_base . $option_base . $subdir;
208			} elseif (is_dir('themes/' . $dir_base . $theme_base . $subdir)) {
209				$path = 'themes/' . $dir_base . $theme_base . $subdir;                // try "parent" theme dir if no option one
210			} elseif (isset($option_base) && is_dir('themes/' . $theme_base . $option_base . $subdir)) {
211				$path = 'themes/' . $theme_base . $option_base . $subdir;                // try non-tikidomain theme dirs if no domain one
212			} elseif (is_dir('themes/' . $theme_base . $subdir)) {
213				$path = 'themes/' . $theme_base . $subdir;                            // try root theme dir if no domain one
214			} elseif (is_dir('themes/' . $theme_base)) {
215				$path = 'themes/' . $theme_base;                                    // fall back to "parent" theme dir with no subdir if not
216			}
217		} else {
218			if (isset($option_base) && is_file('themes/' . $dir_base . $theme_base . $option_base . $subdir . $filename)) {
219				$path = 'themes/' . $dir_base . $theme_base . $option_base . $subdir . $filename;
220			} elseif (is_file('themes/' . $dir_base . $theme_base . $subdir . $filename)) {    // try "parent" themes dir if no option one
221				$path = 'themes/' . $dir_base . $theme_base . $subdir . $filename;
222			} elseif (isset($option_base) && is_file('themes/' . $theme_base . $option_base . $subdir . $filename)) {    // try non-tikidomain dirs if not found
223				$path = 'themes/' . $theme_base . $option_base . $subdir . $filename;
224			} elseif (is_file('themes/' . $theme_base . $subdir . $filename)) {
225				$path = 'themes/' . $theme_base . $subdir . $filename;                        // fall back to "parent" themes dir if no option
226			} elseif (is_file('themes/' . $dir_base . $subdir . $filename)) {
227				$path = 'themes/' . $dir_base . $subdir . $filename;                            // tikidomain root themes dir?
228			} elseif (is_file('themes/' . $subdir . $filename)) {
229				$path = 'themes/' . $subdir . $filename;                                    // root themes subdir?
230			} elseif (is_file('themes/' . $filename)) {
231				$path = 'themes/' . $filename;                                            // root themes dir?
232			}
233		}
234		return $path;
235	}
236
237	function get_theme_css($theme = '', $option = '')
238	{
239		if ($option) {
240			return $this->get_theme_path($theme, $option, $option . '.css');
241		} else {
242			return $this->get_theme_path($theme, $option, $theme . '.css');
243		}
244	}
245
246	/* get list of base iconsets
247	@return $base_iconsets - an array containing all icon set names from themes/base_files/iconsets folder
248	*/
249	function list_base_iconsets()
250	{
251		$base_iconsets = [];
252		$iconsetlib = TikiLib::lib('iconset');
253
254		if (is_dir('themes/base_files/iconsets')) {
255			foreach (scandir('themes/base_files/iconsets') as $iconset_file) {
256				if ($iconset_file[0] != '.' && $iconset_file != 'index.php') {
257					$data = $iconsetlib->loadFile('themes/base_files/iconsets/' . $iconset_file);
258					$base_iconsets[substr($iconset_file, 0, -4)] = $data['name'];
259				}
260			}
261		}
262		return $base_iconsets;
263	}
264
265	/* get list of available themes and options
266	@return array of available themes and options based on $prefs['available_themes'] setting. This function does not consider if change_theme is on or off.
267	*/
268	function get_available_themesandoptions()
269	{
270		global $prefs;
271		$available_themesandoptions = [];
272		if (count($prefs['available_themes'] != 0) and ! empty($prefs['available_themes'][0])) { //if pref['available_themes'] is set, than use it
273			$available_themesandoptions = array_combine($prefs['available_themes'], $prefs['available_themes']); // TODO: does it make any sense to combine the same pref array with itself? -- luci
274		} else {
275			$available_themesandoptions = $this->list_themes_and_options(); //else load all themes and options
276			unset($available_themesandoptions['custom_url']); //make sure Custom URL is removed from the list
277		}
278		return $available_themesandoptions;
279	}
280	/* get a list of available themes
281	@return array of available themes based on $prefs['available_themes'] setting. This function does not consider if change_theme is on or off.
282	*/
283	function get_available_themes()
284	{
285		global $prefs;
286		$available_themes = [];
287		if (! empty($prefs['available_themes']) && ! empty($prefs['available_themes'][0])) { //if pref['available_themes'] is set, than use it
288			foreach ($prefs['available_themes'] as $available_theme) {
289				$theme = $this->extract_theme_and_option($available_theme)[0];
290				$available_themes[$theme] = $theme;
291				$available_themes['default'] = tr('Default Bootstrap');
292			}
293		} else {
294			$available_themes = $this->list_themes(); //else load all themes and options
295			unset($available_themes['custom_url']); //make sure Custom URL is removed from the list
296		}
297		return $available_themes;
298	}
299
300	/* get a list of available options for a theme
301	@return array of available theme options based on $prefs['available_themes'] setting. This function does not consider if change_theme is on or off.
302	*/
303	function get_available_options($theme)
304	{
305		global $prefs;
306		$available_options = [];
307		if (! empty($prefs['available_themes']) && ! empty($prefs['available_themes'][0])) {
308			foreach ($prefs['available_themes'] as $available_themeandoption) {
309				$themeandoption = $this->extract_theme_and_option($available_themeandoption);
310				if ($theme === $themeandoption[0] && ! empty($themeandoption[1])) {
311					$available_options[$themeandoption[1]] = $themeandoption[1];
312				}
313			}
314			return $available_options;
315		} else {
316			return $this->list_theme_options($theme);
317		}
318	}
319}
320