Completed
Push — charts ( 0e4f4a...b7ffec )
by Greg
24:16 queued 12:16
created

action.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 29 and the first side effect is on line 23.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * webtrees: online genealogy
4
 * Copyright (C) 2016 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
/**
19
 * Defined in session.php
20
 *
21
 * @global Tree $WT_TREE
22
 */
23
global $WT_TREE;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
24
25
use Fisharebest\Webtrees\Functions\FunctionsDb;
26
use Fisharebest\Webtrees\Functions\FunctionsEdit;
27
use Fisharebest\Webtrees\Functions\FunctionsImport;
28
29
define('WT_SCRIPT_NAME', 'action.php');
30
require './includes/session.php';
31
32
header('Content-type: text/html; charset=UTF-8');
33
34
if (!Filter::checkCsrf()) {
35
	http_response_code(406);
36
37
	return;
38
}
39
40
switch (Filter::post('action')) {
41
case 'accept-changes':
42
	// Accept all the pending changes for a record
43
	$record = GedcomRecord::getInstance(Filter::post('xref', WT_REGEX_XREF), $WT_TREE);
44
	if ($record && Auth::isModerator($record->getTree()) && $record->canShow() && $record->canEdit()) {
45
		FlashMessages::addMessage(/* I18N: %s is the name of an individual, source or other record */ I18N::translate('The changes to ā€œ%sā€ have been accepted.', $record->getFullName()));
46
		FunctionsImport::acceptAllChanges($record->getXref(), $record->getTree()->getTreeId());
47
	} else {
48
		http_response_code(406);
49
	}
50
	break;
51
52
case 'copy-fact':
53
	// Copy a fact to the clipboard
54
	$xref    = Filter::post('xref', WT_REGEX_XREF);
55
	$fact_id = Filter::post('fact_id');
56
57
	$record = GedcomRecord::getInstance($xref, $WT_TREE);
58
59
	if ($record && $record->canEdit()) {
60
		foreach ($record->getFacts() as $fact) {
61
			if ($fact->getFactId() == $fact_id) {
62
				switch ($fact->getTag()) {
63
				case 'NOTE':
64
				case 'SOUR':
65
				case 'OBJE':
66
					$type = 'all'; // paste this anywhere
67
					break;
68
				default:
69
					$type = $record::RECORD_TYPE; // paste only to the same record type
70
					break;
71
				}
72
				$clipboard = Session::get('clipboard');
73
				if (!is_array($clipboard)) {
74
					$clipboard = array();
75
				}
76
				$clipboard[$fact_id] = array(
77
					'type'    => $type,
78
					'factrec' => $fact->getGedcom(),
79
					'fact'    => $fact->getTag(),
80
					);
81
				// The clipboard only holds 10 facts
82
				while (count($clipboard) > 10) {
83
					array_shift($clipboard);
84
				}
85
				Session::put('clipboard', $clipboard);
86
				FlashMessages::addMessage(I18N::translate('The record has been copied to the clipboard.'));
87
				break 2;
88
			}
89
		}
90
	}
91
	break;
92
93
case 'paste-fact':
94
	// Paste a fact from the clipboard
95
	$xref      = Filter::post('xref', WT_REGEX_XREF);
96
	$fact_id   = Filter::post('fact_id');
97
	$record    = GedcomRecord::getInstance($xref, $WT_TREE);
98
	$clipboard = Session::get('clipboard');
99
100
	if ($record && $record->canEdit() && isset($clipboard[$fact_id])) {
101
		$record->createFact($clipboard[$fact_id]['factrec'], true);
102
	}
103
	break;
104
105
case 'delete-fact':
106
	$xref    = Filter::post('xref', WT_REGEX_XREF);
107
	$fact_id = Filter::post('fact_id');
108
109
	$record = GedcomRecord::getInstance($xref, $WT_TREE);
110
	if ($record && $record->canShow() && $record->canEdit()) {
111
		foreach ($record->getFacts() as $fact) {
112
			if ($fact->getFactId() == $fact_id && $fact->canShow() && $fact->canEdit()) {
113
				$record->deleteFact($fact_id, true);
114
				break 2;
115
			}
116
		}
117
	}
118
119
	// Can’t find the record/fact, or don’t have permission to delete it.
120
	http_response_code(406);
121
	break;
122
123
case 'delete-record':
124
	$record = GedcomRecord::getInstance(Filter::post('xref', WT_REGEX_XREF), $WT_TREE);
125
	if ($record && Auth::isEditor($record->getTree()) && $record->canShow() && $record->canEdit()) {
126
		// Delete links to this record
127
		foreach (FunctionsDb::fetchAllLinks($record->getXref(), $record->getTree()->getTreeId()) as $xref) {
128
			$linker     = GedcomRecord::getInstance($xref, $WT_TREE);
129
			$old_gedcom = $linker->getGedcom();
130
			$new_gedcom = FunctionsEdit::removeLinks($old_gedcom, $record->getXref());
131
			// FunctionsDb::fetch_all_links() does not take account of pending changes. The links (or even the
132
			// record itself) may have already been deleted.
133
			if ($old_gedcom !== $new_gedcom) {
134
				// If we have removed a link from a family to an individual, and it has only one member
135
				if (preg_match('/^0 @' . WT_REGEX_XREF . '@ FAM/', $new_gedcom) && preg_match_all('/\n1 (HUSB|WIFE|CHIL) @(' . WT_REGEX_XREF . ')@/', $new_gedcom, $match) == 1) {
136
					// Delete the family
137
					$family = GedcomRecord::getInstance($xref, $WT_TREE);
138
					FlashMessages::addMessage(/* I18N: %s is the name of a family group, e.g. ā€œHusband name + Wife nameā€ */ I18N::translate('The family ā€œ%sā€ has been deleted because it only has one member.', $family->getFullName()));
139
					$family->deleteRecord();
140
					// Delete any remaining link to this family
141
					if ($match) {
142
						$relict     = GedcomRecord::getInstance($match[2][0], $WT_TREE);
143
						$new_gedcom = $relict->getGedcom();
144
						$new_gedcom = FunctionsEdit::removeLinks($new_gedcom, $linker->getXref());
145
						$relict->updateRecord($new_gedcom, false);
146
						FlashMessages::addMessage(/* I18N: %s are names of records, such as sources, repositories or individuals */ I18N::translate('The link from ā€œ%1$sā€ to ā€œ%2$sā€ has been deleted.', $relict->getFullName(), $family->getFullName()));
147
					}
148
				} else {
149
					// Remove links from $linker to $record
150
					FlashMessages::addMessage(/* I18N: %s are names of records, such as sources, repositories or individuals */ I18N::translate('The link from ā€œ%1$sā€ to ā€œ%2$sā€ has been deleted.', $linker->getFullName(), $record->getFullName()));
151
					$linker->updateRecord($new_gedcom, false);
152
				}
153
			}
154
		}
155
		// Delete the record itself
156
		$record->deleteRecord();
157
	} else {
158
		http_response_code(406);
159
	}
160
	break;
161
162 View Code Duplication
case 'delete-user':
163
	$user = User::find(Filter::postInteger('user_id'));
164
165
	if ($user && Auth::isAdmin() && Auth::user() !== $user) {
166
		Log::addAuthenticationLog('Deleted user: ' . $user->getUserName());
167
		$user->delete();
168
	}
169
	break;
170
171
case 'language':
172
	// Change the current language
173
	$language = Filter::post('language');
174
	try {
175
		I18N::init($language);
176
		Session::put('locale', $language);
177
		// Remember our selection
178
		Auth::user()->setPreference('language', $language);
179
	} catch (\Exception $ex) {
180
		// Request for a non-existant language.
181
		http_response_code(406);
182
	}
183
	break;
184
185 View Code Duplication
case 'masquerade':
186
	$user = User::find(Filter::postInteger('user_id'));
187
188
	if ($user && Auth::isAdmin() && Auth::user() !== $user) {
189
		Log::addAuthenticationLog('Masquerade as user: ' . $user->getUserName());
190
		Auth::login($user);
191
	} else {
192
		http_response_code(406);
193
	}
194
	break;
195
196
case 'unlink-media':
197
	// Remove links from an individual and their spouse-family records to a media object.
198
	// Used by the "unlink" option on the album (lightbox) tab.
199
	$source = Individual::getInstance(Filter::post('source', WT_REGEX_XREF), $WT_TREE);
200
	$target = Filter::post('target', WT_REGEX_XREF);
201
	if ($source && $source->canShow() && $source->canEdit() && $target) {
202
		// Consider the individual and their spouse-family records
203
		$sources   = $source->getSpouseFamilies();
204
		$sources[] = $source;
205
		foreach ($sources as $source) {
206
			foreach ($source->getFacts() as $fact) {
207
				if (!$fact->isPendingDeletion()) {
208
					if ($fact->getValue() == '@' . $target . '@') {
209
						// Level 1 links
210
						$source->deleteFact($fact->getFactId(), true);
211
					} elseif (strpos($fact->getGedcom(), ' @' . $target . '@')) {
212
						// Level 2-3 links
213
						$source->updateFact($fact->getFactId(), preg_replace(array('/\n2 OBJE @' . $target . '@(\n[3-9].*)*/', '/\n3 OBJE @' . $target . '@(\n[4-9].*)*/'), '', $fact->getGedcom()), true);
214
					}
215
				}
216
			}
217
		}
218
	} else {
219
		http_response_code(406);
220
	}
221
	break;
222
223
case 'reject-changes':
224
	// Reject all the pending changes for a record
225
	$record = GedcomRecord::getInstance(Filter::post('xref', WT_REGEX_XREF), $WT_TREE);
226
	if ($record && $record->canEdit() && Auth::isModerator($record->getTree())) {
227
		FlashMessages::addMessage(/* I18N: %s is the name of an individual, source or other record */ I18N::translate('The changes to ā€œ%sā€ have been rejected.', $record->getFullName()));
228
		FunctionsImport::rejectAllChanges($record);
229
	} else {
230
		http_response_code(406);
231
	}
232
	break;
233
234
case 'theme':
235
	// Change the current theme
236
	$theme = Filter::post('theme');
237
	if (Site::getPreference('ALLOW_USER_THEMES') && array_key_exists($theme, Theme::themeNames())) {
238
		Session::put('theme_id', $theme);
239
		// Remember our selection
240
		Auth::user()->setPreference('theme', $theme);
241
	} else {
242
		// Request for a non-existant theme.
243
		http_response_code(406);
244
	}
245
	break;
246
}
247