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