These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * webtrees: online genealogy |
||
4 | * Copyright (C) 2017 webtrees development team |
||
5 | * This program is free software: you can redistribute it and/or modify |
||
6 | * it under the terms of the GNU General Public License as published by |
||
7 | * the Free Software Foundation, either version 3 of the License, or |
||
8 | * (at your option) any later version. |
||
9 | * This program is distributed in the hope that it will be useful, |
||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
12 | * GNU General Public License for more details. |
||
13 | * You should have received a copy of the GNU General Public License |
||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
15 | */ |
||
16 | namespace Fisharebest\Webtrees; |
||
17 | |||
18 | use Fisharebest\Webtrees\Controller\PageController; |
||
19 | use Fisharebest\Webtrees\Functions\Functions; |
||
20 | use Fisharebest\Webtrees\Functions\FunctionsDb; |
||
21 | use Fisharebest\Webtrees\Functions\FunctionsEdit; |
||
22 | use Fisharebest\Webtrees\Functions\FunctionsImport; |
||
23 | use Fisharebest\Webtrees\Functions\FunctionsPrint; |
||
24 | use Fisharebest\Webtrees\Query\QueryMedia; |
||
25 | |||
26 | /** @global Tree $WT_TREE */ |
||
27 | global $WT_TREE; |
||
28 | |||
29 | require 'includes/session.php'; |
||
30 | |||
31 | $NO_UPDATE_CHAN = $WT_TREE->getPreference('NO_UPDATE_CHAN'); |
||
32 | $MEDIA_DIRECTORY = $WT_TREE->getPreference('MEDIA_DIRECTORY'); |
||
33 | |||
34 | $pid = Filter::get('pid', WT_REGEX_XREF, Filter::post('pid', WT_REGEX_XREF)); // edit this media object |
||
35 | $linktoid = Filter::get('linktoid', WT_REGEX_XREF, Filter::post('linktoid', WT_REGEX_XREF)); // create a new media object, linked to this record |
||
36 | $action = Filter::get('action', null, Filter::post('action')); |
||
37 | $filename = Filter::get('filename', null, Filter::post('filename')); |
||
38 | $text = Filter::postArray('text'); |
||
39 | $tag = Filter::postArray('tag', WT_REGEX_TAG); |
||
40 | $islink = Filter::postArray('islink'); |
||
41 | $glevels = Filter::postArray('glevels', '[0-9]'); |
||
42 | $folder = Filter::post('folder'); |
||
43 | $update_CHAN = !Filter::postBool('preserve_last_changed'); |
||
44 | |||
45 | $controller = new PageController; |
||
46 | $controller |
||
47 | ->restrictAccess(Auth::isMember($WT_TREE)); |
||
48 | |||
49 | $disp = true; |
||
50 | $media = Media::getInstance($pid, $WT_TREE); |
||
51 | if ($media) { |
||
52 | $disp = $media->canShow(); |
||
53 | } |
||
54 | if ($action == 'update' || $action == 'create') { |
||
55 | if ($linktoid) { |
||
56 | $disp = GedcomRecord::getInstance($linktoid, $WT_TREE)->canShow(); |
||
57 | } |
||
58 | } |
||
59 | |||
60 | if (!Auth::isEditor($WT_TREE) || !$disp) { |
||
61 | $controller |
||
62 | ->pageHeader() |
||
63 | ->addInlineJavascript('closePopupAndReloadParent();'); |
||
64 | |||
65 | return; |
||
66 | } |
||
67 | |||
68 | // There is a lot of common code in the create and update cases… |
||
69 | // …and also in the admin_media_upload.php script |
||
70 | |||
71 | switch ($action) { |
||
72 | case 'create': // Save the information from the “showcreateform” action |
||
73 | $controller->setPageTitle(I18N::translate('Create a media object')); |
||
74 | |||
75 | // Validate the media folder |
||
76 | $folderName = str_replace('\\', '/', $folder); |
||
77 | $folderName = trim($folderName, '/'); |
||
78 | if ($folderName == '.') { |
||
79 | $folderName = ''; |
||
80 | } |
||
81 | View Code Duplication | if ($folderName) { |
|
82 | $folderName .= '/'; |
||
83 | // Not allowed to use “../” |
||
84 | if (strpos('/' . $folderName, '/../') !== false) { |
||
85 | FlashMessages::addMessage('Folder names are not allowed to include “../”'); |
||
86 | break; |
||
87 | } |
||
88 | } |
||
89 | |||
90 | // Make sure the media folder exists |
||
91 | View Code Duplication | if (!is_dir(WT_DATA_DIR . $MEDIA_DIRECTORY)) { |
|
92 | if (File::mkdir(WT_DATA_DIR . $MEDIA_DIRECTORY)) { |
||
93 | FlashMessages::addMessage(I18N::translate('The folder %s has been created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY))); |
||
94 | } else { |
||
95 | FlashMessages::addMessage(I18N::translate('The folder %s does not exist, and it could not be created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY)), 'danger'); |
||
96 | break; |
||
97 | } |
||
98 | } |
||
99 | |||
100 | // Managers can create new media paths (subfolders). Users must use existing folders. |
||
101 | View Code Duplication | if ($folderName && !is_dir(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName)) { |
|
102 | if (Auth::isManager($WT_TREE)) { |
||
103 | if (File::mkdir(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName)) { |
||
104 | FlashMessages::addMessage(I18N::translate('The folder %s has been created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName))); |
||
105 | } else { |
||
106 | FlashMessages::addMessage(I18N::translate('The folder %s does not exist, and it could not be created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName)), 'danger'); |
||
107 | break; |
||
108 | } |
||
109 | } else { |
||
110 | // Regular users should not have seen this option - so no need for an error message. |
||
111 | break; |
||
112 | } |
||
113 | } |
||
114 | |||
115 | // The media folder exists. Now create a thumbnail folder to match it. |
||
116 | View Code Duplication | if (!is_dir(WT_DATA_DIR . $MEDIA_DIRECTORY . 'thumbs/' . $folderName)) { |
|
117 | if (!File::mkdir(WT_DATA_DIR . $MEDIA_DIRECTORY . 'thumbs/' . $folderName)) { |
||
118 | FlashMessages::addMessage(I18N::translate('The folder %s does not exist, and it could not be created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY . 'thumbs/' . $folderName)), 'danger'); |
||
119 | break; |
||
120 | } |
||
121 | } |
||
122 | |||
123 | // A thumbnail file with no main image? |
||
124 | if (!empty($_FILES['thumbnail']['name']) && empty($_FILES['mediafile']['name'])) { |
||
125 | // Assume the user used the wrong field, and treat this as a main image |
||
126 | $_FILES['mediafile'] = $_FILES['thumbnail']; |
||
127 | unset($_FILES['thumbnail']); |
||
128 | } |
||
129 | |||
130 | // Thumbnail files must contain images. |
||
131 | View Code Duplication | if (!empty($_FILES['thumbnail']['name']) && !preg_match('/^image/', $_FILES['thumbnail']['type'])) { |
|
132 | FlashMessages::addMessage(I18N::translate('Thumbnail files must contain images.')); |
||
133 | break; |
||
134 | } |
||
135 | |||
136 | // User-specified filename? |
||
137 | if ($tag[0] == 'FILE' && $text[0]) { |
||
138 | $filename = $text[0]; |
||
139 | } |
||
140 | // Use the name of the uploaded file? |
||
141 | // If no filename specified, use the name of the uploaded file? |
||
142 | View Code Duplication | if (!$filename && !empty($_FILES['mediafile']['name'])) { |
|
143 | $filename = $_FILES['mediafile']['name']; |
||
144 | } |
||
145 | |||
146 | // Validate the media path and filename |
||
147 | View Code Duplication | if (preg_match('/^https?:\/\//i', $text[0], $match)) { |
|
148 | // External media needs no further validation |
||
149 | $fileName = $filename; |
||
150 | $folderName = ''; |
||
151 | unset($_FILES['mediafile'], $_FILES['thumbnail']); |
||
152 | } elseif (preg_match('/([\/\\\\<>])/', $filename, $match)) { |
||
153 | // Local media files cannot contain certain special characters |
||
154 | FlashMessages::addMessage(I18N::translate('Filenames are not allowed to contain the character “%s”.', $match[1])); |
||
155 | break; |
||
156 | } elseif (preg_match('/(\.(php|pl|cgi|bash|sh|bat|exe|com|htm|html|shtml))$/i', $filename, $match)) { |
||
157 | // Do not allow obvious script files. |
||
158 | FlashMessages::addMessage(I18N::translate('Filenames are not allowed to have the extension “%s”.', $match[1])); |
||
159 | break; |
||
160 | } elseif (!$filename) { |
||
161 | FlashMessages::addMessage(I18N::translate('No media file was provided.')); |
||
162 | break; |
||
163 | } else { |
||
164 | $fileName = $filename; |
||
165 | } |
||
166 | |||
167 | // Now copy the file to the correct location. |
||
168 | if (!empty($_FILES['mediafile']['name'])) { |
||
169 | $serverFileName = WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName . $fileName; |
||
170 | if (file_exists($serverFileName)) { |
||
171 | FlashMessages::addMessage(I18N::translate('The file %s already exists. Use another filename.', $folderName . $fileName)); |
||
172 | break; |
||
173 | } |
||
174 | if (move_uploaded_file($_FILES['mediafile']['tmp_name'], $serverFileName)) { |
||
175 | Log::addMediaLog('Media file ' . $serverFileName . ' uploaded'); |
||
176 | View Code Duplication | } else { |
|
177 | FlashMessages::addMessage( |
||
178 | I18N::translate('There was an error uploading your file.') . |
||
179 | '<br>' . |
||
180 | Functions::fileUploadErrorText($_FILES['mediafile']['error']) |
||
181 | ); |
||
182 | break; |
||
183 | } |
||
184 | |||
185 | // Now copy the (optional) thumbnail |
||
186 | if (!empty($_FILES['thumbnail']['name']) && preg_match('/^image\/(png|gif|jpeg)/', $_FILES['thumbnail']['type'], $match)) { |
||
187 | // Thumbnails have either |
||
188 | // (a) the same filename as the main image |
||
189 | // (b) the same filename as the main image - but with a .png extension |
||
190 | if ($match[1] == 'png' && !preg_match('/\.(png)$/i', $fileName)) { |
||
191 | $thumbFile = preg_replace('/\.[a-z0-9]{3,5}$/', '.png', $fileName); |
||
192 | } else { |
||
193 | $thumbFile = $fileName; |
||
194 | } |
||
195 | $serverFileName = WT_DATA_DIR . $MEDIA_DIRECTORY . 'thumbs/' . $folderName . $thumbFile; |
||
196 | if (move_uploaded_file($_FILES['thumbnail']['tmp_name'], $serverFileName)) { |
||
197 | Log::addMediaLog('Thumbnail file ' . $serverFileName . ' uploaded'); |
||
198 | } |
||
199 | } |
||
200 | } |
||
201 | |||
202 | $controller->pageHeader(); |
||
203 | // Build the gedcom record |
||
204 | $newged = '0 @new@ OBJE'; |
||
205 | if ($tag[0] == 'FILE') { |
||
206 | // The admin has an edit field to change the filename |
||
207 | $text[0] = $folderName . $fileName; |
||
208 | } else { |
||
209 | // Users keep the original filename |
||
210 | $newged .= "\n1 FILE " . $folderName . $fileName; |
||
211 | } |
||
212 | |||
213 | $newged = FunctionsEdit::handleUpdates($newged); |
||
214 | |||
215 | $new_media = $WT_TREE->createRecord($newged); |
||
216 | if ($linktoid) { |
||
217 | $record = GedcomRecord::getInstance($linktoid, $WT_TREE); |
||
218 | $record->createFact('1 OBJE @' . $new_media->getXref() . '@', true); |
||
219 | Log::addEditLog('Media ID ' . $new_media->getXref() . ' successfully added to ' . $linktoid); |
||
220 | $controller->addInlineJavascript('closePopupAndReloadParent();'); |
||
221 | } else { |
||
222 | Log::addEditLog('Media ID ' . $new_media->getXref() . ' successfully added.'); |
||
223 | } |
||
224 | echo '<button onclick="closePopupAndReloadParent();">', I18N::translate('close'), '</button>'; |
||
225 | |||
226 | return; |
||
227 | |||
228 | case 'update': // Save the information from the “editmedia” action |
||
229 | $controller->setPageTitle(I18N::translate('Edit the media object')); |
||
230 | |||
231 | // Validate the media folder |
||
232 | $folderName = str_replace('\\', '/', $folder); |
||
233 | $folderName = trim($folderName, '/'); |
||
234 | if ($folderName == '.') { |
||
235 | $folderName = ''; |
||
236 | } |
||
237 | View Code Duplication | if ($folderName) { |
|
238 | $folderName .= '/'; |
||
239 | // Not allowed to use “../” |
||
240 | if (strpos('/' . $folderName, '/../') !== false) { |
||
241 | FlashMessages::addMessage('Folder names are not allowed to include “../”'); |
||
242 | break; |
||
243 | } |
||
244 | } |
||
245 | |||
246 | // Make sure the media folder exists |
||
247 | View Code Duplication | if (!is_dir(WT_DATA_DIR . $MEDIA_DIRECTORY)) { |
|
248 | if (File::mkdir(WT_DATA_DIR . $MEDIA_DIRECTORY)) { |
||
249 | FlashMessages::addMessage(I18N::translate('The folder %s has been created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY))); |
||
250 | } else { |
||
251 | FlashMessages::addMessage(I18N::translate('The folder %s does not exist, and it could not be created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY)), 'danger'); |
||
252 | break; |
||
253 | } |
||
254 | } |
||
255 | |||
256 | // Managers can create new media paths (subfolders). Users must use existing folders. |
||
257 | View Code Duplication | if ($folderName && !is_dir(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName)) { |
|
258 | if (Auth::isManager($WT_TREE)) { |
||
259 | if (File::mkdir(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName)) { |
||
260 | FlashMessages::addMessage(I18N::translate('The folder %s has been created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName))); |
||
261 | } else { |
||
262 | FlashMessages::addMessage(I18N::translate('The folder %s does not exist, and it could not be created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY . $folderName)), 'danger'); |
||
263 | break; |
||
264 | } |
||
265 | } else { |
||
266 | // Regular users should not have seen this option - so no need for an error message. |
||
267 | break; |
||
268 | } |
||
269 | } |
||
270 | |||
271 | // The media folder exists. Now create a thumbnail folder to match it. |
||
272 | View Code Duplication | if (!is_dir(WT_DATA_DIR . $MEDIA_DIRECTORY . 'thumbs/' . $folderName)) { |
|
273 | if (!File::mkdir(WT_DATA_DIR . $MEDIA_DIRECTORY . 'thumbs/' . $folderName)) { |
||
274 | FlashMessages::addMessage(I18N::translate('The folder %s does not exist, and it could not be created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY . 'thumbs/' . $folderName)), 'danger'); |
||
275 | break; |
||
276 | } |
||
277 | } |
||
278 | |||
279 | // Validate the media path and filename |
||
280 | View Code Duplication | if (preg_match('/^https?:\/\//i', $filename, $match)) { |
|
281 | // External media needs no further validation |
||
282 | $fileName = $filename; |
||
283 | $folderName = ''; |
||
284 | unset($_FILES['mediafile'], $_FILES['thumbnail']); |
||
285 | } elseif (preg_match('/([\/\\\\<>])/', $filename, $match)) { |
||
286 | // Local media files cannot contain certain special characters |
||
287 | FlashMessages::addMessage(I18N::translate('Filenames are not allowed to contain the character “%s”.', $match[1])); |
||
288 | break; |
||
289 | } elseif (preg_match('/(\.(php|pl|cgi|bash|sh|bat|exe|com|htm|html|shtml))$/i', $filename, $match)) { |
||
290 | // Do not allow obvious script files. |
||
291 | FlashMessages::addMessage(I18N::translate('Filenames are not allowed to have the extension “%s”.', $match[1])); |
||
292 | break; |
||
293 | } elseif (!$filename) { |
||
294 | FlashMessages::addMessage(I18N::translate('No media file was provided.')); |
||
295 | break; |
||
296 | } else { |
||
297 | $fileName = $filename; |
||
298 | } |
||
299 | |||
300 | $oldFilename = $media->getFilename(); |
||
301 | $newFilename = $folderName . $fileName; |
||
302 | |||
303 | // Cannot rename local to external or vice-versa |
||
304 | if (Functions::isFileExternal($oldFilename) != Functions::isFileExternal($filename)) { |
||
305 | FlashMessages::addMessage(I18N::translate('The media file %1$s could not be renamed to %2$s.', Html::filename($oldFilename), Html::filename($newFilename))); |
||
306 | break; |
||
307 | } |
||
308 | |||
309 | $messages = false; |
||
310 | $move_file = false; |
||
311 | |||
312 | // Move files on disk (if we can) to reflect the change to the GEDCOM data |
||
313 | if (!$media->isExternal()) { |
||
314 | $oldServerFile = $media->getServerFilename('main'); |
||
315 | $oldServerThumb = $media->getServerFilename('thumb'); |
||
316 | |||
317 | $newmedia = new Media('xxx', "0 @xxx@ OBJE\n1 FILE " . $newFilename, null, $WT_TREE); |
||
318 | $newServerFile = $newmedia->getServerFilename('main'); |
||
319 | $newServerThumb = $newmedia->getServerFilename('thumb'); |
||
320 | |||
321 | // We could be either renaming an existing file, or updating a record (with no valid file) to point to a new file |
||
322 | if ($oldServerFile !== $newServerFile) { |
||
323 | //-- check if the file is used in more than one gedcom |
||
324 | //-- do not allow it to be moved or renamed if it is |
||
325 | if (!$media->isExternal() && FunctionsDb::isMediaUsedInOtherTree($media->getFilename(), $WT_TREE->getTreeId())) { |
||
326 | FlashMessages::addMessage(I18N::translate('This file is linked to another family tree on this server. It cannot be deleted, moved, or renamed until these links have been removed.')); |
||
327 | break; |
||
328 | } |
||
329 | |||
330 | $move_file = true; |
||
331 | View Code Duplication | if (!file_exists($newServerFile) || md5_file($oldServerFile) === md5_file($newServerFile)) { |
|
332 | try { |
||
333 | rename($oldServerFile, $newServerFile); |
||
334 | FlashMessages::addMessage(I18N::translate('The media file %1$s has been renamed to %2$s.', Html::filename($oldFilename), Html::filename($newFilename))); |
||
335 | } catch (\ErrorException $ex) { |
||
336 | FlashMessages::addMessage(I18N::translate('The media file %1$s could not be renamed to %2$s.', Html::filename($oldFilename), Html::filename($newFilename))); |
||
337 | } |
||
338 | $messages = true; |
||
339 | } |
||
340 | View Code Duplication | if (!file_exists($newServerFile)) { |
|
341 | FlashMessages::addMessage(I18N::translate('The media file %s does not exist.', Html::filename($newFilename))); |
||
342 | $messages = true; |
||
343 | } |
||
344 | } |
||
345 | if ($oldServerThumb != $newServerThumb) { |
||
346 | $move_file = true; |
||
347 | View Code Duplication | if (!file_exists($newServerThumb) || md5_file($oldServerFile) == md5_file($newServerThumb)) { |
|
348 | try { |
||
349 | rename($oldServerThumb, $newServerThumb); |
||
350 | FlashMessages::addMessage(I18N::translate('The thumbnail file %1$s has been renamed to %2$s.', Html::filename($oldFilename), Html::filename($newFilename))); |
||
351 | } catch (\ErrorException $ex) { |
||
352 | FlashMessages::addMessage(I18N::translate('The thumbnail file %1$s could not be renamed to %2$s.', Html::filename($oldFilename), Html::filename($newFilename))); |
||
353 | } |
||
354 | $messages = true; |
||
355 | } |
||
356 | View Code Duplication | if (!file_exists($newServerThumb)) { |
|
357 | FlashMessages::addMessage(I18N::translate('The thumbnail file %s does not exist.', Html::filename($newFilename))); |
||
358 | $messages = true; |
||
359 | } |
||
360 | } |
||
361 | } |
||
362 | |||
363 | // Insert the 1 FILE xxx record into the arrays used by function FunctionsEdit::handle_updatesges() |
||
364 | $glevels = array_merge(['1'], $glevels); |
||
365 | $tag = array_merge(['FILE'], $tag); |
||
366 | $islink = array_merge([0], $islink); |
||
367 | $text = array_merge([$newFilename], $text); |
||
368 | |||
369 | $record = GedcomRecord::getInstance($pid, $WT_TREE); |
||
370 | $newrec = '0 @' . $pid . "@ OBJE\n"; |
||
371 | $newrec = FunctionsEdit::handleUpdates($newrec); |
||
372 | $record->updateRecord($newrec, $update_CHAN); |
||
373 | |||
374 | if ($move_file) { |
||
375 | // We've moved a file. Therefore we must approve the change, as rejecting |
||
376 | // the change will create broken references. |
||
377 | FunctionsImport::acceptAllChanges($record->getXref(), $record->getTree()->getTreeId()); |
||
378 | } |
||
379 | |||
380 | if ($pid && $linktoid) { |
||
381 | $record = GedcomRecord::getInstance($linktoid, $WT_TREE); |
||
382 | $record->createFact('1 OBJE @' . $pid . '@', true); |
||
383 | Log::addEditLog('Media ID ' . $pid . ' successfully added to ' . $linktoid); |
||
384 | } |
||
385 | $controller->pageHeader(); |
||
386 | if ($messages) { |
||
387 | echo '<button onclick="closePopupAndReloadParent();">', I18N::translate('close'), '</button>'; |
||
388 | } else { |
||
389 | $controller->addInlineJavascript('closePopupAndReloadParent();'); |
||
390 | } |
||
391 | |||
392 | return; |
||
393 | case 'showmediaform': |
||
394 | $controller->setPageTitle(I18N::translate('Create a media object')); |
||
395 | $action = 'create'; |
||
396 | break; |
||
397 | case 'editmedia': |
||
398 | $controller->setPageTitle(I18N::translate('Edit the media object')); |
||
399 | $action = 'update'; |
||
400 | break; |
||
401 | default: |
||
402 | throw new \Exception('Bad $action (' . $action . ') in addmedia.php'); |
||
403 | } |
||
404 | |||
405 | $controller->pageHeader(); |
||
406 | |||
407 | echo '<div id="addmedia-page">'; //container for media edit pop-up |
||
408 | echo '<form method="post" name="newmedia" action="addmedia.php" enctype="multipart/form-data">'; |
||
409 | echo '<input type="hidden" name="action" value="', $action, '">'; |
||
410 | echo '<input type="hidden" name="ged" value="', $WT_TREE->getNameHtml(), '">'; |
||
411 | echo '<input type="hidden" name="pid" value="', $pid, '">'; |
||
412 | if ($linktoid) { |
||
413 | echo '<input type="hidden" name="linktoid" value="', $linktoid, '">'; |
||
414 | } |
||
415 | echo '<table class="facts_table">'; |
||
416 | echo '<tr><td class="topbottombar" colspan="2">'; |
||
417 | echo $controller->getPageTitle(), FunctionsPrint::helpLink('OBJE'); |
||
418 | echo '</td></tr>'; |
||
419 | View Code Duplication | if (!$linktoid && $action == 'create') { |
|
420 | echo '<tr><td class="descriptionbox wrap width25">'; |
||
421 | echo I18N::translate('Enter an individual, family, or source ID'); |
||
422 | echo '</td><td class="optionbox wrap"><input type="text" data-autocomplete-type="IFS" name="linktoid" id="linktoid" size="6" value="">'; |
||
423 | echo '<p class="small text-muted">', I18N::translate('Enter or search for the ID of the individual, family, or source to which this media object should be linked.'), '</p></td></tr>'; |
||
424 | } |
||
425 | |||
426 | if ($media) { |
||
427 | $gedrec = $media->getGedcom(); |
||
428 | } else { |
||
429 | $gedrec = ''; |
||
430 | } |
||
431 | |||
432 | // 1 FILE |
||
433 | View Code Duplication | if (preg_match('/\n\d (FILE.*)/', $gedrec, $match)) { |
|
434 | $gedfile = $match[1]; |
||
435 | } elseif ($filename) { |
||
436 | $gedfile = 'FILE ' . $filename; |
||
437 | } else { |
||
438 | $gedfile = 'FILE'; |
||
439 | } |
||
440 | |||
441 | if ($gedfile == 'FILE') { |
||
442 | // Box for user to choose to upload file from local computer |
||
443 | echo '<tr><td class="descriptionbox wrap width25">'; |
||
444 | echo I18N::translate('Media file to upload') . '</td><td class="optionbox wrap"><input type="file" name="mediafile" onchange="updateFormat(this.value);" size="40"></td></tr>'; |
||
445 | // Check for thumbnail generation support |
||
446 | if (Auth::isManager($WT_TREE)) { |
||
447 | echo '<tr><td class="descriptionbox wrap width25">'; |
||
448 | echo I18N::translate('Thumbnail to upload') . '</td><td class="optionbox wrap"><input type="file" name="thumbnail" size="40">'; |
||
449 | echo '<p class="small text-muted">', I18N::translate('Choose the thumbnail image that you want to upload. Although thumbnails can be generated automatically for images, you may wish to generate your own thumbnail, especially for other media types. For example, you can provide a still image from a video, or a photograph of the individual who made an audio recording.'), '</p>'; |
||
450 | echo '</td></tr>'; |
||
451 | } |
||
452 | } |
||
453 | |||
454 | // Filename on server |
||
455 | $isExternal = Functions::isFileExternal($gedfile); |
||
456 | if ($gedfile === 'FILE') { |
||
457 | if (Auth::isManager($WT_TREE)) { |
||
458 | FunctionsEdit::addSimpleTag( |
||
459 | '1 FILE', |
||
460 | '', |
||
461 | I18N::translate('Filename on server'), |
||
462 | '<p class="small text-muted">' . I18N::translate('Do not change to keep original filename.') . '<br>' . I18N::translate('You may enter a URL, beginning with “http://”.') . '</p>' |
||
463 | ); |
||
464 | } |
||
465 | $folder = ''; |
||
466 | } else { |
||
467 | if ($isExternal) { |
||
468 | $fileName = substr($gedfile, 5); |
||
469 | $folder = ''; |
||
470 | } else { |
||
471 | $tmp = substr($gedfile, 5); |
||
472 | $fileName = basename($tmp); |
||
473 | $folder = dirname($tmp); |
||
474 | if ($folder === '.') { |
||
475 | $folder = ''; |
||
476 | } |
||
477 | } |
||
478 | |||
479 | echo '<tr>'; |
||
480 | echo '<td class="descriptionbox wrap width25">'; |
||
481 | echo I18N::translate('Filename on server'); |
||
482 | echo '</td>'; |
||
483 | echo '<td class="optionbox wrap wrap">'; |
||
484 | if (Auth::isManager($WT_TREE)) { |
||
485 | echo '<input name="filename" type="text" value="' . Filter::escapeHtml($fileName) . '" size="40"'; |
||
486 | if ($isExternal) { |
||
487 | echo '>'; |
||
488 | } else { |
||
489 | echo '><p class="small text-muted">' . I18N::translate('Do not change to keep original filename.') . '</p>'; |
||
490 | } |
||
491 | } else { |
||
492 | echo $fileName; |
||
493 | echo '<input name="filename" type="hidden" value="' . Filter::escapeHtml($fileName) . '" size="40">'; |
||
494 | } |
||
495 | echo '</td>'; |
||
496 | echo '</tr>'; |
||
497 | } |
||
498 | |||
499 | // Box for user to choose the folder to store the image |
||
500 | if (!$isExternal) { |
||
501 | echo '<tr><td class="descriptionbox wrap width25">'; |
||
502 | echo I18N::translate('Folder name on server'), '</td><td class="optionbox wrap">'; |
||
503 | //-- don’t let regular users change the location of media items |
||
504 | if ($action !== 'update' || Auth::isManager($WT_TREE)) { |
||
505 | $mediaFolders = QueryMedia::folderList(); |
||
506 | echo '<select name="folder_list" onchange="document.newmedia.folder.value=this.options[this.selectedIndex].value;">'; |
||
507 | echo '<option '; |
||
508 | if ($folder == '') { |
||
509 | echo 'selected'; |
||
510 | } |
||
511 | echo ' value=""> ', I18N::translate('Choose: '), ' </option>'; |
||
512 | if (Auth::isAdmin()) { |
||
513 | echo '<option value="other" disabled>', I18N::translate('Other folder… please type in'), '</option>'; |
||
514 | } |
||
515 | foreach ($mediaFolders as $f) { |
||
516 | echo '<option value="', $f, '" '; |
||
517 | if ($folder == $f) { |
||
518 | echo 'selected'; |
||
519 | } |
||
520 | echo '>', $f, '</option>'; |
||
521 | } |
||
522 | echo '</select>'; |
||
523 | } else { |
||
524 | echo $folder; |
||
525 | } |
||
526 | if (Auth::isAdmin()) { |
||
527 | echo '<br><input type="text" name="folder" size="40" value="', $folder, '">'; |
||
528 | if ($gedfile === 'FILE') { |
||
529 | echo '<p class="small text-muted">', I18N::translate('This entry is ignored if you have entered a URL into the filename field.'), '</p>'; |
||
530 | } |
||
531 | } else { |
||
532 | echo '<input name="folder" type="hidden" value="', Filter::escapeHtml($folder), '">'; |
||
533 | } |
||
534 | echo '<p class="small text-muted">', I18N::translate('If you have a large number of media files, you can organize them into folders and subfolders.'), '</p>'; echo '</td></tr>'; |
||
535 | } else { |
||
536 | echo '<input name="folder" type="hidden" value="">'; |
||
537 | } |
||
538 | |||
539 | // 1 FILE / 2 FORM |
||
540 | if (preg_match('/\n(2 FORM .*)/', $gedrec, $match)) { |
||
541 | $gedform = $match[1]; |
||
542 | } else { |
||
543 | $gedform = '2 FORM'; |
||
544 | } |
||
545 | $formid = FunctionsEdit::addSimpleTag($gedform); |
||
546 | |||
547 | // automatically set the format field from the filename |
||
548 | $controller->addInlineJavascript(' |
||
549 | function updateFormat(filename) { |
||
550 | var extsearch=/\.([a-zA-Z]{3,4})$/; |
||
551 | if (extsearch.exec(filename)) { |
||
552 | ext = RegExp.$1.toLowerCase(); |
||
553 | if (ext=="jpg") ext="jpeg"; |
||
554 | if (ext=="tif") ext="tiff"; |
||
555 | } else { |
||
556 | ext = ""; |
||
557 | } |
||
558 | formfield = document.getElementById("' . $formid . '"); |
||
559 | formfield.value = ext; |
||
560 | } |
||
561 | '); |
||
562 | |||
563 | // 1 FILE / 2 FORM / 3 TYPE |
||
564 | if (preg_match('/\n(3 TYPE .*)/', $gedrec, $match)) { |
||
565 | $gedtype = $match[1]; |
||
566 | } else { |
||
567 | $gedtype = '3 TYPE photo'; // default to ‘Photo’ |
||
568 | } |
||
569 | FunctionsEdit::addSimpleTag($gedtype); |
||
570 | |||
571 | // 1 FILE / 2 TITL |
||
572 | if (preg_match('/\n(2 TITL .*)/', $gedrec, $match)) { |
||
573 | $gedtitl = $match[1]; |
||
574 | } else { |
||
575 | $gedtitl = '2 TITL'; |
||
576 | } |
||
577 | FunctionsEdit::addSimpleTag($gedtitl); |
||
578 | |||
579 | // 1 FILE / 2 TITL / 3 _HEB |
||
580 | View Code Duplication | if (strstr($WT_TREE->getPreference('ADVANCED_NAME_FACTS'), '_HEB') !== false) { |
|
581 | if (preg_match('/\n(3 _HEB .*)/', $gedrec, $match)) { |
||
582 | $gedtitl = $match[1]; |
||
583 | } else { |
||
584 | $gedtitl = '3 _HEB'; |
||
585 | } |
||
586 | FunctionsEdit::addSimpleTag($gedtitl); |
||
587 | } |
||
588 | |||
589 | // 1 FILE / 2 TITL / 3 ROMN |
||
590 | View Code Duplication | if (strstr($WT_TREE->getPreference('ADVANCED_NAME_FACTS'), 'ROMN') !== false) { |
|
591 | if (preg_match('/\n(3 ROMN .*)/', $gedrec, $match)) { |
||
592 | $gedtitl = $match[1]; |
||
593 | } else { |
||
594 | $gedtitl = '3 ROMN'; |
||
595 | } |
||
596 | FunctionsEdit::addSimpleTag($gedtitl); |
||
597 | } |
||
598 | |||
599 | // 1 _PRIM |
||
600 | if (preg_match('/\n(1 _PRIM .*)/', $gedrec, $match)) { |
||
601 | $gedprim = $match[1]; |
||
602 | } else { |
||
603 | $gedprim = '1 _PRIM'; |
||
604 | } |
||
605 | FunctionsEdit::addSimpleTag($gedprim); |
||
606 | |||
607 | //-- print out editing fields for any other data in the media record |
||
608 | $sourceLevel = 0; |
||
609 | $sourceSOUR = ''; |
||
610 | $sourcePAGE = ''; |
||
611 | $sourceTEXT = ''; |
||
612 | $sourceDATE = ''; |
||
613 | $sourceQUAY = ''; |
||
614 | if (!empty($gedrec)) { |
||
615 | preg_match_all('/\n(1 (?!FILE|FORM|TYPE|TITL|_PRIM|_THUM|CHAN|DATA).*(\n[2-9] .*)*)/', $gedrec, $matches); |
||
616 | foreach ($matches[1] as $subrec) { |
||
617 | $pieces = explode("\n", $subrec); |
||
618 | foreach ($pieces as $piece) { |
||
619 | $ft = preg_match("/(\d) (\w+)(.*)/", $piece, $match); |
||
620 | if ($ft == 0) { |
||
621 | continue; |
||
622 | } |
||
623 | $subLevel = $match[1]; |
||
624 | $fact = trim($match[2]); |
||
625 | $event = trim($match[3]); |
||
626 | if ($fact === 'NOTE' || $fact === 'TEXT') { |
||
627 | $event .= Functions::getCont($subLevel + 1, $subrec); |
||
628 | } |
||
629 | if ($sourceSOUR !== '' && $subLevel <= $sourceLevel) { |
||
630 | // Get rid of all saved Source data |
||
631 | FunctionsEdit::addSimpleTag($sourceLevel . ' SOUR ' . $sourceSOUR); |
||
632 | FunctionsEdit::addSimpleTag(($sourceLevel + 1) . ' PAGE ' . $sourcePAGE); |
||
633 | FunctionsEdit::addSimpleTag(($sourceLevel + 2) . ' TEXT ' . $sourceTEXT); |
||
634 | FunctionsEdit::addSimpleTag(($sourceLevel + 2) . ' DATE ' . $sourceDATE, '', GedcomTag::getLabel('DATA:DATE')); |
||
635 | FunctionsEdit::addSimpleTag(($sourceLevel + 1) . ' QUAY ' . $sourceQUAY); |
||
636 | $sourceSOUR = ''; |
||
637 | } |
||
638 | |||
639 | if ($fact === 'SOUR') { |
||
640 | $sourceLevel = $subLevel; |
||
641 | $sourceSOUR = $event; |
||
642 | $sourcePAGE = ''; |
||
643 | $sourceTEXT = ''; |
||
644 | $sourceDATE = ''; |
||
645 | $sourceQUAY = ''; |
||
646 | continue; |
||
647 | } |
||
648 | |||
649 | // Save all incoming data about this source reference |
||
650 | if ($sourceSOUR !== '') { |
||
651 | if ($fact === 'PAGE') { |
||
652 | $sourcePAGE = $event; |
||
653 | continue; |
||
654 | } |
||
655 | if ($fact === 'TEXT') { |
||
656 | $sourceTEXT = $event; |
||
657 | continue; |
||
658 | } |
||
659 | if ($fact === 'DATE') { |
||
660 | $sourceDATE = $event; |
||
661 | continue; |
||
662 | } |
||
663 | if ($fact === 'QUAY') { |
||
664 | $sourceQUAY = $event; |
||
665 | continue; |
||
666 | } |
||
667 | continue; |
||
668 | } |
||
669 | |||
670 | // Output anything that isn’t part of a source reference |
||
671 | if (!empty($fact) && $fact !== 'CONC' && $fact !== 'CONT' && $fact !== 'DATA') { |
||
672 | FunctionsEdit::addSimpleTag($subLevel . ' ' . $fact . ' ' . $event); |
||
673 | } |
||
674 | } |
||
675 | } |
||
676 | |||
677 | if ($sourceSOUR !== '') { |
||
678 | // Get rid of all saved Source data |
||
679 | FunctionsEdit::addSimpleTag($sourceLevel . ' SOUR ' . $sourceSOUR); |
||
680 | FunctionsEdit::addSimpleTag(($sourceLevel + 1) . ' PAGE ' . $sourcePAGE); |
||
681 | FunctionsEdit::addSimpleTag(($sourceLevel + 2) . ' TEXT ' . $sourceTEXT); |
||
682 | FunctionsEdit::addSimpleTag(($sourceLevel + 2) . ' DATE ' . $sourceDATE, '', GedcomTag::getLabel('DATA:DATE')); |
||
683 | FunctionsEdit::addSimpleTag(($sourceLevel + 1) . ' QUAY ' . $sourceQUAY); |
||
684 | } |
||
685 | } |
||
686 | View Code Duplication | if (Auth::isAdmin() && $action === 'update') { |
|
687 | echo '<tr><td class="descriptionbox wrap width25">'; |
||
688 | echo I18N::translate('Last change'), '</td><td class="optionbox wrap">'; |
||
689 | if ($NO_UPDATE_CHAN) { |
||
690 | echo '<input type="checkbox" checked name="preserve_last_changed">'; |
||
691 | } else { |
||
692 | echo '<input type="checkbox" name="preserve_last_changed">'; |
||
693 | } |
||
694 | echo I18N::translate('Keep the existing “last change” information'), '<br>'; |
||
695 | echo '</td></tr>'; |
||
696 | } |
||
697 | echo '</table>'; |
||
698 | FunctionsEdit::printAddLayer('SOUR', 1); |
||
699 | FunctionsEdit::printAddLayer('NOTE', 1); |
||
700 | FunctionsEdit::printAddLayer('SHARED_NOTE', 1); |
||
701 | FunctionsEdit::printAddLayer('RESN', 1); |
||
702 | ?> |
||
703 | <div class="row form-group"> |
||
704 | <div class="col-sm-9 offset-sm-3"> |
||
705 | <button class="btn btn-primary" type="submit"> |
||
706 | <?= FontAwesome::decorativeIcon('save') ?> |
||
707 | <?= /* I18N: A button label. */ I18N::translate('save') ?> |
||
708 | </button> |
||
709 | <a class="btn btn-secondary" href="<?= $media->getHtmlUrl() ?>"> |
||
0 ignored issues
–
show
|
|||
710 | <?= FontAwesome::decorativeIcon('cancel') ?> |
||
711 | <?= /* I18N: A button label. */ I18N::translate('cancel') ?> |
||
712 | </a> |
||
713 | </div> |
||
714 | </div> |
||
715 | </form> |
||
716 | </div> |
||
717 |
$media->getHtmlUrl()
can contain request data and is used in html attribute with double-quotes context(s) leading to a potential security vulnerability.4 paths for user data to reach this point
$_FILES,
and$file
is assigned in action.php on line 112$_FILES,
and$file
is assignedin action.php on line 112
$gedcom
is assignedin action.php on line 148
$gedcom
is passed to Tree::createRecord()in action.php on line 158
$gedcom
is passed to GedcomRecord::getInstance()in app/Tree.php on line 782
$gedcom
is passed to GedcomRecord::__construct()in app/GedcomRecord.php on line 202
in app/GedcomRecord.php on line 80
in app/GedcomRecord.php on line 495
$newgedrec
is assignedin app/Report/ReportParserGenerate.php on line 594
in app/Report/ReportParserGenerate.php on line 618
in app/Report/ReportParserGenerate.php on line 1262
in vendor/app/Functions/Functions.php on line 160
$thisSubrecord
is assignedin vendor/app/Functions/Functions.php on line 161
in vendor/app/Functions/Functions.php on line 167
in app/Report/ReportParserGenerate.php on line 1262
$value
is assignedin app/Report/ReportParserGenerate.php on line 1308
in app/Report/ReportParserGenerate.php on line 1358
$id
is assignedin app/Report/ReportParserGenerate.php on line 827
$id
is passed to GedcomRecord::getInstance()in app/Report/ReportParserGenerate.php on line 841
$xref
is passed to GedcomRecord::__construct()in app/GedcomRecord.php on line 202
in app/GedcomRecord.php on line 79
in app/GedcomRecord.php on line 280
in app/GedcomRecord.php on line 351
in app/GedcomRecord.php on line 330
in addmedia.php on line 709
$_POST,
and$newged
is assigned in edit_interface.php on line 426$_POST,
and$newged
is assignedin edit_interface.php on line 426
$newged
is assignedin edit_interface.php on line 446
$newged
is passed through substr(), and$newged
is assignedin edit_interface.php on line 459
$newged
is passed to GedcomRecord::updateFact()in edit_interface.php on line 467
$gedcom
is passed through preg_replace(), and$gedcom
is assignedin app/GedcomRecord.php on line 1192
$gedcom
is passed through trim(), and$gedcom
is assignedin app/GedcomRecord.php on line 1193
$new_gedcom
is assignedin app/GedcomRecord.php on line 1230
in app/GedcomRecord.php on line 1249
in app/GedcomRecord.php on line 495
$newgedrec
is assignedin app/Report/ReportParserGenerate.php on line 594
in app/Report/ReportParserGenerate.php on line 618
in app/Report/ReportParserGenerate.php on line 1262
in vendor/app/Functions/Functions.php on line 160
$thisSubrecord
is assignedin vendor/app/Functions/Functions.php on line 161
in vendor/app/Functions/Functions.php on line 167
in app/Report/ReportParserGenerate.php on line 1262
$value
is assignedin app/Report/ReportParserGenerate.php on line 1308
in app/Report/ReportParserGenerate.php on line 1358
$id
is assignedin app/Report/ReportParserGenerate.php on line 827
$id
is passed to GedcomRecord::getInstance()in app/Report/ReportParserGenerate.php on line 841
$xref
is passed to GedcomRecord::__construct()in app/GedcomRecord.php on line 202
in app/GedcomRecord.php on line 79
in app/GedcomRecord.php on line 280
in app/GedcomRecord.php on line 351
in app/GedcomRecord.php on line 330
in addmedia.php on line 709
$_POST,
and$newged
is assigned in edit_interface.php on line 430$_POST,
and$newged
is assignedin edit_interface.php on line 430
$newged
is assignedin edit_interface.php on line 446
$newged
is passed through substr(), and$newged
is assignedin edit_interface.php on line 459
$newged
is passed to GedcomRecord::updateFact()in edit_interface.php on line 467
$gedcom
is passed through preg_replace(), and$gedcom
is assignedin app/GedcomRecord.php on line 1192
$gedcom
is passed through trim(), and$gedcom
is assignedin app/GedcomRecord.php on line 1193
$new_gedcom
is assignedin app/GedcomRecord.php on line 1230
in app/GedcomRecord.php on line 1249
in app/GedcomRecord.php on line 495
$newgedrec
is assignedin app/Report/ReportParserGenerate.php on line 594
in app/Report/ReportParserGenerate.php on line 618
in app/Report/ReportParserGenerate.php on line 1262
in vendor/app/Functions/Functions.php on line 160
$thisSubrecord
is assignedin vendor/app/Functions/Functions.php on line 161
in vendor/app/Functions/Functions.php on line 167
in app/Report/ReportParserGenerate.php on line 1262
$value
is assignedin app/Report/ReportParserGenerate.php on line 1308
in app/Report/ReportParserGenerate.php on line 1358
$id
is assignedin app/Report/ReportParserGenerate.php on line 827
$id
is passed to GedcomRecord::getInstance()in app/Report/ReportParserGenerate.php on line 841
$xref
is passed to GedcomRecord::__construct()in app/GedcomRecord.php on line 202
in app/GedcomRecord.php on line 79
in app/GedcomRecord.php on line 280
in app/GedcomRecord.php on line 351
in app/GedcomRecord.php on line 330
in addmedia.php on line 709
$_POST,
and$newged
is assigned in edit_interface.php on line 454$_POST,
and$newged
is assignedin edit_interface.php on line 454
$newged
is passed through substr(), and$newged
is assignedin edit_interface.php on line 459
$newged
is passed to GedcomRecord::updateFact()in edit_interface.php on line 467
$gedcom
is passed through preg_replace(), and$gedcom
is assignedin app/GedcomRecord.php on line 1192
$gedcom
is passed through trim(), and$gedcom
is assignedin app/GedcomRecord.php on line 1193
$new_gedcom
is assignedin app/GedcomRecord.php on line 1230
in app/GedcomRecord.php on line 1249
in app/GedcomRecord.php on line 495
$newgedrec
is assignedin app/Report/ReportParserGenerate.php on line 594
in app/Report/ReportParserGenerate.php on line 618
in app/Report/ReportParserGenerate.php on line 1262
in vendor/app/Functions/Functions.php on line 160
$thisSubrecord
is assignedin vendor/app/Functions/Functions.php on line 161
in vendor/app/Functions/Functions.php on line 167
in app/Report/ReportParserGenerate.php on line 1262
$value
is assignedin app/Report/ReportParserGenerate.php on line 1308
in app/Report/ReportParserGenerate.php on line 1358
$id
is assignedin app/Report/ReportParserGenerate.php on line 827
$id
is passed to GedcomRecord::getInstance()in app/Report/ReportParserGenerate.php on line 841
$xref
is passed to GedcomRecord::__construct()in app/GedcomRecord.php on line 202
in app/GedcomRecord.php on line 79
in app/GedcomRecord.php on line 280
in app/GedcomRecord.php on line 351
in app/GedcomRecord.php on line 330
in addmedia.php on line 709
Preventing Cross-Site-Scripting Attacks
Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.
In order to prevent this, make sure to escape all user-provided data:
General Strategies to prevent injection
In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:
For numeric data, we recommend to explicitly cast the data: