1 | <?php |
||||
2 | |||||
3 | /** |
||||
4 | * This file contains functions to export a member's profile data to a |
||||
5 | * downloadable file. |
||||
6 | * |
||||
7 | * Simple Machines Forum (SMF) |
||||
8 | * |
||||
9 | * @package SMF |
||||
10 | * @author Simple Machines https://www.simplemachines.org |
||||
11 | * @copyright 2022 Simple Machines and individual contributors |
||||
12 | * @license https://www.simplemachines.org/about/smf/license.php BSD |
||||
13 | * |
||||
14 | * @version 2.1.2 |
||||
15 | */ |
||||
16 | |||||
17 | if (!defined('SMF')) |
||||
18 | die('No direct access...'); |
||||
19 | |||||
20 | |||||
21 | /** |
||||
22 | * Initiates exports a member's profile, posts, and personal messages to a file. |
||||
23 | * |
||||
24 | * @todo Add CSV, JSON as other possible export formats besides XML and HTML? |
||||
25 | * |
||||
26 | * @param int $uid The ID of the member whose data we're exporting. |
||||
27 | */ |
||||
28 | function export_profile_data($uid) |
||||
29 | { |
||||
30 | global $context, $smcFunc, $txt, $modSettings, $sourcedir, $scripturl; |
||||
31 | global $query_this_board; |
||||
32 | |||||
33 | if (!isset($context['token_check'])) |
||||
34 | $context['token_check'] = 'profile-ex' . $uid; |
||||
35 | |||||
36 | $context['export_formats'] = get_export_formats(); |
||||
37 | |||||
38 | if (!isset($_POST['format']) || !isset($context['export_formats'][$_POST['format']])) |
||||
39 | unset($_POST['format'], $_POST['delete'], $_POST['export_begin']); |
||||
40 | |||||
41 | // This lists the types of data we can export and info for doing so. |
||||
42 | $context['export_datatypes'] = array( |
||||
43 | 'profile' => array( |
||||
44 | 'label' => null, |
||||
45 | 'total' => 0, |
||||
46 | 'latest' => 1, |
||||
47 | // Instructions to pass to ExportProfileData background task: |
||||
48 | 'XML' => array( |
||||
49 | 'func' => 'getXmlProfile', |
||||
50 | 'langfile' => 'Profile', |
||||
51 | ), |
||||
52 | 'HTML' => array( |
||||
53 | 'func' => 'getXmlProfile', |
||||
54 | 'langfile' => 'Profile', |
||||
55 | ), |
||||
56 | 'XML_XSLT' => array( |
||||
57 | 'func' => 'getXmlProfile', |
||||
58 | 'langfile' => 'Profile', |
||||
59 | ), |
||||
60 | // 'CSV' => array(), |
||||
61 | // 'JSON' => array(), |
||||
62 | ), |
||||
63 | 'posts' => array( |
||||
64 | 'label' => $txt['export_include_posts'], |
||||
65 | 'total' => $context['member']['real_posts'], |
||||
66 | 'latest' => function($uid) |
||||
67 | { |
||||
68 | global $smcFunc, $modSettings; |
||||
69 | |||||
70 | static $latest_post; |
||||
71 | |||||
72 | if (isset($latest_post)) |
||||
73 | return $latest_post; |
||||
74 | |||||
75 | $query_this_board = !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? 'b.id_board != ' . $modSettings['recycle_board'] : '1=1'; |
||||
76 | |||||
77 | $request = $smcFunc['db_query']('', ' |
||||
78 | SELECT m.id_msg |
||||
79 | FROM {db_prefix}messages as m |
||||
80 | INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board) |
||||
81 | WHERE id_member = {int:uid} |
||||
82 | AND ' . $query_this_board . ' |
||||
83 | ORDER BY id_msg DESC |
||||
84 | LIMIT {int:limit}', |
||||
85 | array( |
||||
86 | 'limit' => 1, |
||||
87 | 'uid' => $uid, |
||||
88 | ) |
||||
89 | ); |
||||
90 | list($latest_post) = $smcFunc['db_fetch_row']($request); |
||||
91 | $smcFunc['db_free_result']($request); |
||||
92 | |||||
93 | return $latest_post; |
||||
94 | }, |
||||
95 | // Instructions to pass to ExportProfileData background task: |
||||
96 | 'XML' => array( |
||||
97 | 'func' => 'getXmlPosts', |
||||
98 | 'langfile' => 'Post', |
||||
99 | ), |
||||
100 | 'HTML' => array( |
||||
101 | 'func' => 'getXmlPosts', |
||||
102 | 'langfile' => 'Post', |
||||
103 | ), |
||||
104 | 'XML_XSLT' => array( |
||||
105 | 'func' => 'getXmlPosts', |
||||
106 | 'langfile' => 'Post', |
||||
107 | ), |
||||
108 | // 'CSV' => array(), |
||||
109 | // 'JSON' => array(), |
||||
110 | ), |
||||
111 | 'personal_messages' => array( |
||||
112 | 'label' => $txt['export_include_personal_messages'], |
||||
113 | 'total' => function($uid) |
||||
114 | { |
||||
115 | global $smcFunc; |
||||
116 | |||||
117 | static $total_pms; |
||||
118 | |||||
119 | if (isset($total_pms)) |
||||
120 | return $total_pms; |
||||
121 | |||||
122 | $request = $smcFunc['db_query']('', ' |
||||
123 | SELECT COUNT(*) |
||||
124 | FROM {db_prefix}personal_messages AS pm |
||||
125 | INNER JOIN {db_prefix}pm_recipients AS pmr ON (pm.id_pm = pmr.id_pm) |
||||
126 | WHERE (pm.id_member_from = {int:uid} AND pm.deleted_by_sender = {int:not_deleted}) |
||||
127 | OR (pmr.id_member = {int:uid} AND pmr.deleted = {int:not_deleted})', |
||||
128 | array( |
||||
129 | 'uid' => $uid, |
||||
130 | 'not_deleted' => 0, |
||||
131 | ) |
||||
132 | ); |
||||
133 | list($total_pms) = $smcFunc['db_fetch_row']($request); |
||||
134 | $smcFunc['db_free_result']($request); |
||||
135 | |||||
136 | return $total_pms; |
||||
137 | }, |
||||
138 | 'latest' => function($uid) |
||||
139 | { |
||||
140 | global $smcFunc; |
||||
141 | |||||
142 | static $latest_pm; |
||||
143 | |||||
144 | if (isset($latest_pm)) |
||||
145 | return $latest_pm; |
||||
146 | |||||
147 | $request = $smcFunc['db_query']('', ' |
||||
148 | SELECT pm.id_pm |
||||
149 | FROM {db_prefix}personal_messages AS pm |
||||
150 | INNER JOIN {db_prefix}pm_recipients AS pmr ON (pm.id_pm = pmr.id_pm) |
||||
151 | WHERE (pm.id_member_from = {int:uid} AND pm.deleted_by_sender = {int:not_deleted}) |
||||
152 | OR (pmr.id_member = {int:uid} AND pmr.deleted = {int:not_deleted}) |
||||
153 | ORDER BY pm.id_pm DESC |
||||
154 | LIMIT {int:limit}', |
||||
155 | array( |
||||
156 | 'limit' => 1, |
||||
157 | 'uid' => $uid, |
||||
158 | 'not_deleted' => 0, |
||||
159 | ) |
||||
160 | ); |
||||
161 | list($latest_pm) = $smcFunc['db_fetch_row']($request); |
||||
162 | $smcFunc['db_free_result']($request); |
||||
163 | |||||
164 | return $latest_pm; |
||||
165 | }, |
||||
166 | // Instructions to pass to ExportProfileData background task: |
||||
167 | 'XML' => array( |
||||
168 | 'func' => 'getXmlPMs', |
||||
169 | 'langfile' => 'PersonalMessage', |
||||
170 | ), |
||||
171 | 'HTML' => array( |
||||
172 | 'func' => 'getXmlPMs', |
||||
173 | 'langfile' => 'PersonalMessage', |
||||
174 | ), |
||||
175 | 'XML_XSLT' => array( |
||||
176 | 'func' => 'getXmlPMs', |
||||
177 | 'langfile' => 'PersonalMessage', |
||||
178 | ), |
||||
179 | // 'CSV' => array(), |
||||
180 | // 'JSON' => array(), |
||||
181 | ), |
||||
182 | ); |
||||
183 | |||||
184 | if (empty($modSettings['export_dir']) || !is_dir($modSettings['export_dir']) || !smf_chmod($modSettings['export_dir'])) |
||||
185 | create_export_dir(); |
||||
186 | |||||
187 | $export_dir_slash = $modSettings['export_dir'] . DIRECTORY_SEPARATOR; |
||||
188 | |||||
189 | $idhash = hash_hmac('sha1', $uid, get_auth_secret()); |
||||
190 | $dltoken = hash_hmac('sha1', $idhash, get_auth_secret()); |
||||
191 | |||||
192 | $query_this_board = !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? 'b.id_board != ' . $modSettings['recycle_board'] : '1=1'; |
||||
193 | |||||
194 | $context['completed_exports'] = array(); |
||||
195 | $context['active_exports'] = array(); |
||||
196 | $existing_export_formats = array(); |
||||
197 | $latest = array(); |
||||
198 | |||||
199 | foreach ($context['export_formats'] as $format => $format_settings) |
||||
200 | { |
||||
201 | $idhash_ext = $idhash . '.' . $format_settings['extension']; |
||||
202 | |||||
203 | $done = null; |
||||
204 | $context['outdated_exports'][$idhash_ext] = array(); |
||||
205 | |||||
206 | // $realfile needs to be the highest numbered one, or 1_*** if none exist. |
||||
207 | $filenum = 1; |
||||
208 | $realfile = $export_dir_slash . $filenum . '_' . $idhash_ext; |
||||
209 | while (file_exists($export_dir_slash . ($filenum + 1) . '_' . $idhash_ext)) |
||||
210 | $realfile = $export_dir_slash . ++$filenum . '_' . $idhash_ext; |
||||
211 | |||||
212 | $tempfile = $export_dir_slash . $idhash_ext . '.tmp'; |
||||
213 | $progressfile = $export_dir_slash . $idhash_ext . '.progress.json'; |
||||
214 | |||||
215 | // If requested by the user, delete any existing export files and background tasks. |
||||
216 | if (isset($_POST['delete']) && isset($_POST['format']) && $_POST['format'] === $format && isset($_POST['t']) && $_POST['t'] === $dltoken) |
||||
217 | { |
||||
218 | $smcFunc['db_query']('', ' |
||||
219 | DELETE FROM {db_prefix}background_tasks |
||||
220 | WHERE task_class = {string:class} |
||||
221 | AND task_data LIKE {string:details}', |
||||
222 | array( |
||||
223 | 'class' => 'ExportProfileData_Background', |
||||
224 | 'details' => substr($smcFunc['json_encode'](array('format' => $format, 'uid' => $uid)), 0, -1) . ',%', |
||||
225 | ) |
||||
226 | ); |
||||
227 | |||||
228 | foreach (glob($export_dir_slash . '*' . $idhash_ext . '*') as $fpath) |
||||
229 | @unlink($fpath); |
||||
230 | |||||
231 | if (empty($_POST['export_begin'])) |
||||
232 | redirectexit('action=profile;area=getprofiledata;u=' . $uid); |
||||
233 | } |
||||
234 | |||||
235 | $progress = file_exists($progressfile) ? $smcFunc['json_decode'](file_get_contents($progressfile), true) : array(); |
||||
236 | |||||
237 | if (!empty($progress)) |
||||
238 | $included = array_keys($progress); |
||||
239 | else |
||||
240 | $included = array_intersect(array_keys($context['export_datatypes']), array_keys($_POST)); |
||||
241 | |||||
242 | // If we're starting a new export in this format, we're done here. |
||||
243 | if (!empty($_POST['export_begin']) && isset($_POST['format']) && $_POST['format'] === $format) |
||||
244 | break; |
||||
245 | |||||
246 | // The rest of this loop deals with current exports, if any. |
||||
247 | |||||
248 | $included_desc = array(); |
||||
249 | foreach ($included as $datatype) |
||||
250 | $included_desc[] = $txt[$datatype]; |
||||
251 | |||||
252 | $dlfilename = array_merge(array($context['forum_name'], $context['member']['username']), $included_desc); |
||||
253 | $dlfilename = preg_replace('/[^\p{L}\p{M}\p{N}_]+/u', '-', str_replace('"', '', un_htmlspecialchars(strip_tags(implode('_', $dlfilename))))); |
||||
254 | |||||
255 | if (file_exists($tempfile) && file_exists($progressfile)) |
||||
256 | { |
||||
257 | $done = false; |
||||
258 | } |
||||
259 | elseif (file_exists($realfile)) |
||||
260 | { |
||||
261 | // It looks like we're done. |
||||
262 | $done = true; |
||||
263 | |||||
264 | // But let's check whether it's outdated. |
||||
265 | foreach ($context['export_datatypes'] as $datatype => $datatype_settings) |
||||
266 | { |
||||
267 | if (!isset($progress[$datatype])) |
||||
268 | continue; |
||||
269 | |||||
270 | if (!isset($latest[$datatype])) |
||||
271 | $latest[$datatype] = is_callable($datatype_settings['latest']) ? $datatype_settings['latest']($uid) : $datatype_settings['latest']; |
||||
272 | |||||
273 | if ($latest[$datatype] > $progress[$datatype]) |
||||
274 | $context['outdated_exports'][$idhash_ext][] = $datatype; |
||||
275 | } |
||||
276 | } |
||||
277 | |||||
278 | if ($done === true) |
||||
279 | { |
||||
280 | $exportfilepaths = glob($export_dir_slash . '*_' . $idhash_ext); |
||||
281 | |||||
282 | foreach ($exportfilepaths as $exportfilepath) |
||||
283 | { |
||||
284 | $exportbasename = basename($exportfilepath); |
||||
285 | |||||
286 | $part = substr($exportbasename, 0, strcspn($exportbasename, '_')); |
||||
287 | $suffix = count($exportfilepaths) == 1 ? '' : '_' . $part; |
||||
288 | |||||
289 | $size = filesize($exportfilepath) / 1024; |
||||
290 | $units = array('KB', 'MB', 'GB', 'TB'); |
||||
291 | $unitkey = 0; |
||||
292 | while ($size > 1024) |
||||
293 | { |
||||
294 | $size = $size / 1024; |
||||
295 | $unitkey++; |
||||
296 | } |
||||
297 | $size = round($size, 2) . $units[$unitkey]; |
||||
298 | |||||
299 | $context['completed_exports'][$idhash_ext][$part] = array( |
||||
300 | 'realname' => $exportbasename, |
||||
301 | 'dlbasename' => $dlfilename . $suffix . '.' . $format_settings['extension'], |
||||
302 | 'dltoken' => $dltoken, |
||||
303 | 'included' => $included, |
||||
304 | 'included_desc' => sentence_list($included_desc), |
||||
305 | 'format' => $format, |
||||
306 | 'mtime' => timeformat(filemtime($exportfilepath)), |
||||
307 | 'size' => $size, |
||||
308 | ); |
||||
309 | } |
||||
310 | |||||
311 | ksort($context['completed_exports'][$idhash_ext], SORT_NUMERIC); |
||||
312 | |||||
313 | $existing_export_formats[] = $format; |
||||
314 | } |
||||
315 | elseif ($done === false) |
||||
316 | { |
||||
317 | $context['active_exports'][$idhash_ext] = array( |
||||
318 | 'dltoken' => $dltoken, |
||||
319 | 'included' => $included, |
||||
320 | 'included_desc' => sentence_list($included_desc), |
||||
321 | 'format' => $format, |
||||
322 | ); |
||||
323 | |||||
324 | $existing_export_formats[] = $format; |
||||
325 | } |
||||
326 | } |
||||
327 | |||||
328 | if (!empty($_POST['export_begin'])) |
||||
329 | { |
||||
330 | checkSession(); |
||||
331 | validateToken($context['token_check'], 'post'); |
||||
332 | |||||
333 | $format = isset($_POST['format']) && isset($context['export_formats'][$_POST['format']]) ? $_POST['format'] : 'XML'; |
||||
334 | |||||
335 | $included = array(); |
||||
336 | $included_desc = array(); |
||||
337 | foreach ($context['export_datatypes'] as $datatype => $datatype_settings) |
||||
338 | { |
||||
339 | if ($datatype == 'profile' || !empty($_POST[$datatype])) |
||||
340 | { |
||||
341 | $included[$datatype] = $datatype_settings[$format]; |
||||
342 | $included_desc[] = $txt[$datatype]; |
||||
343 | |||||
344 | $start[$datatype] = !empty($start[$datatype]) ? $start[$datatype] : 0; |
||||
345 | |||||
346 | if (!isset($latest[$datatype])) |
||||
347 | $latest[$datatype] = is_callable($datatype_settings['latest']) ? $datatype_settings['latest']($uid) : $datatype_settings['latest']; |
||||
348 | |||||
349 | if (!isset($total[$datatype])) |
||||
350 | $total[$datatype] = is_callable($datatype_settings['total']) ? $datatype_settings['total']($uid) : $datatype_settings['total']; |
||||
351 | } |
||||
352 | } |
||||
353 | |||||
354 | $dlfilename = array_merge(array($context['forum_name'], $context['member']['username']), $included_desc); |
||||
355 | $dlfilename = preg_replace('/[^\p{L}\p{M}\p{N}_]+/u', '-', str_replace('"', '', un_htmlspecialchars(strip_tags(implode('_', $dlfilename))))); |
||||
356 | |||||
357 | $last_page = ceil(array_sum($total) / $context['export_formats'][$format]['per_page']); |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
![]() |
|||||
358 | |||||
359 | $data = $smcFunc['json_encode'](array( |
||||
360 | 'format' => $format, |
||||
361 | 'uid' => $uid, |
||||
362 | 'lang' => $context['member']['language'], |
||||
363 | 'included' => $included, |
||||
364 | 'start' => $start, |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
365 | 'latest' => $latest, |
||||
366 | 'datatype' => isset($current_datatype) ? $current_datatype : key($included), |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
367 | 'format_settings' => $context['export_formats'][$format], |
||||
368 | 'last_page' => $last_page, |
||||
369 | 'dlfilename' => $dlfilename, |
||||
370 | )); |
||||
371 | |||||
372 | $smcFunc['db_insert']('insert', '{db_prefix}background_tasks', |
||||
373 | array('task_file' => 'string-255', 'task_class' => 'string-255', 'task_data' => 'string', 'claimed_time' => 'int'), |
||||
374 | array('$sourcedir/tasks/ExportProfileData.php', 'ExportProfileData_Background', $data, 0), |
||||
375 | array() |
||||
376 | ); |
||||
377 | |||||
378 | // So the user can see that we've started. |
||||
379 | if (!file_exists($tempfile)) |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
380 | touch($tempfile); |
||||
381 | if (!file_exists($progressfile)) |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
382 | file_put_contents($progressfile, $smcFunc['json_encode'](array_fill_keys(array_keys($included), 0))); |
||||
383 | |||||
384 | redirectexit('action=profile;area=getprofiledata;u=' . $uid); |
||||
385 | } |
||||
386 | |||||
387 | createToken($context['token_check'], 'post'); |
||||
388 | |||||
389 | $context['page_title'] = $txt['export_profile_data']; |
||||
390 | |||||
391 | if (empty($modSettings['export_expiry'])) |
||||
392 | unset($txt['export_profile_data_desc_list']['expiry']); |
||||
393 | else |
||||
394 | $txt['export_profile_data_desc_list']['expiry'] = sprintf($txt['export_profile_data_desc_list']['expiry'], $modSettings['export_expiry']); |
||||
395 | |||||
396 | $context['export_profile_data_desc'] = sprintf($txt['export_profile_data_desc'], '<li>' . implode('</li><li>', $txt['export_profile_data_desc_list']) . '</li>'); |
||||
397 | |||||
398 | addJavaScriptVar('completed_formats', '[\'' . implode('\', \'', array_unique($existing_export_formats)) . '\']', false); |
||||
399 | } |
||||
400 | |||||
401 | /** |
||||
402 | * Downloads exported profile data file. |
||||
403 | * |
||||
404 | * @param int $uid The ID of the member whose data we're exporting. |
||||
405 | */ |
||||
406 | function download_export_file($uid) |
||||
407 | { |
||||
408 | global $modSettings, $maintenance, $context, $txt, $smcFunc; |
||||
409 | |||||
410 | $export_formats = get_export_formats(); |
||||
411 | |||||
412 | // This is done to clear any output that was made before now. |
||||
413 | ob_end_clean(); |
||||
414 | |||||
415 | if (!empty($modSettings['enableCompressedOutput']) && !headers_sent() && ob_get_length() == 0) |
||||
416 | { |
||||
417 | if (@ini_get('zlib.output_compression') == '1' || @ini_get('output_handler') == 'ob_gzhandler') |
||||
418 | $modSettings['enableCompressedOutput'] = 0; |
||||
419 | |||||
420 | else |
||||
421 | ob_start('ob_gzhandler'); |
||||
422 | } |
||||
423 | |||||
424 | if (empty($modSettings['enableCompressedOutput'])) |
||||
425 | { |
||||
426 | ob_start(); |
||||
427 | header('content-encoding: none'); |
||||
428 | } |
||||
429 | |||||
430 | // No access in strict maintenance mode. |
||||
431 | if (!empty($maintenance) && $maintenance == 2) |
||||
432 | { |
||||
433 | send_http_status(404); |
||||
434 | exit; |
||||
0 ignored issues
–
show
|
|||||
435 | } |
||||
436 | |||||
437 | // We can't give them anything without these. |
||||
438 | if (empty($_GET['t']) || empty($_GET['format']) || !isset($export_formats[$_GET['format']])) |
||||
439 | { |
||||
440 | send_http_status(400); |
||||
441 | exit; |
||||
0 ignored issues
–
show
|
|||||
442 | } |
||||
443 | |||||
444 | $export_dir_slash = $modSettings['export_dir'] . DIRECTORY_SEPARATOR; |
||||
445 | |||||
446 | $idhash = hash_hmac('sha1', $uid, get_auth_secret()); |
||||
447 | $part = isset($_GET['part']) ? (int) $_GET['part'] : 1; |
||||
448 | $extension = $export_formats[$_GET['format']]['extension']; |
||||
449 | |||||
450 | $filepath = $export_dir_slash . $part . '_' . $idhash . '.' . $extension; |
||||
451 | $progressfile = $export_dir_slash . $idhash . '.' . $extension . '.progress.json'; |
||||
452 | |||||
453 | // Make sure they gave the correct authentication token. |
||||
454 | // We use these tokens so the user can download without logging in, as required by the GDPR. |
||||
455 | $dltoken = hash_hmac('sha1', $idhash, get_auth_secret()); |
||||
456 | if ($_GET['t'] !== $dltoken) |
||||
457 | { |
||||
458 | send_http_status(403); |
||||
459 | exit; |
||||
0 ignored issues
–
show
|
|||||
460 | } |
||||
461 | |||||
462 | // Obviously we can't give what we don't have. |
||||
463 | if (empty($modSettings['export_dir']) || !file_exists($filepath)) |
||||
464 | { |
||||
465 | send_http_status(404); |
||||
466 | exit; |
||||
0 ignored issues
–
show
|
|||||
467 | } |
||||
468 | |||||
469 | // Figure out the filename we'll tell the browser. |
||||
470 | $datatypes = file_exists($progressfile) ? array_keys($smcFunc['json_decode'](file_get_contents($progressfile), true)) : array('profile'); |
||||
471 | $included_desc = array_map( |
||||
472 | function ($datatype) use ($txt) |
||||
473 | { |
||||
474 | return $txt[$datatype]; |
||||
475 | }, |
||||
476 | $datatypes |
||||
477 | ); |
||||
478 | |||||
479 | $dlfilename = array_merge(array($context['forum_name'], $context['member']['username']), $included_desc); |
||||
480 | $dlfilename = preg_replace('/[^\p{L}\p{M}\p{N}_]+/u', '-', str_replace('"', '', un_htmlspecialchars(strip_tags(implode('_', $dlfilename))))); |
||||
481 | |||||
482 | $suffix = ($part > 1 || file_exists($export_dir_slash . '2_' . $idhash . '.' . $extension)) ? '_' . $part : ''; |
||||
483 | |||||
484 | $dlbasename = $dlfilename . $suffix . '.' . $extension; |
||||
485 | |||||
486 | $mtime = filemtime($filepath); |
||||
487 | $size = filesize($filepath); |
||||
488 | |||||
489 | // If it hasn't been modified since the last time it was retrieved, there's no need to serve it again. |
||||
490 | if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) |
||||
491 | { |
||||
492 | list($modified_since) = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE']); |
||||
493 | if (strtotime($modified_since) >= $mtime) |
||||
494 | { |
||||
495 | ob_end_clean(); |
||||
496 | header_remove('content-encoding'); |
||||
497 | |||||
498 | // Answer the question - no, it hasn't been modified ;). |
||||
499 | send_http_status(304); |
||||
500 | exit; |
||||
0 ignored issues
–
show
|
|||||
501 | } |
||||
502 | } |
||||
503 | |||||
504 | // Check whether the ETag was sent back, and cache based on that... |
||||
505 | $eTag = md5(implode(' ', array($dlbasename, $size, $mtime))); |
||||
506 | if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) && strpos($_SERVER['HTTP_IF_NONE_MATCH'], $eTag) !== false) |
||||
507 | { |
||||
508 | ob_end_clean(); |
||||
509 | header_remove('content-encoding'); |
||||
510 | |||||
511 | send_http_status(304); |
||||
512 | exit; |
||||
0 ignored issues
–
show
|
|||||
513 | } |
||||
514 | |||||
515 | // If this is a partial download, we need to determine what data range to send |
||||
516 | $range = 0; |
||||
517 | if (isset($_SERVER['HTTP_RANGE'])) |
||||
518 | { |
||||
519 | list($a, $range) = explode("=", $_SERVER['HTTP_RANGE'], 2); |
||||
520 | list($range) = explode(",", $range, 2); |
||||
521 | list($range, $range_end) = explode("-", $range); |
||||
522 | $range = intval($range); |
||||
523 | $range_end = !$range_end ? $size - 1 : intval($range_end); |
||||
524 | $new_length = $range_end - $range + 1; |
||||
525 | } |
||||
526 | |||||
527 | header('pragma: '); |
||||
528 | |||||
529 | if (!isBrowser('gecko')) |
||||
530 | header('content-transfer-encoding: binary'); |
||||
531 | |||||
532 | header('expires: ' . gmdate('D, d M Y H:i:s', time() + 525600 * 60) . ' GMT'); |
||||
533 | header('last-modified: ' . gmdate('D, d M Y H:i:s', $mtime) . ' GMT'); |
||||
534 | header('accept-ranges: bytes'); |
||||
535 | header('connection: close'); |
||||
536 | header('etag: ' . $eTag); |
||||
537 | header('content-type: ' . $export_formats[$_GET['format']]['mime']); |
||||
538 | |||||
539 | // Convert the file to UTF-8, cuz most browsers dig that. |
||||
540 | $utf8name = !$context['utf8'] && function_exists('iconv') ? iconv($context['character_set'], 'UTF-8', $dlbasename) : (!$context['utf8'] && function_exists('mb_convert_encoding') ? mb_convert_encoding($dlbasename, 'UTF-8', $context['character_set']) : $dlbasename); |
||||
541 | |||||
542 | // Different browsers like different standards... |
||||
543 | if (isBrowser('firefox')) |
||||
544 | header('content-disposition: attachment; filename*=UTF-8\'\'' . rawurlencode(preg_replace_callback('~&#(\d{3,8});~', 'fixchar__callback', $utf8name))); |
||||
545 | |||||
546 | elseif (isBrowser('opera')) |
||||
547 | header('content-disposition: attachment; filename="' . preg_replace_callback('~&#(\d{3,8});~', 'fixchar__callback', $utf8name) . '"'); |
||||
548 | |||||
549 | elseif (isBrowser('ie')) |
||||
550 | header('content-disposition: attachment; filename="' . urlencode(preg_replace_callback('~&#(\d{3,8});~', 'fixchar__callback', $utf8name)) . '"'); |
||||
551 | |||||
552 | else |
||||
553 | header('content-disposition: attachment; filename="' . $utf8name . '"'); |
||||
0 ignored issues
–
show
Are you sure
$utf8name of type array|string can be used in concatenation ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
554 | |||||
555 | header('cache-control: max-age=' . (525600 * 60) . ', private'); |
||||
556 | |||||
557 | // Multipart and resuming support |
||||
558 | if (isset($_SERVER['HTTP_RANGE'])) |
||||
559 | { |
||||
560 | send_http_status(206); |
||||
561 | header("content-length: $new_length"); |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
562 | header("content-range: bytes $range-$range_end/$size"); |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
563 | } |
||||
564 | else |
||||
565 | header("content-length: $size"); |
||||
566 | |||||
567 | // Try to buy some time... |
||||
568 | @set_time_limit(600); |
||||
569 | |||||
570 | // For multipart/resumable downloads, send the requested chunk(s) of the file |
||||
571 | if (isset($_SERVER['HTTP_RANGE'])) |
||||
572 | { |
||||
573 | while (@ob_get_level() > 0) |
||||
574 | @ob_end_clean(); |
||||
575 | |||||
576 | header_remove('content-encoding'); |
||||
577 | |||||
578 | // 40 kilobytes is a good-ish amount |
||||
579 | $chunksize = 40 * 1024; |
||||
580 | $bytes_sent = 0; |
||||
581 | |||||
582 | $fp = fopen($filepath, 'rb'); |
||||
583 | |||||
584 | fseek($fp, $range); |
||||
585 | |||||
586 | while (!feof($fp) && (!connection_aborted()) && ($bytes_sent < $new_length)) |
||||
587 | { |
||||
588 | $buffer = fread($fp, $chunksize); |
||||
589 | echo($buffer); |
||||
590 | flush(); |
||||
591 | $bytes_sent += strlen($buffer); |
||||
592 | } |
||||
593 | fclose($fp); |
||||
594 | } |
||||
595 | |||||
596 | // Since we don't do output compression for files this large... |
||||
597 | elseif ($size > 4194304) |
||||
598 | { |
||||
599 | // Forcibly end any output buffering going on. |
||||
600 | while (@ob_get_level() > 0) |
||||
601 | @ob_end_clean(); |
||||
602 | |||||
603 | header_remove('content-encoding'); |
||||
604 | |||||
605 | $fp = fopen($filepath, 'rb'); |
||||
606 | while (!feof($fp)) |
||||
607 | { |
||||
608 | echo fread($fp, 8192); |
||||
609 | flush(); |
||||
610 | } |
||||
611 | fclose($fp); |
||||
612 | } |
||||
613 | |||||
614 | // On some of the less-bright hosts, readfile() is disabled. It's just a faster, more byte safe, version of what's in the if. |
||||
615 | elseif (@readfile($filepath) === null) |
||||
616 | echo file_get_contents($filepath); |
||||
617 | |||||
618 | exit; |
||||
0 ignored issues
–
show
|
|||||
619 | } |
||||
620 | |||||
621 | /** |
||||
622 | * Allows a member to export their attachments. |
||||
623 | * Mostly just a wrapper for showAttachment() but with a few tweaks. |
||||
624 | * |
||||
625 | * @param int $uid The ID of the member whose data we're exporting. |
||||
626 | */ |
||||
627 | function export_attachment($uid) |
||||
628 | { |
||||
629 | global $sourcedir, $context, $smcFunc; |
||||
630 | |||||
631 | $idhash = hash_hmac('sha1', $uid, get_auth_secret()); |
||||
632 | $dltoken = hash_hmac('sha1', $idhash, get_auth_secret()); |
||||
633 | if (!isset($_GET['t']) || $_GET['t'] !== $dltoken) |
||||
634 | { |
||||
635 | send_http_status(403); |
||||
636 | exit; |
||||
0 ignored issues
–
show
|
|||||
637 | } |
||||
638 | |||||
639 | $attachId = isset($_REQUEST['attach']) ? (int) $_REQUEST['attach'] : 0; |
||||
640 | if (empty($attachId)) |
||||
641 | { |
||||
642 | send_http_status(404, 'File Not Found'); |
||||
643 | die('404 File Not Found'); |
||||
0 ignored issues
–
show
|
|||||
644 | } |
||||
645 | |||||
646 | // Does this attachment belong to this member? |
||||
647 | $request = $smcFunc['db_query']('', ' |
||||
648 | SELECT m.id_topic |
||||
649 | FROM {db_prefix}messages AS m |
||||
650 | INNER JOIN {db_prefix}attachments AS a ON (m.id_msg = a.id_msg) |
||||
651 | WHERE m.id_member = {int:uid} |
||||
652 | AND a.id_attach = {int:attachId}', |
||||
653 | array( |
||||
654 | 'uid' => $uid, |
||||
655 | 'attachId' => $attachId, |
||||
656 | ) |
||||
657 | ); |
||||
658 | if ($smcFunc['db_num_rows']($request) == 0) |
||||
659 | { |
||||
660 | $smcFunc['db_free_result']($request); |
||||
661 | send_http_status(403); |
||||
662 | exit; |
||||
0 ignored issues
–
show
|
|||||
663 | } |
||||
664 | $smcFunc['db_free_result']($request); |
||||
665 | |||||
666 | // This doesn't count as a normal download. |
||||
667 | $context['skip_downloads_increment'] = true; |
||||
668 | |||||
669 | // Try to avoid collisons when attachment names are not unique. |
||||
670 | $context['prepend_attachment_id'] = true; |
||||
671 | |||||
672 | // Allow access to their attachments even if they can't see the board. |
||||
673 | // This is just like what we do with posts during export. |
||||
674 | $context['attachment_allow_hidden_boards'] = true; |
||||
675 | |||||
676 | // We should now have what we need to serve the file. |
||||
677 | require_once($sourcedir . DIRECTORY_SEPARATOR . 'ShowAttachments.php'); |
||||
678 | showAttachment(); |
||||
679 | } |
||||
680 | |||||
681 | /** |
||||
682 | * Helper function that defines data export formats in a single location. |
||||
683 | * |
||||
684 | * @return array Information about supported data formats for profile exports. |
||||
685 | */ |
||||
686 | function get_export_formats() |
||||
687 | { |
||||
688 | global $txt; |
||||
689 | |||||
690 | $export_formats = array( |
||||
691 | 'XML_XSLT' => array( |
||||
692 | 'extension' => 'styled.xml', |
||||
693 | 'mime' => 'text/xml', |
||||
694 | 'description' => $txt['export_format_xml_xslt'], |
||||
695 | 'per_page' => 500, |
||||
696 | ), |
||||
697 | 'HTML' => array( |
||||
698 | 'extension' => 'html', |
||||
699 | 'mime' => 'text/html', |
||||
700 | 'description' => $txt['export_format_html'], |
||||
701 | 'per_page' => 500, |
||||
702 | ), |
||||
703 | 'XML' => array( |
||||
704 | 'extension' => 'xml', |
||||
705 | 'mime' => 'text/xml', |
||||
706 | 'description' => $txt['export_format_xml'], |
||||
707 | 'per_page' => 2000, |
||||
708 | ), |
||||
709 | // 'CSV' => array( |
||||
710 | // 'extension' => 'csv', |
||||
711 | // 'mime' => 'text/csv', |
||||
712 | // 'description' => $txt['export_format_csv'], |
||||
713 | // 'per_page' => 2000, |
||||
714 | // ), |
||||
715 | // 'JSON' => array( |
||||
716 | // 'extension' => 'json', |
||||
717 | // 'mime' => 'application/json', |
||||
718 | // 'description' => $txt['export_format_json'], |
||||
719 | // 'per_page' => 2000, |
||||
720 | // ), |
||||
721 | ); |
||||
722 | |||||
723 | // If these are missing, we can't transform the XML on the server. |
||||
724 | if (!class_exists('DOMDocument') || !class_exists('XSLTProcessor')) |
||||
725 | unset($export_formats['HTML']); |
||||
726 | |||||
727 | return $export_formats; |
||||
728 | } |
||||
729 | |||||
730 | /** |
||||
731 | * Returns the path to a secure directory for storing exported profile data. |
||||
732 | * |
||||
733 | * The directory is created if it does not yet exist, and is secured using the |
||||
734 | * same method that we use to secure attachment directories. Files in this |
||||
735 | * directory can only be downloaded via the download_export_file() function. |
||||
736 | * |
||||
737 | * @return string|bool The path to the directory, or false on error. |
||||
738 | */ |
||||
739 | function create_export_dir($fallback = '') |
||||
740 | { |
||||
741 | global $boarddir, $modSettings, $txt; |
||||
742 | |||||
743 | // No supplied fallback, so use the default location. |
||||
744 | if (empty($fallback)) |
||||
745 | $fallback = $boarddir . DIRECTORY_SEPARATOR . 'exports'; |
||||
746 | |||||
747 | // Automatically set it to the fallback if it is missing. |
||||
748 | if (empty($modSettings['export_dir'])) |
||||
749 | updateSettings(array('export_dir' => $fallback)); |
||||
750 | |||||
751 | // Make sure the directory exists. |
||||
752 | if (!file_exists($modSettings['export_dir'])) |
||||
753 | @mkdir($modSettings['export_dir'], null, true); |
||||
0 ignored issues
–
show
null of type null is incompatible with the type integer expected by parameter $permissions of mkdir() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
754 | |||||
755 | // Make sure the directory has the correct permissions. |
||||
756 | if (!is_dir($modSettings['export_dir']) || !smf_chmod($modSettings['export_dir'])) |
||||
757 | { |
||||
758 | loadLanguage('Errors'); |
||||
759 | |||||
760 | // Try again at the fallback location. |
||||
761 | if ($modSettings['export_dir'] != $fallback) |
||||
762 | { |
||||
763 | log_error(sprintf($txt['export_dir_forced_change'], $modSettings['export_dir'], $fallback)); |
||||
764 | updateSettings(array('export_dir' => $fallback)); |
||||
765 | |||||
766 | // Secondary fallback will be the default location, so no parameter this time. |
||||
767 | create_export_dir(); |
||||
768 | } |
||||
769 | // Uh-oh. Even the default location failed. |
||||
770 | else |
||||
771 | { |
||||
772 | log_error($txt['export_dir_not_writable']); |
||||
773 | return false; |
||||
774 | } |
||||
775 | } |
||||
776 | |||||
777 | return secureDirectory(array($modSettings['export_dir']), true); |
||||
778 | } |
||||
779 | |||||
780 | /** |
||||
781 | * Provides an XSLT stylesheet to transform an XML-based profile export file |
||||
782 | * into the desired output format. |
||||
783 | * |
||||
784 | * @param string $format The desired output format. Currently accepts 'HTML' and 'XML_XSLT'. |
||||
785 | * @param int $uid The ID of the member whose data we're exporting. |
||||
786 | * @return array The XSLT stylesheet and a (possibly empty) DTD to insert into the XML document. |
||||
787 | */ |
||||
788 | function get_xslt_stylesheet($format, $uid) |
||||
789 | { |
||||
790 | global $context, $txt, $settings, $modSettings, $sourcedir, $forum_copyright, $scripturl, $smcFunc; |
||||
791 | |||||
792 | static $xslts = array(); |
||||
793 | |||||
794 | $doctype = ''; |
||||
795 | $stylesheet = array(); |
||||
796 | $xslt_variables = array(); |
||||
797 | |||||
798 | // Do not change any of these to HTTPS URLs. For explanation, see comments in the buildXmlFeed() function. |
||||
799 | $smf_ns = 'htt'.'p:/'.'/ww'.'w.simple'.'machines.o'.'rg/xml/profile'; |
||||
800 | $xslt_ns = 'htt'.'p:/'.'/ww'.'w.w3.o'.'rg/1999/XSL/Transform'; |
||||
801 | $html_ns = 'htt'.'p:/'.'/ww'.'w.w3.o'.'rg/1999/xhtml'; |
||||
802 | |||||
803 | require_once($sourcedir . DIRECTORY_SEPARATOR . 'News.php'); |
||||
804 | |||||
805 | if (in_array($format, array('HTML', 'XML_XSLT'))) |
||||
806 | { |
||||
807 | if (!class_exists('DOMDocument') || !class_exists('XSLTProcessor')) |
||||
808 | $format = 'XML_XSLT'; |
||||
809 | |||||
810 | $export_formats = get_export_formats(); |
||||
811 | |||||
812 | /* Notes: |
||||
813 | * 1. The 'value' can be one of the following: |
||||
814 | * - an integer or string |
||||
815 | * - an XPath expression |
||||
816 | * - raw XML, which may or not not include other XSLT statements. |
||||
817 | * |
||||
818 | * 2. Always set 'no_cdata_parse' to true when the value is raw XML. |
||||
819 | * |
||||
820 | * 3. Set 'xpath' to true if the value is an XPath expression. When this |
||||
821 | * is true, the value will be placed in the 'select' attribute of the |
||||
822 | * <xsl:variable> element rather than in a child node. |
||||
823 | * |
||||
824 | * 4. Set 'param' to true in order to create an <xsl:param> instead |
||||
825 | * of an <xsl:variable>. |
||||
826 | * |
||||
827 | * A word to PHP coders: Do not let the term "variable" mislead you. |
||||
828 | * XSLT variables are roughly equivalent to PHP constants rather |
||||
829 | * than PHP variables; once the value has been set, it is immutable. |
||||
830 | * Keeping this in mind may spare you from some confusion and |
||||
831 | * frustration while working with XSLT. |
||||
832 | */ |
||||
833 | $xslt_variables = array( |
||||
834 | 'scripturl' => array( |
||||
835 | 'value' => $scripturl, |
||||
836 | ), |
||||
837 | 'themeurl' => array( |
||||
838 | 'value' => $settings['default_theme_url'], |
||||
839 | ), |
||||
840 | 'member_id' => array( |
||||
841 | 'value' => $uid, |
||||
842 | ), |
||||
843 | 'last_page' => array( |
||||
844 | 'param' => true, |
||||
845 | 'value' => !empty($context['export_last_page']) ? $context['export_last_page'] : 1, |
||||
846 | 'xpath' => true, |
||||
847 | ), |
||||
848 | 'dlfilename' => array( |
||||
849 | 'param' => true, |
||||
850 | 'value' => !empty($context['export_dlfilename']) ? $context['export_dlfilename'] : '', |
||||
851 | ), |
||||
852 | 'ext' => array( |
||||
853 | 'value' => $export_formats[$format]['extension'], |
||||
854 | ), |
||||
855 | 'forum_copyright' => array( |
||||
856 | 'value' => sprintf($forum_copyright, SMF_FULL_VERSION, SMF_SOFTWARE_YEAR, $scripturl), |
||||
857 | ), |
||||
858 | 'txt_summary_heading' => array( |
||||
859 | 'value' => $txt['summary'], |
||||
860 | ), |
||||
861 | 'txt_posts_heading' => array( |
||||
862 | 'value' => $txt['posts'], |
||||
863 | ), |
||||
864 | 'txt_personal_messages_heading' => array( |
||||
865 | 'value' => $txt['personal_messages'], |
||||
866 | ), |
||||
867 | 'txt_view_source_button' => array( |
||||
868 | 'value' => $txt['export_view_source_button'], |
||||
869 | ), |
||||
870 | 'txt_download_original' => array( |
||||
871 | 'value' => $txt['export_download_original'], |
||||
872 | ), |
||||
873 | 'txt_help' => array( |
||||
874 | 'value' => $txt['help'], |
||||
875 | ), |
||||
876 | 'txt_terms_rules' => array( |
||||
877 | 'value' => $txt['terms_and_rules'], |
||||
878 | ), |
||||
879 | 'txt_go_up' => array( |
||||
880 | 'value' => $txt['go_up'], |
||||
881 | ), |
||||
882 | 'txt_pages' => array( |
||||
883 | 'value' => $txt['pages'], |
||||
884 | ), |
||||
885 | ); |
||||
886 | |||||
887 | // Let mods adjust the XSLT variables. |
||||
888 | call_integration_hook('integrate_export_xslt_variables', array(&$xslt_variables, $format)); |
||||
889 | |||||
890 | $idhash = hash_hmac('sha1', $uid, get_auth_secret()); |
||||
891 | $xslt_variables['dltoken'] = array( |
||||
892 | 'value' => hash_hmac('sha1', $idhash, get_auth_secret()) |
||||
893 | ); |
||||
894 | |||||
895 | // Efficiency = good. |
||||
896 | $xslt_key = $smcFunc['json_encode'](array($format, $uid, $xslt_variables)); |
||||
897 | if (isset($xslts[$xslt_key])) |
||||
898 | return $xslts[$xslt_key]; |
||||
899 | |||||
900 | if ($format == 'XML_XSLT') |
||||
901 | { |
||||
902 | $doctype = implode("\n", array( |
||||
903 | '<!--', |
||||
904 | "\t" . $txt['export_open_in_browser'], |
||||
905 | '-->', |
||||
906 | '<?xml-stylesheet type="text/xsl" href="#stylesheet"?>', |
||||
907 | '<!DOCTYPE smf:xml-feed [', |
||||
908 | '<!ATTLIST xsl:stylesheet', |
||||
909 | 'id ID #REQUIRED>', |
||||
910 | ']>', |
||||
911 | )); |
||||
912 | |||||
913 | $stylesheet['header'] = "\n" . implode("\n", array( |
||||
914 | '', |
||||
915 | "\t" . '<xsl:stylesheet version="1.0" xmlns:xsl="' . $xslt_ns . '" xmlns:html="' . $html_ns . '" xmlns:smf="' . $smf_ns . '" exclude-result-prefixes="smf html" id="stylesheet">', |
||||
916 | '', |
||||
917 | "\t\t" . '<xsl:template match="xsl:stylesheet"/>', |
||||
918 | "\t\t" . '<xsl:template match="xsl:stylesheet" mode="detailedinfo"/>', |
||||
919 | )); |
||||
920 | } |
||||
921 | else |
||||
922 | { |
||||
923 | $doctype = ''; |
||||
924 | $stylesheet['header'] = implode("\n", array( |
||||
925 | '<?xml version="1.0" encoding="' . $context['character_set'] . '"?' . '>', |
||||
926 | '<xsl:stylesheet version="1.0" xmlns:xsl="' . $xslt_ns . '" xmlns:html="' . $html_ns . '" xmlns:smf="' . $smf_ns . '" exclude-result-prefixes="smf html">', |
||||
927 | )); |
||||
928 | } |
||||
929 | |||||
930 | // Output control settings. |
||||
931 | $stylesheet['output_control'] = ' |
||||
932 | <xsl:output method="html" encoding="utf-8" indent="yes"/> |
||||
933 | <xsl:strip-space elements="*"/>'; |
||||
934 | |||||
935 | // Insert the XSLT variables. |
||||
936 | $stylesheet['variables'] = ''; |
||||
937 | |||||
938 | foreach ($xslt_variables as $name => $var) |
||||
939 | { |
||||
940 | $element = !empty($var['param']) ? 'param' : 'variable'; |
||||
941 | |||||
942 | $stylesheet['variables'] .= "\n\t\t" . '<xsl:' . $element . ' name="' . $name . '"'; |
||||
943 | |||||
944 | if (isset($var['xpath'])) |
||||
945 | $stylesheet['variables'] .= ' select="' . $var['value'] . '"/>'; |
||||
946 | else |
||||
947 | $stylesheet['variables'] .= '>' . (!empty($var['no_cdata_parse']) ? $var['value'] : cdata_parse($var['value'])) . '</xsl:' . $element . '>'; |
||||
948 | } |
||||
949 | |||||
950 | // The top-level template. Creates the shell of the HTML document. |
||||
951 | $stylesheet['html'] = ' |
||||
952 | <xsl:template match="/*"> |
||||
953 | <xsl:text disable-output-escaping="yes"><!DOCTYPE html></xsl:text> |
||||
954 | <html> |
||||
955 | <head> |
||||
956 | <title> |
||||
957 | <xsl:value-of select="@title"/> |
||||
958 | </title> |
||||
959 | <xsl:call-template name="css_js"/> |
||||
960 | </head> |
||||
961 | <body> |
||||
962 | <div id="footerfix"> |
||||
963 | <div id="header"> |
||||
964 | <h1 class="forumtitle"> |
||||
965 | <a id="top"> |
||||
966 | <xsl:attribute name="href"> |
||||
967 | <xsl:value-of select="$scripturl"/> |
||||
968 | </xsl:attribute> |
||||
969 | <xsl:value-of select="@forum-name"/> |
||||
970 | </a> |
||||
971 | </h1> |
||||
972 | </div> |
||||
973 | <div id="wrapper"> |
||||
974 | <div id="upper_section"> |
||||
975 | <div id="inner_section"> |
||||
976 | <div id="inner_wrap"> |
||||
977 | <div class="user"> |
||||
978 | <time> |
||||
979 | <xsl:attribute name="datetime"> |
||||
980 | <xsl:value-of select="@generated-date-UTC"/> |
||||
981 | </xsl:attribute> |
||||
982 | <xsl:value-of select="@generated-date-localized"/> |
||||
983 | </time> |
||||
984 | </div> |
||||
985 | <hr class="clear"/> |
||||
986 | </div> |
||||
987 | </div> |
||||
988 | </div> |
||||
989 | |||||
990 | <xsl:call-template name="content_section"/> |
||||
991 | |||||
992 | </div> |
||||
993 | </div> |
||||
994 | <div id="footer"> |
||||
995 | <div class="inner_wrap"> |
||||
996 | <ul> |
||||
997 | <li class="floatright"> |
||||
998 | <a> |
||||
999 | <xsl:attribute name="href"> |
||||
1000 | <xsl:value-of select="concat($scripturl, \'?action=help\')"/> |
||||
1001 | </xsl:attribute> |
||||
1002 | <xsl:value-of select="$txt_help"/> |
||||
1003 | </a> |
||||
1004 | <xsl:text> | </xsl:text> |
||||
1005 | <a> |
||||
1006 | <xsl:attribute name="href"> |
||||
1007 | <xsl:value-of select="concat($scripturl, \'?action=help;sa=rules\')"/> |
||||
1008 | </xsl:attribute> |
||||
1009 | <xsl:value-of select="$txt_terms_rules"/> |
||||
1010 | </a> |
||||
1011 | <xsl:text> | </xsl:text> |
||||
1012 | <a href="#top"> |
||||
1013 | <xsl:value-of select="$txt_go_up"/> |
||||
1014 | <xsl:text> ▲</xsl:text> |
||||
1015 | </a> |
||||
1016 | </li> |
||||
1017 | <li class="copyright"> |
||||
1018 | <xsl:value-of select="$forum_copyright" disable-output-escaping="yes"/> |
||||
1019 | </li> |
||||
1020 | </ul> |
||||
1021 | </div> |
||||
1022 | </div> |
||||
1023 | </body> |
||||
1024 | </html> |
||||
1025 | </xsl:template>'; |
||||
1026 | |||||
1027 | // Template to show the content of the export file. |
||||
1028 | $stylesheet['content_section'] = ' |
||||
1029 | <xsl:template name="content_section"> |
||||
1030 | <div id="content_section"> |
||||
1031 | <div id="main_content_section"> |
||||
1032 | |||||
1033 | <div class="cat_bar"> |
||||
1034 | <h3 class="catbg"> |
||||
1035 | <xsl:value-of select="@title"/> |
||||
1036 | </h3> |
||||
1037 | </div> |
||||
1038 | <div class="information"> |
||||
1039 | <h2 class="display_title"> |
||||
1040 | <xsl:value-of select="@description"/> |
||||
1041 | </h2> |
||||
1042 | </div> |
||||
1043 | |||||
1044 | <xsl:if test="username"> |
||||
1045 | <div class="cat_bar"> |
||||
1046 | <h3 class="catbg"> |
||||
1047 | <xsl:value-of select="$txt_summary_heading"/> |
||||
1048 | </h3> |
||||
1049 | </div> |
||||
1050 | <div id="profileview" class="roundframe flow_auto noup"> |
||||
1051 | <xsl:call-template name="summary"/> |
||||
1052 | </div> |
||||
1053 | </xsl:if> |
||||
1054 | |||||
1055 | <xsl:call-template name="page_index"/> |
||||
1056 | |||||
1057 | <xsl:if test="member_post"> |
||||
1058 | <div class="cat_bar"> |
||||
1059 | <h3 class="catbg"> |
||||
1060 | <xsl:value-of select="$txt_posts_heading"/> |
||||
1061 | </h3> |
||||
1062 | </div> |
||||
1063 | <div id="posts" class="roundframe flow_auto noup"> |
||||
1064 | <xsl:apply-templates select="member_post" mode="posts"/> |
||||
1065 | </div> |
||||
1066 | </xsl:if> |
||||
1067 | |||||
1068 | <xsl:if test="personal_message"> |
||||
1069 | <div class="cat_bar"> |
||||
1070 | <h3 class="catbg"> |
||||
1071 | <xsl:value-of select="$txt_personal_messages_heading"/> |
||||
1072 | </h3> |
||||
1073 | </div> |
||||
1074 | <div id="personal_messages" class="roundframe flow_auto noup"> |
||||
1075 | <xsl:apply-templates select="personal_message" mode="pms"/> |
||||
1076 | </div> |
||||
1077 | </xsl:if> |
||||
1078 | |||||
1079 | <xsl:call-template name="page_index"/> |
||||
1080 | |||||
1081 | </div> |
||||
1082 | </div> |
||||
1083 | </xsl:template>'; |
||||
1084 | |||||
1085 | // Template for user profile summary |
||||
1086 | $stylesheet['summary'] = ' |
||||
1087 | <xsl:template name="summary"> |
||||
1088 | <div id="basicinfo"> |
||||
1089 | <div class="username clear"> |
||||
1090 | <h4> |
||||
1091 | <a> |
||||
1092 | <xsl:attribute name="href"> |
||||
1093 | <xsl:value-of select="link"/> |
||||
1094 | </xsl:attribute> |
||||
1095 | <xsl:value-of select="name"/> |
||||
1096 | </a> |
||||
1097 | <xsl:text> </xsl:text> |
||||
1098 | <span class="position"> |
||||
1099 | <xsl:choose> |
||||
1100 | <xsl:when test="position"> |
||||
1101 | <xsl:value-of select="position"/> |
||||
1102 | </xsl:when> |
||||
1103 | <xsl:otherwise> |
||||
1104 | <xsl:value-of select="post_group"/> |
||||
1105 | </xsl:otherwise> |
||||
1106 | </xsl:choose> |
||||
1107 | </span> |
||||
1108 | </h4> |
||||
1109 | </div> |
||||
1110 | <img class="avatar"> |
||||
1111 | <xsl:attribute name="src"> |
||||
1112 | <xsl:value-of select="avatar"/> |
||||
1113 | </xsl:attribute> |
||||
1114 | </img> |
||||
1115 | </div> |
||||
1116 | |||||
1117 | <div id="detailedinfo"> |
||||
1118 | <dl class="settings noborder"> |
||||
1119 | <xsl:apply-templates mode="detailedinfo"/> |
||||
1120 | </dl> |
||||
1121 | </div> |
||||
1122 | </xsl:template>'; |
||||
1123 | |||||
1124 | // Some helper templates for details inside the summary. |
||||
1125 | $stylesheet['detail_default'] = ' |
||||
1126 | <xsl:template match="*" mode="detailedinfo"> |
||||
1127 | <dt> |
||||
1128 | <xsl:value-of select="concat(@label, \':\')"/> |
||||
1129 | </dt> |
||||
1130 | <dd> |
||||
1131 | <xsl:value-of select="." disable-output-escaping="yes"/> |
||||
1132 | </dd> |
||||
1133 | </xsl:template>'; |
||||
1134 | |||||
1135 | $stylesheet['detail_email'] = ' |
||||
1136 | <xsl:template match="email" mode="detailedinfo"> |
||||
1137 | <dt> |
||||
1138 | <xsl:value-of select="concat(@label, \':\')"/> |
||||
1139 | </dt> |
||||
1140 | <dd> |
||||
1141 | <a> |
||||
1142 | <xsl:attribute name="href"> |
||||
1143 | <xsl:text>mailto:</xsl:text> |
||||
1144 | <xsl:value-of select="."/> |
||||
1145 | </xsl:attribute> |
||||
1146 | <xsl:value-of select="."/> |
||||
1147 | </a> |
||||
1148 | </dd> |
||||
1149 | </xsl:template>'; |
||||
1150 | |||||
1151 | $stylesheet['detail_website'] = ' |
||||
1152 | <xsl:template match="website" mode="detailedinfo"> |
||||
1153 | <dt> |
||||
1154 | <xsl:value-of select="concat(@label, \':\')"/> |
||||
1155 | </dt> |
||||
1156 | <dd> |
||||
1157 | <a> |
||||
1158 | <xsl:attribute name="href"> |
||||
1159 | <xsl:value-of select="link"/> |
||||
1160 | </xsl:attribute> |
||||
1161 | <xsl:value-of select="title"/> |
||||
1162 | </a> |
||||
1163 | </dd> |
||||
1164 | </xsl:template>'; |
||||
1165 | |||||
1166 | $stylesheet['detail_ip'] = ' |
||||
1167 | <xsl:template match="ip_addresses" mode="detailedinfo"> |
||||
1168 | <dt> |
||||
1169 | <xsl:value-of select="concat(@label, \':\')"/> |
||||
1170 | </dt> |
||||
1171 | <dd> |
||||
1172 | <ul class="nolist"> |
||||
1173 | <xsl:apply-templates mode="ip_address"/> |
||||
1174 | </ul> |
||||
1175 | </dd> |
||||
1176 | </xsl:template> |
||||
1177 | <xsl:template match="*" mode="ip_address"> |
||||
1178 | <li> |
||||
1179 | <xsl:value-of select="."/> |
||||
1180 | <xsl:if test="@label and following-sibling"> |
||||
1181 | <xsl:text> </xsl:text> |
||||
1182 | <span>(<xsl:value-of select="@label"/>)</span> |
||||
1183 | </xsl:if> |
||||
1184 | </li> |
||||
1185 | </xsl:template>'; |
||||
1186 | |||||
1187 | $stylesheet['detail_not_included'] = ' |
||||
1188 | <xsl:template match="name|link|avatar|online|member_post|personal_message" mode="detailedinfo"/>'; |
||||
1189 | |||||
1190 | // Template for printing a single post |
||||
1191 | $stylesheet['member_post'] = ' |
||||
1192 | <xsl:template match="member_post" mode="posts"> |
||||
1193 | <div> |
||||
1194 | <xsl:attribute name="id"> |
||||
1195 | <xsl:value-of select="concat(\'member_post_\', id)"/> |
||||
1196 | </xsl:attribute> |
||||
1197 | <xsl:attribute name="class"> |
||||
1198 | <xsl:choose> |
||||
1199 | <xsl:when test="approval_status = 1"> |
||||
1200 | <xsl:text>windowbg</xsl:text> |
||||
1201 | </xsl:when> |
||||
1202 | <xsl:otherwise> |
||||
1203 | <xsl:text>approvebg</xsl:text> |
||||
1204 | </xsl:otherwise> |
||||
1205 | </xsl:choose> |
||||
1206 | </xsl:attribute> |
||||
1207 | |||||
1208 | <div class="post_wrapper"> |
||||
1209 | <div class="poster"> |
||||
1210 | <h4> |
||||
1211 | <a> |
||||
1212 | <xsl:attribute name="href"> |
||||
1213 | <xsl:value-of select="poster/link"/> |
||||
1214 | </xsl:attribute> |
||||
1215 | <xsl:value-of select="poster/name"/> |
||||
1216 | </a> |
||||
1217 | </h4> |
||||
1218 | <ul class="user_info"> |
||||
1219 | <xsl:if test="poster/id = $member_id"> |
||||
1220 | <xsl:call-template name="own_user_info"/> |
||||
1221 | </xsl:if> |
||||
1222 | <li> |
||||
1223 | <xsl:value-of select="poster/email"/> |
||||
1224 | </li> |
||||
1225 | <li class="poster_ip"> |
||||
1226 | <xsl:value-of select="concat(poster/ip/@label, \': \')"/> |
||||
1227 | <xsl:value-of select="poster/ip"/> |
||||
1228 | </li> |
||||
1229 | </ul> |
||||
1230 | </div> |
||||
1231 | |||||
1232 | <div class="postarea"> |
||||
1233 | <div class="flow_hidden"> |
||||
1234 | |||||
1235 | <div class="keyinfo"> |
||||
1236 | <h5> |
||||
1237 | <strong> |
||||
1238 | <a> |
||||
1239 | <xsl:attribute name="href"> |
||||
1240 | <xsl:value-of select="board/link"/> |
||||
1241 | </xsl:attribute> |
||||
1242 | <xsl:value-of select="board/name"/> |
||||
1243 | </a> |
||||
1244 | <xsl:text> / </xsl:text> |
||||
1245 | <a> |
||||
1246 | <xsl:attribute name="href"> |
||||
1247 | <xsl:value-of select="link"/> |
||||
1248 | </xsl:attribute> |
||||
1249 | <xsl:value-of select="subject"/> |
||||
1250 | </a> |
||||
1251 | </strong> |
||||
1252 | </h5> |
||||
1253 | <span class="smalltext"><xsl:value-of select="time"/></span> |
||||
1254 | <xsl:if test="modified_time"> |
||||
1255 | <span class="smalltext modified floatright mvisible em"> |
||||
1256 | <xsl:attribute name="id"> |
||||
1257 | <xsl:value-of select="concat(\'modified_\', id)"/> |
||||
1258 | </xsl:attribute> |
||||
1259 | <span class="lastedit"> |
||||
1260 | <xsl:value-of select="modified_time/@label"/> |
||||
1261 | </span> |
||||
1262 | <xsl:text>: </xsl:text> |
||||
1263 | <xsl:value-of select="modified_time"/> |
||||
1264 | <xsl:text>. </xsl:text> |
||||
1265 | <xsl:value-of select="modified_by/@label"/> |
||||
1266 | <xsl:text>: </xsl:text> |
||||
1267 | <xsl:value-of select="modified_by"/> |
||||
1268 | <xsl:text>. </xsl:text> |
||||
1269 | </span> |
||||
1270 | </xsl:if> |
||||
1271 | </div> |
||||
1272 | |||||
1273 | <div class="post"> |
||||
1274 | <div class="inner"> |
||||
1275 | <xsl:value-of select="body_html" disable-output-escaping="yes"/> |
||||
1276 | </div> |
||||
1277 | <div class="inner monospace" style="display:none;"> |
||||
1278 | <xsl:choose> |
||||
1279 | <xsl:when test="contains(body/text(), \'[html]\')"> |
||||
1280 | <xsl:call-template name="bbc_html_splitter"> |
||||
1281 | <xsl:with-param name="bbc_string" select="body/text()"/> |
||||
1282 | </xsl:call-template> |
||||
1283 | </xsl:when> |
||||
1284 | <xsl:otherwise> |
||||
1285 | <xsl:value-of select="body" disable-output-escaping="yes"/> |
||||
1286 | </xsl:otherwise> |
||||
1287 | </xsl:choose> |
||||
1288 | </div> |
||||
1289 | </div> |
||||
1290 | |||||
1291 | <xsl:apply-templates select="attachments"> |
||||
1292 | <xsl:with-param name="post_id" select="id"/> |
||||
1293 | </xsl:apply-templates> |
||||
1294 | |||||
1295 | <div class="under_message"> |
||||
1296 | <ul class="floatleft"> |
||||
1297 | <xsl:if test="likes > 0"> |
||||
1298 | <li class="smflikebutton"> |
||||
1299 | <xsl:attribute name="id"> |
||||
1300 | <xsl:value-of select="concat(\'msg_\', id, \'_likes\')"/> |
||||
1301 | </xsl:attribute> |
||||
1302 | <span><span class="main_icons like"></span> <xsl:value-of select="likes"/></span> |
||||
1303 | </li> |
||||
1304 | </xsl:if> |
||||
1305 | </ul> |
||||
1306 | <xsl:call-template name="quickbuttons"> |
||||
1307 | <xsl:with-param name="toggle_target" select="concat(\'member_post_\', id)"/> |
||||
1308 | </xsl:call-template> |
||||
1309 | </div> |
||||
1310 | |||||
1311 | </div> |
||||
1312 | </div> |
||||
1313 | |||||
1314 | <div class="moderatorbar"> |
||||
1315 | <xsl:if test="poster/id = $member_id"> |
||||
1316 | <xsl:call-template name="signature"/> |
||||
1317 | </xsl:if> |
||||
1318 | </div> |
||||
1319 | |||||
1320 | </div> |
||||
1321 | </div> |
||||
1322 | </xsl:template>'; |
||||
1323 | |||||
1324 | // Template for printing a single PM |
||||
1325 | $stylesheet['personal_message'] = ' |
||||
1326 | <xsl:template match="personal_message" mode="pms"> |
||||
1327 | <div class="windowbg"> |
||||
1328 | <xsl:attribute name="id"> |
||||
1329 | <xsl:value-of select="concat(\'personal_message_\', id)"/> |
||||
1330 | </xsl:attribute> |
||||
1331 | |||||
1332 | <div class="post_wrapper"> |
||||
1333 | <div class="poster"> |
||||
1334 | <h4> |
||||
1335 | <a> |
||||
1336 | <xsl:attribute name="href"> |
||||
1337 | <xsl:value-of select="sender/link"/> |
||||
1338 | </xsl:attribute> |
||||
1339 | <xsl:value-of select="sender/name"/> |
||||
1340 | </a> |
||||
1341 | </h4> |
||||
1342 | <ul class="user_info"> |
||||
1343 | <xsl:if test="sender/id = $member_id"> |
||||
1344 | <xsl:call-template name="own_user_info"/> |
||||
1345 | </xsl:if> |
||||
1346 | </ul> |
||||
1347 | </div> |
||||
1348 | |||||
1349 | <div class="postarea"> |
||||
1350 | <div class="flow_hidden"> |
||||
1351 | |||||
1352 | <div class="keyinfo"> |
||||
1353 | <h5> |
||||
1354 | <xsl:attribute name="id"> |
||||
1355 | <xsl:value-of select="concat(\'subject_\', id)"/> |
||||
1356 | </xsl:attribute> |
||||
1357 | <xsl:value-of select="subject"/> |
||||
1358 | </h5> |
||||
1359 | <span class="smalltext"> |
||||
1360 | <strong> |
||||
1361 | <xsl:value-of select="concat(recipient[1]/@label, \': \')"/> |
||||
1362 | </strong> |
||||
1363 | <xsl:apply-templates select="recipient"/> |
||||
1364 | </span> |
||||
1365 | <br/> |
||||
1366 | <span class="smalltext"> |
||||
1367 | <strong> |
||||
1368 | <xsl:value-of select="concat(sent_date/@label, \': \')"/> |
||||
1369 | </strong> |
||||
1370 | <time> |
||||
1371 | <xsl:attribute name="datetime"> |
||||
1372 | <xsl:value-of select="sent_date/@UTC"/> |
||||
1373 | </xsl:attribute> |
||||
1374 | <xsl:value-of select="normalize-space(sent_date)"/> |
||||
1375 | </time> |
||||
1376 | </span> |
||||
1377 | </div> |
||||
1378 | |||||
1379 | <div class="post"> |
||||
1380 | <div class="inner"> |
||||
1381 | <xsl:value-of select="body_html" disable-output-escaping="yes"/> |
||||
1382 | </div> |
||||
1383 | <div class="inner monospace" style="display:none;"> |
||||
1384 | <xsl:call-template name="bbc_html_splitter"> |
||||
1385 | <xsl:with-param name="bbc_string" select="body/text()"/> |
||||
1386 | </xsl:call-template> |
||||
1387 | </div> |
||||
1388 | </div> |
||||
1389 | |||||
1390 | <div class="under_message"> |
||||
1391 | <xsl:call-template name="quickbuttons"> |
||||
1392 | <xsl:with-param name="toggle_target" select="concat(\'personal_message_\', id)"/> |
||||
1393 | </xsl:call-template> |
||||
1394 | </div> |
||||
1395 | |||||
1396 | </div> |
||||
1397 | </div> |
||||
1398 | |||||
1399 | <div class="moderatorbar"> |
||||
1400 | <xsl:if test="sender/id = $member_id"> |
||||
1401 | <xsl:call-template name="signature"/> |
||||
1402 | </xsl:if> |
||||
1403 | </div> |
||||
1404 | |||||
1405 | </div> |
||||
1406 | </div> |
||||
1407 | </xsl:template>'; |
||||
1408 | |||||
1409 | // A couple of templates to handle attachments |
||||
1410 | $stylesheet['attachments'] = ' |
||||
1411 | <xsl:template match="attachments"> |
||||
1412 | <xsl:param name="post_id"/> |
||||
1413 | <xsl:if test="attachment"> |
||||
1414 | <div class="attachments"> |
||||
1415 | <xsl:attribute name="id"> |
||||
1416 | <xsl:value-of select="concat(\'msg_\', $post_id, \'_footer\')"/> |
||||
1417 | </xsl:attribute> |
||||
1418 | <xsl:apply-templates/> |
||||
1419 | </div> |
||||
1420 | </xsl:if> |
||||
1421 | </xsl:template> |
||||
1422 | <xsl:template match="attachment"> |
||||
1423 | <div class="attached"> |
||||
1424 | <div class="attachments_bot"> |
||||
1425 | <a> |
||||
1426 | <xsl:attribute name="href"> |
||||
1427 | <xsl:value-of select="concat(id, \' - \', name)"/> |
||||
1428 | </xsl:attribute> |
||||
1429 | <img class="centericon" alt="*"> |
||||
1430 | <xsl:attribute name="src"> |
||||
1431 | <xsl:value-of select="concat($themeurl, \'/images/icons/clip.png\')"/> |
||||
1432 | </xsl:attribute> |
||||
1433 | </img> |
||||
1434 | <xsl:text> </xsl:text> |
||||
1435 | <xsl:value-of select="name"/> |
||||
1436 | </a> |
||||
1437 | <br/> |
||||
1438 | <xsl:text>(</xsl:text> |
||||
1439 | <a class="bbc_link"> |
||||
1440 | <xsl:attribute name="href"> |
||||
1441 | <xsl:value-of select="concat($scripturl, \'?action=profile;area=dlattach;u=\', $member_id, \';attach=\', id, \';t=\', $dltoken)"/> |
||||
1442 | </xsl:attribute> |
||||
1443 | <xsl:value-of select="$txt_download_original"/> |
||||
1444 | </a> |
||||
1445 | <xsl:text>)</xsl:text> |
||||
1446 | <br/> |
||||
1447 | <xsl:value-of select="size/@label"/> |
||||
1448 | <xsl:text>: </xsl:text> |
||||
1449 | <xsl:value-of select="size"/> |
||||
1450 | <br/> |
||||
1451 | <xsl:value-of select="downloads/@label"/> |
||||
1452 | <xsl:text>: </xsl:text> |
||||
1453 | <xsl:value-of select="downloads"/> |
||||
1454 | </div> |
||||
1455 | </div> |
||||
1456 | </xsl:template>'; |
||||
1457 | |||||
1458 | // Helper template for printing the user's own info next to the post or personal message. |
||||
1459 | $stylesheet['own_user_info'] = ' |
||||
1460 | <xsl:template name="own_user_info"> |
||||
1461 | <xsl:if test="/*/avatar"> |
||||
1462 | <li class="avatar"> |
||||
1463 | <a> |
||||
1464 | <xsl:attribute name="href"> |
||||
1465 | <xsl:value-of select="/*/link"/> |
||||
1466 | </xsl:attribute> |
||||
1467 | <img class="avatar"> |
||||
1468 | <xsl:attribute name="src"> |
||||
1469 | <xsl:value-of select="/*/avatar"/> |
||||
1470 | </xsl:attribute> |
||||
1471 | </img> |
||||
1472 | </a> |
||||
1473 | </li> |
||||
1474 | </xsl:if> |
||||
1475 | <li class="membergroup"> |
||||
1476 | <xsl:value-of select="/*/position"/> |
||||
1477 | </li> |
||||
1478 | <xsl:if test="/*/title"> |
||||
1479 | <li class="title"> |
||||
1480 | <xsl:value-of select="/*/title"/> |
||||
1481 | </li> |
||||
1482 | </xsl:if> |
||||
1483 | <li class="postgroup"> |
||||
1484 | <xsl:value-of select="/*/post_group"/> |
||||
1485 | </li> |
||||
1486 | <li class="postcount"> |
||||
1487 | <xsl:value-of select="concat(/*/posts/@label, \': \')"/> |
||||
1488 | <xsl:value-of select="/*/posts"/> |
||||
1489 | </li> |
||||
1490 | <xsl:if test="/*/blurb"> |
||||
1491 | <li class="blurb"> |
||||
1492 | <xsl:value-of select="/*/blurb"/> |
||||
1493 | </li> |
||||
1494 | </xsl:if> |
||||
1495 | </xsl:template>'; |
||||
1496 | |||||
1497 | // Helper template for printing the quickbuttons |
||||
1498 | $stylesheet['quickbuttons'] = ' |
||||
1499 | <xsl:template name="quickbuttons"> |
||||
1500 | <xsl:param name="toggle_target"/> |
||||
1501 | <ul class="quickbuttons quickbuttons_post sf-js-enabled sf-arrows" style="touch-action: pan-y;"> |
||||
1502 | <li> |
||||
1503 | <a> |
||||
1504 | <xsl:attribute name="onclick"> |
||||
1505 | <xsl:text>$(\'#</xsl:text> |
||||
1506 | <xsl:value-of select="$toggle_target"/> |
||||
1507 | <xsl:text> .inner\').toggle();</xsl:text> |
||||
1508 | </xsl:attribute> |
||||
1509 | <xsl:value-of select="$txt_view_source_button"/> |
||||
1510 | </a> |
||||
1511 | </li> |
||||
1512 | </ul> |
||||
1513 | </xsl:template>'; |
||||
1514 | |||||
1515 | // Helper template for printing a signature |
||||
1516 | $stylesheet['signature'] = ' |
||||
1517 | <xsl:template name="signature"> |
||||
1518 | <xsl:if test="/*/signature"> |
||||
1519 | <div class="signature"> |
||||
1520 | <xsl:value-of select="/*/signature" disable-output-escaping="yes"/> |
||||
1521 | </div> |
||||
1522 | </xsl:if> |
||||
1523 | </xsl:template>'; |
||||
1524 | |||||
1525 | // Helper template for printing a list of PM recipients |
||||
1526 | $stylesheet['recipient'] = ' |
||||
1527 | <xsl:template match="recipient"> |
||||
1528 | <a> |
||||
1529 | <xsl:attribute name="href"> |
||||
1530 | <xsl:value-of select="link"/> |
||||
1531 | </xsl:attribute> |
||||
1532 | <xsl:value-of select="name"/> |
||||
1533 | </a> |
||||
1534 | <xsl:choose> |
||||
1535 | <xsl:when test="following-sibling::recipient"> |
||||
1536 | <xsl:text>, </xsl:text> |
||||
1537 | </xsl:when> |
||||
1538 | <xsl:otherwise> |
||||
1539 | <xsl:text>. </xsl:text> |
||||
1540 | </xsl:otherwise> |
||||
1541 | </xsl:choose> |
||||
1542 | </xsl:template>'; |
||||
1543 | |||||
1544 | // Helper template for special handling of the contents of the [html] BBCode |
||||
1545 | $stylesheet['bbc_html'] = ' |
||||
1546 | <xsl:template name="bbc_html_splitter"> |
||||
1547 | <xsl:param name="bbc_string"/> |
||||
1548 | <xsl:param name="inside_outside" select="outside"/> |
||||
1549 | <xsl:choose> |
||||
1550 | <xsl:when test="$inside_outside = \'outside\'"> |
||||
1551 | <xsl:choose> |
||||
1552 | <xsl:when test="contains($bbc_string, \'[html]\')"> |
||||
1553 | <xsl:variable name="following_string"> |
||||
1554 | <xsl:value-of select="substring-after($bbc_string, \'[html]\')" disable-output-escaping="yes"/> |
||||
1555 | </xsl:variable> |
||||
1556 | <xsl:value-of select="substring-before($bbc_string, \'[html]\')" disable-output-escaping="yes"/> |
||||
1557 | <xsl:text>[html]</xsl:text> |
||||
1558 | <xsl:call-template name="bbc_html_splitter"> |
||||
1559 | <xsl:with-param name="bbc_string" select="$following_string"/> |
||||
1560 | <xsl:with-param name="inside_outside" select="inside"/> |
||||
1561 | </xsl:call-template> |
||||
1562 | </xsl:when> |
||||
1563 | <xsl:otherwise> |
||||
1564 | <xsl:value-of select="$bbc_string" disable-output-escaping="yes"/> |
||||
1565 | </xsl:otherwise> |
||||
1566 | </xsl:choose> |
||||
1567 | </xsl:when> |
||||
1568 | <xsl:otherwise> |
||||
1569 | <xsl:choose> |
||||
1570 | <xsl:when test="contains($bbc_string, \'[/html]\')"> |
||||
1571 | <xsl:variable name="following_string"> |
||||
1572 | <xsl:value-of select="substring-after($bbc_string, \'[/html]\')" disable-output-escaping="yes"/> |
||||
1573 | </xsl:variable> |
||||
1574 | <xsl:value-of select="substring-before($bbc_string, \'[/html]\')" disable-output-escaping="no"/> |
||||
1575 | <xsl:text>[/html]</xsl:text> |
||||
1576 | <xsl:call-template name="bbc_html_splitter"> |
||||
1577 | <xsl:with-param name="bbc_string" select="$following_string"/> |
||||
1578 | <xsl:with-param name="inside_outside" select="outside"/> |
||||
1579 | </xsl:call-template> |
||||
1580 | </xsl:when> |
||||
1581 | <xsl:otherwise> |
||||
1582 | <xsl:value-of select="$bbc_string" disable-output-escaping="no"/> |
||||
1583 | </xsl:otherwise> |
||||
1584 | </xsl:choose> |
||||
1585 | </xsl:otherwise> |
||||
1586 | </xsl:choose> |
||||
1587 | </xsl:template>'; |
||||
1588 | |||||
1589 | // Helper templates to build a page index |
||||
1590 | $stylesheet['page_index'] = ' |
||||
1591 | <xsl:template name="page_index"> |
||||
1592 | <xsl:variable name="current_page" select="/*/@page"/> |
||||
1593 | <xsl:variable name="prev_page" select="/*/@page - 1"/> |
||||
1594 | <xsl:variable name="next_page" select="/*/@page + 1"/> |
||||
1595 | |||||
1596 | <div class="pagesection"> |
||||
1597 | <div class="pagelinks floatleft"> |
||||
1598 | |||||
1599 | <span class="pages"> |
||||
1600 | <xsl:value-of select="$txt_pages"/> |
||||
1601 | </span> |
||||
1602 | |||||
1603 | <xsl:if test="$current_page > 1"> |
||||
1604 | <a class="nav_page"> |
||||
1605 | <xsl:attribute name="href"> |
||||
1606 | <xsl:value-of select="concat($dlfilename, \'_\', $prev_page, \'.\', $ext)"/> |
||||
1607 | </xsl:attribute> |
||||
1608 | <span class="main_icons previous_page"></span> |
||||
1609 | </a> |
||||
1610 | </xsl:if> |
||||
1611 | |||||
1612 | <xsl:call-template name="page_links"/> |
||||
1613 | |||||
1614 | <xsl:if test="$current_page < $last_page"> |
||||
1615 | <a class="nav_page"> |
||||
1616 | <xsl:attribute name="href"> |
||||
1617 | <xsl:value-of select="concat($dlfilename, \'_\', $next_page, \'.\', $ext)"/> |
||||
1618 | </xsl:attribute> |
||||
1619 | <span class="main_icons next_page"></span> |
||||
1620 | </a> |
||||
1621 | </xsl:if> |
||||
1622 | </div> |
||||
1623 | </div> |
||||
1624 | </xsl:template> |
||||
1625 | |||||
1626 | <xsl:template name="page_links"> |
||||
1627 | <xsl:param name="page_num" select="1"/> |
||||
1628 | <xsl:variable name="current_page" select="/*/@page"/> |
||||
1629 | <xsl:variable name="prev_page" select="/*/@page - 1"/> |
||||
1630 | <xsl:variable name="next_page" select="/*/@page + 1"/> |
||||
1631 | |||||
1632 | <xsl:choose> |
||||
1633 | <xsl:when test="$page_num = $current_page"> |
||||
1634 | <span class="current_page"> |
||||
1635 | <xsl:value-of select="$page_num"/> |
||||
1636 | </span> |
||||
1637 | </xsl:when> |
||||
1638 | <xsl:when test="$page_num = 1 or $page_num = ($current_page - 1) or $page_num = ($current_page + 1) or $page_num = $last_page"> |
||||
1639 | <a class="nav_page"> |
||||
1640 | <xsl:attribute name="href"> |
||||
1641 | <xsl:value-of select="concat($dlfilename, \'_\', $page_num, \'.\', $ext)"/> |
||||
1642 | </xsl:attribute> |
||||
1643 | <xsl:value-of select="$page_num"/> |
||||
1644 | </a> |
||||
1645 | </xsl:when> |
||||
1646 | <xsl:when test="$page_num = 2 or $page_num = ($current_page + 2)"> |
||||
1647 | <span class="expand_pages" onclick="$(\'.nav_page\').removeClass(\'hidden\'); $(\'.expand_pages\').hide();"> ... </span> |
||||
1648 | <a class="nav_page hidden"> |
||||
1649 | <xsl:attribute name="href"> |
||||
1650 | <xsl:value-of select="concat($dlfilename, \'_\', $page_num, \'.\', $ext)"/> |
||||
1651 | </xsl:attribute> |
||||
1652 | <xsl:value-of select="$page_num"/> |
||||
1653 | </a> |
||||
1654 | </xsl:when> |
||||
1655 | <xsl:otherwise> |
||||
1656 | <a class="nav_page hidden"> |
||||
1657 | <xsl:attribute name="href"> |
||||
1658 | <xsl:value-of select="concat($dlfilename, \'_\', $page_num, \'.\', $ext)"/> |
||||
1659 | </xsl:attribute> |
||||
1660 | <xsl:value-of select="$page_num"/> |
||||
1661 | </a> |
||||
1662 | </xsl:otherwise> |
||||
1663 | </xsl:choose> |
||||
1664 | |||||
1665 | <xsl:text> </xsl:text> |
||||
1666 | |||||
1667 | <xsl:if test="$page_num < $last_page"> |
||||
1668 | <xsl:call-template name="page_links"> |
||||
1669 | <xsl:with-param name="page_num" select="$page_num + 1"/> |
||||
1670 | </xsl:call-template> |
||||
1671 | </xsl:if> |
||||
1672 | </xsl:template>'; |
||||
1673 | |||||
1674 | // Template to insert CSS and JavaScript |
||||
1675 | $stylesheet['css_js'] = ' |
||||
1676 | <xsl:template name="css_js">'; |
||||
1677 | |||||
1678 | export_load_css_js(); |
||||
1679 | |||||
1680 | if (!empty($context['export_css_files'])) |
||||
1681 | { |
||||
1682 | foreach ($context['export_css_files'] as $css_file) |
||||
1683 | { |
||||
1684 | $stylesheet['css_js'] .= ' |
||||
1685 | <link rel="stylesheet"> |
||||
1686 | <xsl:attribute name="href"> |
||||
1687 | <xsl:text>' . $css_file['fileUrl'] . '</xsl:text> |
||||
1688 | </xsl:attribute>'; |
||||
1689 | |||||
1690 | if (!empty($css_file['options']['attributes'])) |
||||
1691 | { |
||||
1692 | foreach ($css_file['options']['attributes'] as $key => $value) |
||||
1693 | $stylesheet['css_js'] .= ' |
||||
1694 | <xsl:attribute name="' . $key . '"> |
||||
1695 | <xsl:text>' . (is_bool($value) ? $key : $value) . '</xsl:text> |
||||
1696 | </xsl:attribute>'; |
||||
1697 | } |
||||
1698 | |||||
1699 | $stylesheet['css_js'] .= ' |
||||
1700 | </link>'; |
||||
1701 | } |
||||
1702 | } |
||||
1703 | |||||
1704 | if (!empty($context['export_css_header'])) |
||||
1705 | { |
||||
1706 | $stylesheet['css_js'] .= ' |
||||
1707 | <style><![CDATA[' . "\n" . implode("\n", $context['export_css_header']) . "\n" . ']]> |
||||
1708 | </style>'; |
||||
1709 | } |
||||
1710 | |||||
1711 | if (!empty($context['export_javascript_vars'])) |
||||
1712 | { |
||||
1713 | $stylesheet['css_js'] .= ' |
||||
1714 | <script><![CDATA['; |
||||
1715 | |||||
1716 | foreach ($context['export_javascript_vars'] as $var => $val) |
||||
1717 | $stylesheet['css_js'] .= "\nvar " . $var . (!empty($val) ? ' = ' . $val : '') . ';'; |
||||
1718 | |||||
1719 | $stylesheet['css_js'] .= "\n" . ']]> |
||||
1720 | </script>'; |
||||
1721 | } |
||||
1722 | |||||
1723 | if (!empty($context['export_javascript_files'])) |
||||
1724 | { |
||||
1725 | foreach ($context['export_javascript_files'] as $js_file) |
||||
1726 | { |
||||
1727 | $stylesheet['css_js'] .= ' |
||||
1728 | <script> |
||||
1729 | <xsl:attribute name="src"> |
||||
1730 | <xsl:text>' . $js_file['fileUrl'] . '</xsl:text> |
||||
1731 | </xsl:attribute>'; |
||||
1732 | |||||
1733 | if (!empty($js_file['options']['attributes'])) |
||||
1734 | { |
||||
1735 | foreach ($js_file['options']['attributes'] as $key => $value) |
||||
1736 | $stylesheet['css_js'] .= ' |
||||
1737 | <xsl:attribute name="' . $key . '"> |
||||
1738 | <xsl:text>' . (is_bool($value) ? $key : $value) . '</xsl:text> |
||||
1739 | </xsl:attribute>'; |
||||
1740 | } |
||||
1741 | |||||
1742 | $stylesheet['css_js'] .= ' |
||||
1743 | </script>'; |
||||
1744 | } |
||||
1745 | } |
||||
1746 | |||||
1747 | if (!empty($context['export_javascript_inline']['standard'])) |
||||
1748 | { |
||||
1749 | $stylesheet['css_js'] .= ' |
||||
1750 | <script><![CDATA[' . "\n" . implode("\n", $context['export_javascript_inline']['standard']) . "\n" . ']]> |
||||
1751 | </script>'; |
||||
1752 | } |
||||
1753 | |||||
1754 | if (!empty($context['export_javascript_inline']['defer'])) |
||||
1755 | { |
||||
1756 | $stylesheet['css_js'] .= ' |
||||
1757 | <script><![CDATA[' . "\n" . 'window.addEventListener("DOMContentLoaded", function() {'; |
||||
1758 | |||||
1759 | $stylesheet['css_js'] .= "\n\t" . str_replace("\n", "\n\t", implode("\n", $context['export_javascript_inline']['defer'])); |
||||
1760 | |||||
1761 | $stylesheet['css_js'] .= "\n" . '});'. "\n" . ']]> |
||||
1762 | </script>'; |
||||
1763 | } |
||||
1764 | |||||
1765 | $stylesheet['css_js'] .= ' |
||||
1766 | </xsl:template>'; |
||||
1767 | |||||
1768 | // End of the XSLT stylesheet |
||||
1769 | $stylesheet['footer'] = ($format == 'XML_XSLT' ? "\t" : '') . '</xsl:stylesheet>'; |
||||
1770 | } |
||||
1771 | |||||
1772 | // Let mods adjust the XSLT stylesheet. |
||||
1773 | call_integration_hook('integrate_export_xslt_stylesheet', array(&$stylesheet, $format)); |
||||
1774 | |||||
1775 | // Remember for later. |
||||
1776 | $xslt_key = isset($xslt_key) ? $xslt_key : $smcFunc['json_encode'](array($format, $uid, $xslt_variables)); |
||||
1777 | $xslts[$xslt_key] = array('stylesheet' => implode("\n", (array) $stylesheet), 'doctype' => $doctype); |
||||
1778 | |||||
1779 | return $xslts[$xslt_key]; |
||||
1780 | } |
||||
1781 | |||||
1782 | /** |
||||
1783 | * Loads and prepares CSS and JavaScript for insertion into an XSLT stylesheet. |
||||
1784 | */ |
||||
1785 | function export_load_css_js() |
||||
1786 | { |
||||
1787 | global $context, $modSettings, $sourcedir, $smcFunc, $user_info; |
||||
1788 | |||||
1789 | // If we're not running a background task, we need to preserve any existing CSS and JavaScript. |
||||
1790 | if (SMF != 'BACKGROUND') |
||||
0 ignored issues
–
show
|
|||||
1791 | { |
||||
1792 | foreach (array('css_files', 'css_header', 'javascript_vars', 'javascript_files', 'javascript_inline') as $var) |
||||
1793 | { |
||||
1794 | if (isset($context[$var])) |
||||
1795 | $context['real_' . $var] = $context[$var]; |
||||
1796 | |||||
1797 | if ($var == 'javascript_inline') |
||||
1798 | { |
||||
1799 | foreach ($context[$var] as $key => $value) |
||||
1800 | $context[$var][$key] = array(); |
||||
1801 | } |
||||
1802 | else |
||||
1803 | $context[$var] = array(); |
||||
1804 | } |
||||
1805 | } |
||||
1806 | // Autoloading is unavailable for background tasks, so we have to do things the hard way... |
||||
1807 | else |
||||
1808 | { |
||||
1809 | if (!empty($modSettings['minimize_files']) && (!class_exists('MatthiasMullie\\Minify\\CSS') || !class_exists('MatthiasMullie\\Minify\\JS'))) |
||||
1810 | { |
||||
1811 | // Include, not require, because minimization is nice to have but not vital here. |
||||
1812 | include_once(implode(DIRECTORY_SEPARATOR, array($sourcedir, 'minify', 'src', 'Exception.php'))); |
||||
1813 | include_once(implode(DIRECTORY_SEPARATOR, array($sourcedir, 'minify', 'src', 'Exceptions', 'BasicException.php'))); |
||||
1814 | include_once(implode(DIRECTORY_SEPARATOR, array($sourcedir, 'minify', 'src', 'Exceptions', 'FileImportException.php'))); |
||||
1815 | include_once(implode(DIRECTORY_SEPARATOR, array($sourcedir, 'minify', 'src', 'Exceptions', 'IOException.php'))); |
||||
1816 | |||||
1817 | include_once(implode(DIRECTORY_SEPARATOR, array($sourcedir, 'minify', 'src', 'Minify.php'))); |
||||
1818 | include_once(implode(DIRECTORY_SEPARATOR, array($sourcedir, 'minify', 'path-converter', 'src', 'Converter.php'))); |
||||
1819 | |||||
1820 | include_once(implode(DIRECTORY_SEPARATOR, array($sourcedir, 'minify', 'src', 'CSS.php'))); |
||||
1821 | include_once(implode(DIRECTORY_SEPARATOR, array($sourcedir, 'minify', 'src', 'JS.php'))); |
||||
1822 | |||||
1823 | if (!class_exists('MatthiasMullie\\Minify\\CSS') || !class_exists('MatthiasMullie\\Minify\\JS')) |
||||
1824 | $modSettings['minimize_files'] = false; |
||||
1825 | } |
||||
1826 | } |
||||
1827 | |||||
1828 | // Load our standard CSS files. |
||||
1829 | loadCSSFile('index.css', array('minimize' => true, 'order_pos' => 1), 'smf_index'); |
||||
1830 | loadCSSFile('responsive.css', array('force_current' => false, 'validate' => true, 'minimize' => true, 'order_pos' => 9000), 'smf_responsive'); |
||||
1831 | |||||
1832 | if ($context['right_to_left']) |
||||
1833 | loadCSSFile('rtl.css', array('order_pos' => 4000), 'smf_rtl'); |
||||
1834 | |||||
1835 | // In case any mods added relevant CSS. |
||||
1836 | call_integration_hook('integrate_pre_css_output'); |
||||
1837 | |||||
1838 | // This next chunk mimics some of template_css() |
||||
1839 | $css_to_minify = array(); |
||||
1840 | $normal_css_files = array(); |
||||
1841 | |||||
1842 | usort( |
||||
1843 | $context['css_files'], |
||||
1844 | function ($a, $b) |
||||
1845 | { |
||||
1846 | return $a['options']['order_pos'] < $b['options']['order_pos'] ? -1 : ($a['options']['order_pos'] > $b['options']['order_pos'] ? 1 : 0); |
||||
1847 | } |
||||
1848 | ); |
||||
1849 | |||||
1850 | foreach ($context['css_files'] as $css_file) |
||||
1851 | { |
||||
1852 | if (!isset($css_file['options']['minimize'])) |
||||
1853 | $css_file['options']['minimize'] = true; |
||||
1854 | |||||
1855 | if (!empty($css_file['options']['minimize']) && !empty($modSettings['minimize_files'])) |
||||
1856 | $css_to_minify[] = $css_file; |
||||
1857 | else |
||||
1858 | $normal_css_files[] = $css_file; |
||||
1859 | } |
||||
1860 | |||||
1861 | $minified_css_files = !empty($css_to_minify) ? custMinify($css_to_minify, 'css') : array(); |
||||
1862 | |||||
1863 | $context['css_files'] = array(); |
||||
1864 | foreach (array_merge($minified_css_files, $normal_css_files) as $css_file) |
||||
1865 | { |
||||
1866 | // Embed the CSS in a <style> element if possible, since exports are supposed to be standalone files. |
||||
1867 | if (file_exists($css_file['filePath'])) |
||||
1868 | $context['css_header'][] = file_get_contents($css_file['filePath']); |
||||
1869 | |||||
1870 | elseif (!empty($css_file['fileUrl'])) |
||||
1871 | $context['css_files'][] = $css_file; |
||||
1872 | } |
||||
1873 | |||||
1874 | // Next, we need to do for JavaScript what we just did for CSS. |
||||
1875 | loadJavaScriptFile('https://ajax.googleapis.com/ajax/libs/jquery/' . JQUERY_VERSION . '/jquery.min.js', array('external' => true, 'seed' => false), 'smf_jquery'); |
||||
1876 | |||||
1877 | // There might be JavaScript that we need to add in order to support custom BBC or something. |
||||
1878 | call_integration_hook('integrate_pre_javascript_output', array(false)); |
||||
1879 | call_integration_hook('integrate_pre_javascript_output', array(true)); |
||||
1880 | |||||
1881 | $js_to_minify = array(); |
||||
1882 | $all_js_files = array(); |
||||
1883 | |||||
1884 | foreach ($context['javascript_files'] as $js_file) |
||||
1885 | { |
||||
1886 | if (!empty($js_file['options']['minimize']) && !empty($modSettings['minimize_files'])) |
||||
1887 | { |
||||
1888 | if (!empty($js_file['options']['async'])) |
||||
1889 | $js_to_minify['async'][] = $js_file; |
||||
1890 | |||||
1891 | elseif (!empty($js_file['options']['defer'])) |
||||
1892 | $js_to_minify['defer'][] = $js_file; |
||||
1893 | |||||
1894 | else |
||||
1895 | $js_to_minify['standard'][] = $js_file; |
||||
1896 | } |
||||
1897 | else |
||||
1898 | $all_js_files[] = $js_file; |
||||
1899 | } |
||||
1900 | |||||
1901 | $context['javascript_files'] = array(); |
||||
1902 | foreach ($js_to_minify as $type => $js_files) |
||||
1903 | { |
||||
1904 | if (!empty($js_files)) |
||||
1905 | { |
||||
1906 | $minified_js_files = custMinify($js_files, 'js'); |
||||
1907 | $all_js_files = array_merge($all_js_files, $minified_js_files); |
||||
1908 | } |
||||
1909 | } |
||||
1910 | |||||
1911 | foreach ($all_js_files as $js_file) |
||||
1912 | { |
||||
1913 | // As with the CSS, embed whatever JavaScript we can. |
||||
1914 | if (file_exists($js_file['filePath'])) |
||||
1915 | $context['javascript_inline'][(!empty($js_file['options']['defer']) ? 'defer' : 'standard')][] = file_get_contents($js_file['filePath']); |
||||
1916 | |||||
1917 | elseif (!empty($js_file['fileUrl'])) |
||||
1918 | $context['javascript_files'][] = $js_file; |
||||
1919 | } |
||||
1920 | |||||
1921 | // We need to embed the smiley images, too. To save space, we store the image data in JS variables. |
||||
1922 | $smiley_mimetypes = array( |
||||
1923 | 'gif' => 'image/gif', |
||||
1924 | 'png' => 'image/png', |
||||
1925 | 'jpg' => 'image/jpeg', |
||||
1926 | 'jpeg' => 'image/jpeg', |
||||
1927 | 'tiff' => 'image/tiff', |
||||
1928 | 'svg' => 'image/svg+xml', |
||||
1929 | ); |
||||
1930 | |||||
1931 | foreach (glob(implode(DIRECTORY_SEPARATOR, array($modSettings['smileys_dir'], $user_info['smiley_set'], '*.*'))) as $smiley_file) |
||||
1932 | { |
||||
1933 | $pathinfo = pathinfo($smiley_file); |
||||
1934 | |||||
1935 | if (!isset($smiley_mimetypes[$pathinfo['extension']])) |
||||
1936 | continue; |
||||
1937 | |||||
1938 | $var = implode('_', array('smf', 'smiley', $pathinfo['filename'], $pathinfo['extension'])); |
||||
1939 | |||||
1940 | if (!isset($context['javascript_vars'][$var])) |
||||
1941 | $context['javascript_vars'][$var] = '\'data:' . $smiley_mimetypes[$pathinfo['extension']] . ';base64,' . base64_encode(file_get_contents($smiley_file)) . '\''; |
||||
1942 | } |
||||
1943 | |||||
1944 | $context['javascript_inline']['defer'][] = implode("\n", array( |
||||
1945 | '$("img.smiley").each(function() {', |
||||
1946 | ' var data_uri_var = $(this).attr("src").replace(/.*\/(\w+)\.(\w+)$/, "smf_smiley_$1_$2");', |
||||
1947 | ' $(this).attr("src", window[data_uri_var]);', |
||||
1948 | '});', |
||||
1949 | )); |
||||
1950 | |||||
1951 | // Now move everything to the special export version of these arrays. |
||||
1952 | foreach (array('css_files', 'css_header', 'javascript_vars', 'javascript_files', 'javascript_inline') as $var) |
||||
1953 | { |
||||
1954 | if (isset($context[$var])) |
||||
1955 | $context['export_' . $var] = $context[$var]; |
||||
1956 | |||||
1957 | unset($context[$var]); |
||||
1958 | } |
||||
1959 | |||||
1960 | // Finally, restore the real values. |
||||
1961 | if (SMF !== 'BACKGROUND') |
||||
0 ignored issues
–
show
|
|||||
1962 | { |
||||
1963 | foreach (array('css_files', 'css_header', 'javascript_vars', 'javascript_files', 'javascript_inline') as $var) |
||||
1964 | { |
||||
1965 | if (isset($context['real_' . $var])) |
||||
1966 | $context[$var] = $context['real_' . $var]; |
||||
1967 | |||||
1968 | unset($context['real_' . $var]); |
||||
1969 | } |
||||
1970 | } |
||||
1971 | } |
||||
1972 | |||||
1973 | ?> |