|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* @package midcom.helper |
|
4
|
|
|
* @author The Midgard Project, http://www.midgard-project.org |
|
5
|
|
|
* @copyright The Midgard Project, http://www.midgard-project.org |
|
6
|
|
|
* @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License |
|
7
|
|
|
*/ |
|
8
|
|
|
|
|
9
|
|
|
use Cocur\Slugify\Slugify; |
|
10
|
|
|
|
|
11
|
|
|
/** |
|
12
|
|
|
* Miscellaneous helper functions |
|
13
|
|
|
* |
|
14
|
|
|
* @package midcom.helper |
|
15
|
|
|
*/ |
|
16
|
|
|
class midcom_helper_misc |
|
17
|
|
|
{ |
|
18
|
|
|
/** |
|
19
|
|
|
* @param integer $length |
|
20
|
|
|
* @param string $characters |
|
21
|
|
|
* @throws InvalidArgumentException |
|
22
|
|
|
*/ |
|
23
|
13 |
|
public static function random_string($length, $characters) : string |
|
24
|
|
|
{ |
|
25
|
13 |
|
if ($length < 1) { |
|
26
|
|
|
throw new InvalidArgumentException('invalid length'); |
|
27
|
|
|
} |
|
28
|
13 |
|
$size = strlen($characters) - 1; |
|
29
|
13 |
|
if ($size < 1) { |
|
30
|
|
|
throw new InvalidArgumentException('invalid characters'); |
|
31
|
|
|
} |
|
32
|
13 |
|
$return = ''; |
|
33
|
13 |
|
for ($i = 0; $i < $length; $i++) { |
|
34
|
13 |
|
$return .= $characters[random_int(0, $size)]; |
|
35
|
|
|
} |
|
36
|
13 |
|
return $return; |
|
37
|
|
|
} |
|
38
|
|
|
|
|
39
|
|
|
/** |
|
40
|
|
|
* @param string $input |
|
41
|
|
|
*/ |
|
42
|
27 |
|
public static function urlize($input) : string |
|
43
|
|
|
{ |
|
44
|
27 |
|
$slugify = new Slugify; |
|
45
|
27 |
|
return $slugify->slugify($input); |
|
46
|
|
|
} |
|
47
|
|
|
|
|
48
|
|
|
/** |
|
49
|
|
|
* Turn midcom config files into PHP arrays |
|
50
|
|
|
* |
|
51
|
|
|
* @param string $data The data to parse |
|
52
|
|
|
* @throws midcom_error |
|
53
|
|
|
*/ |
|
54
|
513 |
|
public static function parse_config($data) : array |
|
55
|
|
|
{ |
|
56
|
513 |
|
$data = eval("return [{$data}\n];"); |
|
|
|
|
|
|
57
|
513 |
|
if ($data === false) { |
|
58
|
|
|
throw new midcom_error("Failed to parse config data, see above for PHP errors."); |
|
59
|
|
|
} |
|
60
|
513 |
|
return $data; |
|
61
|
|
|
} |
|
62
|
|
|
|
|
63
|
|
|
/** |
|
64
|
|
|
* This helper function searches for a snippet either in the Filesystem |
|
65
|
|
|
* or in the database and returns its content or code-field, respectively. |
|
66
|
|
|
* |
|
67
|
|
|
* Prefix the snippet Path with 'file:' for retrieval of a file relative to |
|
68
|
|
|
* MIDCOM_ROOT; omit it to get the code field of a Snippet. |
|
69
|
|
|
* |
|
70
|
|
|
* Any error (files not found) will return null. If you want to trigger an error, |
|
71
|
|
|
* look for midcom_helper_misc::get_snippet_content. |
|
72
|
|
|
* |
|
73
|
|
|
* @param string $path The URL to the snippet. |
|
74
|
|
|
* @return string The content of the snippet/file. |
|
75
|
|
|
*/ |
|
76
|
513 |
|
public static function get_snippet_content_graceful($path) |
|
77
|
|
|
{ |
|
78
|
513 |
|
static $cached_snippets = []; |
|
79
|
|
|
|
|
80
|
513 |
|
if (!array_key_exists($path, $cached_snippets)) { |
|
81
|
63 |
|
if (substr($path, 0, 5) == 'file:') { |
|
82
|
46 |
|
$cached_snippets[$path] = self::load_from_file($path); |
|
83
|
22 |
|
} elseif (substr($path, 0, 5) == 'conf:') { |
|
84
|
22 |
|
$cached_snippets[$path] = self::load(midcom::get()->config->get('midcom_config_basedir') . '/midcom' . substr($path, 5)); |
|
85
|
|
|
} else { |
|
86
|
17 |
|
$cached_snippets[$path] = self::load_from_snippet($path); |
|
87
|
|
|
} |
|
88
|
|
|
} |
|
89
|
|
|
|
|
90
|
513 |
|
return $cached_snippets[$path]; |
|
91
|
|
|
} |
|
92
|
|
|
|
|
93
|
17 |
|
private static function load_from_snippet(string $path) |
|
94
|
|
|
{ |
|
95
|
17 |
|
$snippet = new midgard_snippet(); |
|
96
|
17 |
|
if (!$snippet->get_by_path($path)) { |
|
97
|
17 |
|
return null; |
|
98
|
|
|
} |
|
99
|
|
|
if (isset(midcom::get()->cache->content)) { |
|
100
|
|
|
midcom::get()->cache->content->register($snippet->guid); |
|
101
|
|
|
} |
|
102
|
|
|
return $snippet->code; |
|
103
|
|
|
} |
|
104
|
|
|
|
|
105
|
46 |
|
private static function load_from_file(string $path) |
|
106
|
|
|
{ |
|
107
|
46 |
|
$filename = MIDCOM_ROOT . substr($path, 5); |
|
108
|
46 |
|
if (!file_exists($filename)) { |
|
109
|
|
|
// try in src |
|
110
|
2 |
|
$filename = preg_replace('/\/lib\/?$/', '/src', MIDCOM_ROOT) . substr($path, 5); |
|
111
|
2 |
|
if (!file_exists($filename)) { |
|
112
|
|
|
//If we can't find the file in-tree, we look for out-of-tree components before giving up |
|
113
|
|
|
$filename = substr($path, 6); |
|
114
|
|
|
if (preg_match('|.+?/.+?/.+?/|', $filename)) { |
|
115
|
|
|
$component_name = preg_replace('|(.+?)/(.+?)/(.+?)/.+|', '$1.$2.$3', $filename); |
|
116
|
|
|
if (midcom::get()->componentloader->is_installed($component_name)) { |
|
117
|
|
|
$filename = substr($filename, strlen($component_name)); |
|
118
|
|
|
$filename = midcom::get()->componentloader->path_to_snippetpath($component_name) . $filename; |
|
119
|
|
|
} |
|
120
|
|
|
} |
|
121
|
|
|
} |
|
122
|
|
|
} |
|
123
|
46 |
|
return self::load($filename); |
|
124
|
|
|
} |
|
125
|
|
|
|
|
126
|
63 |
|
private static function load(string $filename) |
|
127
|
|
|
{ |
|
128
|
63 |
|
if (!file_exists($filename)) { |
|
129
|
22 |
|
return null; |
|
130
|
|
|
} |
|
131
|
46 |
|
return file_get_contents($filename); |
|
132
|
|
|
} |
|
133
|
|
|
|
|
134
|
|
|
/** |
|
135
|
|
|
* This helper function searches for a snippet either in the Filesystem |
|
136
|
|
|
* or in the database and returns its content or code-field, respectively. |
|
137
|
|
|
* |
|
138
|
|
|
* Prefix the snippet Path with 'file:' for retrieval of a file relative to |
|
139
|
|
|
* MIDCOM_ROOT; omit it to get the code field of a Snippet. |
|
140
|
|
|
* |
|
141
|
|
|
* Any error (files not found) will raise a MidCOM Error. If you want a more |
|
142
|
|
|
* graceful behavior, look for midcom_helper_misc::get_snippet_content_graceful |
|
143
|
|
|
* |
|
144
|
|
|
* @param string $path The URL to the snippet. |
|
145
|
|
|
*/ |
|
146
|
247 |
|
public static function get_snippet_content($path) : string |
|
147
|
|
|
{ |
|
148
|
247 |
|
$data = self::get_snippet_content_graceful($path); |
|
149
|
247 |
|
if ($data === null) { |
|
|
|
|
|
|
150
|
|
|
throw new midcom_error("Could not load the contents of the snippet {$path}: Snippet does not exist."); |
|
151
|
|
|
} |
|
152
|
247 |
|
return $data; |
|
153
|
|
|
} |
|
154
|
|
|
|
|
155
|
|
|
/** |
|
156
|
|
|
* This is a bit of a hack to allow &(); tags |
|
157
|
|
|
* |
|
158
|
|
|
* @param string $code The unprocessed code |
|
159
|
|
|
*/ |
|
160
|
201 |
|
public static function preparse($code) : string |
|
161
|
|
|
{ |
|
162
|
|
|
// Get style elements |
|
163
|
201 |
|
$code = preg_replace_callback("/<\\(([a-zA-Z0-9 _-]+)\\)>/", [midcom_helper_misc::class, 'include_element'], $code); |
|
164
|
|
|
// Echo variables |
|
165
|
201 |
|
return preg_replace_callback("%&\(([^)]*)\);%i", [midcom_helper_formatter::class, 'convert_to_php'], $code); |
|
166
|
|
|
} |
|
167
|
|
|
|
|
168
|
|
|
/** |
|
169
|
|
|
* Include a theme element |
|
170
|
|
|
*/ |
|
171
|
20 |
|
public static function include_element($name) : string |
|
172
|
|
|
{ |
|
173
|
20 |
|
if (is_array($name)) { |
|
174
|
20 |
|
$element = $name[1]; |
|
175
|
|
|
} else { |
|
176
|
|
|
$element = $name; |
|
177
|
|
|
} |
|
178
|
|
|
|
|
179
|
20 |
|
switch ($element) { |
|
180
|
20 |
|
case 'title': |
|
181
|
10 |
|
return midcom::get()->config->get('midcom_site_title'); |
|
182
|
19 |
|
case 'content': |
|
183
|
19 |
|
return '<?php midcom_core_context::get()->show(); ?>'; |
|
184
|
|
|
default: |
|
185
|
9 |
|
$value = self::get_element_content($element); |
|
186
|
|
|
|
|
187
|
9 |
|
if (empty($value)) { |
|
188
|
|
|
return ''; |
|
189
|
|
|
} |
|
190
|
9 |
|
return preg_replace_callback("/<\\(([a-zA-Z0-9 _-]+)\\)>/", [midcom_helper_misc::class, 'include_element'], $value); |
|
191
|
|
|
} |
|
192
|
|
|
} |
|
193
|
|
|
|
|
194
|
|
|
/** |
|
195
|
|
|
* Find MIME type image for a document |
|
196
|
|
|
* |
|
197
|
|
|
* Used in midcom.helper.imagepopup, midgard.admin.asgard and org.openpsa.documents. |
|
198
|
|
|
* |
|
199
|
|
|
* @param string $mimetype Document MIME type |
|
200
|
|
|
* @return string Path to the icon |
|
201
|
|
|
*/ |
|
202
|
|
|
public static function get_mime_icon($mimetype) : string |
|
203
|
|
|
{ |
|
204
|
|
|
$mime_fspath = MIDCOM_STATIC_ROOT . '/stock-icons/mime'; |
|
205
|
|
|
$mime_urlpath = MIDCOM_STATIC_URL . '/stock-icons/mime'; |
|
206
|
|
|
$mimetype_filename = str_replace('/', '-', $mimetype); |
|
207
|
|
|
if (!is_readable($mime_fspath)) { |
|
208
|
|
|
debug_add("Couldn't read directory {$mime_fspath}", MIDCOM_LOG_WARN); |
|
209
|
|
|
} |
|
210
|
|
|
|
|
211
|
|
|
$check_files = []; |
|
212
|
|
|
switch ($mimetype_filename) { |
|
213
|
|
|
case 'application-x-zip-compressed': |
|
214
|
|
|
$check_files[] = "gnome-application-zip.png"; |
|
215
|
|
|
break; |
|
216
|
|
|
default: |
|
217
|
|
|
$check_files[] = "{$mimetype_filename}.png"; |
|
218
|
|
|
$check_files[] = "gnome-{$mimetype_filename}.png"; |
|
219
|
|
|
break; |
|
220
|
|
|
} |
|
221
|
|
|
|
|
222
|
|
|
// Return first match |
|
223
|
|
|
foreach ($check_files as $filename) { |
|
224
|
|
|
if (is_readable("{$mime_fspath}/{$filename}")) { |
|
225
|
|
|
return "{$mime_urlpath}/{$filename}"; |
|
226
|
|
|
} |
|
227
|
|
|
} |
|
228
|
|
|
// Default icon if there is none for the MIME type |
|
229
|
|
|
return $mime_urlpath . '/gnome-unknown.png'; |
|
230
|
|
|
} |
|
231
|
|
|
|
|
232
|
|
|
/** |
|
233
|
|
|
* Pretty print file sizes |
|
234
|
|
|
* |
|
235
|
|
|
* @param int $size File size in bytes |
|
236
|
|
|
*/ |
|
237
|
7 |
|
public static function filesize_to_string($size) : string |
|
238
|
|
|
{ |
|
239
|
7 |
|
if ($size >= 1048576) { |
|
240
|
|
|
// More than a meg |
|
241
|
|
|
return sprintf("%01.1f", $size / 1048576) . " MB"; |
|
242
|
|
|
} |
|
243
|
7 |
|
if ($size >= 1024) { |
|
244
|
|
|
// More than a kilo |
|
245
|
|
|
return sprintf("%01.1f", $size / 1024) . " KB"; |
|
246
|
|
|
} |
|
247
|
7 |
|
return $size . " Bytes"; |
|
248
|
|
|
} |
|
249
|
|
|
|
|
250
|
|
|
/** |
|
251
|
|
|
* Fix newline etc encoding issues in serialized data |
|
252
|
|
|
* |
|
253
|
|
|
* @param string $data The data to fix. |
|
254
|
|
|
* @return string $data with serializations fixed. |
|
255
|
|
|
*/ |
|
256
|
|
|
public static function fix_serialization($data) |
|
257
|
|
|
{ |
|
258
|
|
|
//Skip on empty data |
|
259
|
|
|
if (empty($data)) { |
|
260
|
|
|
return $data; |
|
261
|
|
|
} |
|
262
|
|
|
|
|
263
|
|
|
$preg='/s:([0-9]+):"(.*?)";/ms'; |
|
264
|
|
|
preg_match_all($preg, $data, $matches); |
|
265
|
|
|
$cache = []; |
|
266
|
|
|
|
|
267
|
|
|
foreach ($matches[0] as $k => $origFullStr) { |
|
268
|
|
|
$origLen = $matches[1][$k]; |
|
269
|
|
|
$origStr = $matches[2][$k]; |
|
270
|
|
|
$newLen = strlen($origStr); |
|
271
|
|
|
if ($newLen != $origLen) { |
|
272
|
|
|
$newFullStr = "s:$newLen:\"$origStr\";"; |
|
273
|
|
|
//For performance we cache information on which strings have already been replaced |
|
274
|
|
|
if (!array_key_exists($origFullStr, $cache)) { |
|
275
|
|
|
$data = str_replace($origFullStr, $newFullStr, $data); |
|
276
|
|
|
$cache[$origFullStr] = true; |
|
277
|
|
|
} |
|
278
|
|
|
} |
|
279
|
|
|
} |
|
280
|
|
|
|
|
281
|
|
|
return $data; |
|
282
|
|
|
} |
|
283
|
|
|
|
|
284
|
|
|
/** |
|
285
|
|
|
* Returns the first instance of a given component on the site. |
|
286
|
|
|
* |
|
287
|
|
|
* @param string $component The component name |
|
288
|
|
|
* @return array NAP array of the first component instance found |
|
289
|
|
|
*/ |
|
290
|
6 |
|
public static function find_node_by_component($component) |
|
291
|
|
|
{ |
|
292
|
6 |
|
static $cache = []; |
|
293
|
|
|
|
|
294
|
6 |
|
if (!array_key_exists($component, $cache)) { |
|
295
|
2 |
|
$cache[$component] = null; |
|
296
|
|
|
|
|
297
|
2 |
|
$nap = new midcom_helper_nav; |
|
298
|
2 |
|
$node_id = $nap->get_root_node(); |
|
299
|
2 |
|
$root_node = $nap->get_node($node_id); |
|
300
|
|
|
|
|
301
|
2 |
|
if ($root_node[MIDCOM_NAV_COMPONENT] == $component) { |
|
302
|
|
|
$cache[$component] = $root_node; |
|
303
|
|
|
} else { |
|
304
|
2 |
|
$qb = midcom_db_topic::new_query_builder(); |
|
305
|
2 |
|
$qb->add_constraint('component', '=', $component); |
|
306
|
2 |
|
$qb->add_constraint('name', '<>', ''); |
|
307
|
2 |
|
$qb->add_constraint('up', 'INTREE', $node_id); |
|
308
|
2 |
|
$qb->set_limit(1); |
|
309
|
2 |
|
$topics = $qb->execute(); |
|
310
|
|
|
|
|
311
|
2 |
|
if (count($topics) === 1) { |
|
312
|
1 |
|
$cache[$component] = $nap->get_node($topics[0]->id); |
|
313
|
|
|
} |
|
314
|
|
|
} |
|
315
|
|
|
} |
|
316
|
|
|
|
|
317
|
6 |
|
return $cache[$component]; |
|
318
|
|
|
} |
|
319
|
|
|
|
|
320
|
|
|
/** |
|
321
|
|
|
* Get the content of the element by the passed element name. |
|
322
|
|
|
* Tries to resolve path according to theme-name & page |
|
323
|
|
|
* |
|
324
|
|
|
* @param string $element_name |
|
325
|
|
|
*/ |
|
326
|
202 |
|
public static function get_element_content($element_name) |
|
327
|
|
|
{ |
|
328
|
202 |
|
$theme = midcom::get()->config->get('theme'); |
|
329
|
202 |
|
$path_array = explode('/', $theme); |
|
330
|
|
|
|
|
331
|
|
|
//get the page if there is one |
|
332
|
202 |
|
$page = midcom_connection::get_url('page_style'); |
|
333
|
202 |
|
$substyle = midcom_core_context::get()->get_key(MIDCOM_CONTEXT_SUBSTYLE); |
|
334
|
|
|
//check if we have elements for the sub-styles |
|
335
|
202 |
|
while (!empty($path_array)) { |
|
336
|
202 |
|
$theme_path = implode('/', $path_array); |
|
337
|
202 |
|
$candidates = []; |
|
338
|
202 |
|
if ($substyle) { |
|
339
|
31 |
|
$candidates[] = '/' . $substyle . '/' . $element_name; |
|
340
|
|
|
} |
|
341
|
202 |
|
if ($page) { |
|
342
|
|
|
$candidates[] = $page . '/' . $element_name; |
|
343
|
|
|
} |
|
344
|
202 |
|
$candidates[] = '/' . $element_name; |
|
345
|
|
|
|
|
346
|
202 |
|
foreach ($candidates as $candidate) { |
|
347
|
202 |
|
$filename = OPENPSA2_THEME_ROOT . $theme_path . '/style' . $candidate . '.php'; |
|
348
|
202 |
|
if (file_exists($filename)) { |
|
349
|
10 |
|
return file_get_contents($filename); |
|
350
|
|
|
} |
|
351
|
|
|
} |
|
352
|
|
|
|
|
353
|
|
|
//remove last theme part |
|
354
|
202 |
|
array_pop($path_array); |
|
355
|
|
|
} |
|
356
|
|
|
|
|
357
|
202 |
|
return false; |
|
358
|
|
|
} |
|
359
|
|
|
} |
|
360
|
|
|
|