]> git.mxchange.org Git - friendica.git/blob - src/Core/Theme.php
7df985df35095b9e9eee79c9f5e935bbca0055e7
[friendica.git] / src / Core / Theme.php
1 <?php
2
3 /**
4  * @file src/Core/Theme.php
5  */
6
7 namespace Friendica\Core;
8
9 use Friendica\BaseObject;
10 use Friendica\DI;
11 use Friendica\Model\Profile;
12 use Friendica\Util\Strings;
13
14 require_once 'boot.php';
15
16 /**
17  * Some functions to handle themes
18  */
19 class Theme
20 {
21         public static function getAllowedList()
22         {
23                 $allowed_themes_str = Config::get('system', 'allowed_themes');
24                 $allowed_themes_raw = explode(',', str_replace(' ', '', $allowed_themes_str));
25                 $allowed_themes = [];
26                 if (count($allowed_themes_raw)) {
27                         foreach ($allowed_themes_raw as $theme) {
28                                 $theme = Strings::sanitizeFilePathItem(trim($theme));
29                                 if (strlen($theme) && is_dir("view/theme/$theme")) {
30                                         $allowed_themes[] = $theme;
31                                 }
32                         }
33                 }
34
35                 return array_unique($allowed_themes);
36         }
37
38         public static function setAllowedList(array $allowed_themes)
39         {
40                 Config::set('system', 'allowed_themes', implode(',', array_unique($allowed_themes)));
41         }
42
43         /**
44          * @brief Parse theme comment in search of theme infos.
45          *
46          * like
47          * \code
48          * ..* Name: My Theme
49          *   * Description: My Cool Theme
50          * . * Version: 1.2.3
51          *   * Author: John <profile url>
52          *   * Maintainer: Jane <profile url>
53          *   *
54          * \endcode
55          * @param string $theme the name of the theme
56          * @return array
57          */
58         public static function getInfo($theme)
59         {
60                 $theme = Strings::sanitizeFilePathItem($theme);
61
62                 $info = [
63                         'name' => $theme,
64                         'description' => "",
65                         'author' => [],
66                         'maintainer' => [],
67                         'version' => "",
68                         'credits' => "",
69                         'experimental' => file_exists("view/theme/$theme/experimental"),
70                         'unsupported' => file_exists("view/theme/$theme/unsupported")
71                 ];
72
73                 if (!is_file("view/theme/$theme/theme.php")) {
74                         return $info;
75                 }
76
77                 $a = \get_app();
78                 $stamp1 = microtime(true);
79                 $theme_file = file_get_contents("view/theme/$theme/theme.php");
80                 $a->getProfiler()->saveTimestamp($stamp1, "file", System::callstack());
81
82                 $result = preg_match("|/\*.*\*/|msU", $theme_file, $matches);
83
84                 if ($result) {
85                         $comment_lines = explode("\n", $matches[0]);
86                         foreach ($comment_lines as $comment_line) {
87                                 $comment_line = trim($comment_line, "\t\n\r */");
88                                 if ($comment_line != "") {
89                                         list($key, $value) = array_map("trim", explode(":", $comment_line, 2));
90                                         $key = strtolower($key);
91                                         if ($key == "author") {
92                                                 $result = preg_match("|([^<]+)<([^>]+)>|", $value, $matches);
93                                                 if ($result) {
94                                                         $info['author'][] = ['name' => $matches[1], 'link' => $matches[2]];
95                                                 } else {
96                                                         $info['author'][] = ['name' => $value];
97                                                 }
98                                         } elseif ($key == "maintainer") {
99                                                 $result = preg_match("|([^<]+)<([^>]+)>|", $value, $matches);
100                                                 if ($result) {
101                                                         $info['maintainer'][] = ['name' => $matches[1], 'link' => $matches[2]];
102                                                 } else {
103                                                         $info['maintainer'][] = ['name' => $value];
104                                                 }
105                                         } elseif (array_key_exists($key, $info)) {
106                                                 $info[$key] = $value;
107                                         }
108                                 }
109                         }
110                 }
111                 return $info;
112         }
113
114         /**
115          * @brief Returns the theme's screenshot.
116          *
117          * The screenshot is expected as view/theme/$theme/screenshot.[png|jpg].
118          *
119          * @param string $theme The name of the theme
120          * @return string
121          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
122          */
123         public static function getScreenshot($theme)
124         {
125                 $theme = Strings::sanitizeFilePathItem($theme);
126
127                 $exts = ['.png', '.jpg'];
128                 foreach ($exts as $ext) {
129                         if (file_exists('view/theme/' . $theme . '/screenshot' . $ext)) {
130                                 return System::baseUrl() . '/view/theme/' . $theme . '/screenshot' . $ext;
131                         }
132                 }
133                 return System::baseUrl() . '/images/blank.png';
134         }
135
136         public static function uninstall($theme)
137         {
138                 $theme = Strings::sanitizeFilePathItem($theme);
139
140                 // silently fail if theme was removed or if $theme is funky
141                 if (file_exists("view/theme/$theme/theme.php")) {
142                         include_once "view/theme/$theme/theme.php";
143
144                         $func = "{$theme}_uninstall";
145                         if (function_exists($func)) {
146                                 $func();
147                         }
148                 }
149
150                 $allowed_themes = Theme::getAllowedList();
151                 $key = array_search($theme, $allowed_themes);
152                 if ($key !== false) {
153                         unset($allowed_themes[$key]);
154                         Theme::setAllowedList($allowed_themes);
155                 }
156         }
157
158         public static function install($theme)
159         {
160                 $theme = Strings::sanitizeFilePathItem($theme);
161
162                 // silently fail if theme was removed or if $theme is funky
163                 if (!file_exists("view/theme/$theme/theme.php")) {
164                         return false;
165                 }
166
167                 try {
168                         include_once "view/theme/$theme/theme.php";
169
170                         $func = "{$theme}_install";
171                         if (function_exists($func)) {
172                                 $func();
173                         }
174
175                         $allowed_themes = Theme::getAllowedList();
176                         $allowed_themes[] = $theme;
177                         Theme::setAllowedList($allowed_themes);
178
179                         return true;
180                 } catch (\Exception $e) {
181                         Logger::error('Theme installation failed', ['theme' => $theme, 'error' => $e->getMessage()]);
182                         return false;
183                 }
184         }
185
186         /**
187          * @brief Get the full path to relevant theme files by filename
188          *
189          * This function searches in order in the current theme directory, in the current theme parent directory, and lastly
190          * in the base view/ folder.
191          *
192          * @param string $file Filename
193          * @return string Path to the file or empty string if the file isn't found
194          * @throws \Exception
195          */
196         public static function getPathForFile($file)
197         {
198                 $a = DI::app();
199
200                 $theme = $a->getCurrentTheme();
201
202                 $parent = Strings::sanitizeFilePathItem($a->theme_info['extends'] ?? $theme);
203
204                 $paths = [
205                         "view/theme/$theme/$file",
206                         "view/theme/$parent/$file",
207                         "view/$file",
208                 ];
209
210                 foreach ($paths as $path) {
211                         if (file_exists($path)) {
212                                 return $path;
213                         }
214                 }
215
216                 return '';
217         }
218
219         /**
220          * @brief Return relative path to theme stylesheet file
221          *
222          * Provide a sane default if nothing is chosen or the specified theme does not exist.
223          *
224          * @param string $theme Theme name
225          *
226          * @return string
227          */
228         public static function getStylesheetPath($theme)
229         {
230                 $theme = Strings::sanitizeFilePathItem($theme);
231
232                 if (!file_exists('view/theme/' . $theme . '/style.php')) {
233                         return 'view/theme/' . $theme . '/style.css';
234                 }
235
236                 $a = DI::app();
237
238                 $query_params = [];
239
240                 $puid = Profile::getThemeUid($a);
241                 if ($puid) {
242                         $query_params['puid'] = $puid;
243                 }
244
245                 return 'view/theme/' . $theme . '/style.pcss' . (!empty($query_params) ? '?' . http_build_query($query_params) : '');
246         }
247 }