Passed
Push — 1.7 ( 4c1abf...23cbb7 )
by Greg
08:10
created

action.php (8 issues)

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) 2018 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
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
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
		Session::put('masquerade', '1');
198
	} else {
199
		http_response_code(406);
200
	}
201
	break;
202
203
case 'unlink-media':
204
	// Remove links from an individual and their spouse-family records to a media object.
205
	// Used by the "unlink" option on the album (lightbox) tab.
206
	$source = Individual::getInstance(Filter::post('source', WT_REGEX_XREF), $WT_TREE);
207
	$target = Filter::post('target', WT_REGEX_XREF);
208
	if ($source && $source->canShow() && $source->canEdit() && $target) {
209
		// Consider the individual and their spouse-family records
210
		$sources   = $source->getSpouseFamilies();
0 ignored issues
show
The method getSpouseFamilies() does not exist on Fisharebest\Webtrees\Media. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

210
		/** @scrutinizer ignore-call */ 
211
  $sources   = $source->getSpouseFamilies();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
The method getSpouseFamilies() does not exist on Fisharebest\Webtrees\Family. Did you maybe mean getSpouse()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

210
		/** @scrutinizer ignore-call */ 
211
  $sources   = $source->getSpouseFamilies();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
The method getSpouseFamilies() does not exist on Fisharebest\Webtrees\GedcomRecord. It seems like you code against a sub-type of Fisharebest\Webtrees\GedcomRecord such as Fisharebest\Webtrees\Individual. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

210
		/** @scrutinizer ignore-call */ 
211
  $sources   = $source->getSpouseFamilies();
Loading history...
The method getSpouseFamilies() does not exist on Fisharebest\Webtrees\Source. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

210
		/** @scrutinizer ignore-call */ 
211
  $sources   = $source->getSpouseFamilies();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
The method getSpouseFamilies() does not exist on Fisharebest\Webtrees\Note. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

210
		/** @scrutinizer ignore-call */ 
211
  $sources   = $source->getSpouseFamilies();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
The method getSpouseFamilies() does not exist on Fisharebest\Webtrees\Repository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

210
		/** @scrutinizer ignore-call */ 
211
  $sources   = $source->getSpouseFamilies();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
211
		$sources[] = $source;
212
		foreach ($sources as $source) {
213
			foreach ($source->getFacts() as $fact) {
214
				if (!$fact->isPendingDeletion()) {
215
					if ($fact->getValue() == '@' . $target . '@') {
216
						// Level 1 links
217
						$source->deleteFact($fact->getFactId(), true);
218
					} elseif (strpos($fact->getGedcom(), ' @' . $target . '@')) {
219
						// Level 2-3 links
220
						$source->updateFact($fact->getFactId(), preg_replace(array('/\n2 OBJE @' . $target . '@(\n[3-9].*)*/', '/\n3 OBJE @' . $target . '@(\n[4-9].*)*/'), '', $fact->getGedcom()), true);
221
					}
222
				}
223
			}
224
		}
225
	} else {
226
		http_response_code(406);
227
	}
228
	break;
229
230
case 'reject-changes':
231
	// Reject all the pending changes for a record
232
	$record = GedcomRecord::getInstance(Filter::post('xref', WT_REGEX_XREF), $WT_TREE);
233
	if ($record && $record->canEdit() && Auth::isModerator($record->getTree())) {
234
		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()));
235
		FunctionsImport::rejectAllChanges($record);
236
	} else {
237
		http_response_code(406);
238
	}
239
	break;
240
241
case 'theme':
242
	// Change the current theme
243
	$theme = Filter::post('theme');
244
	if (Site::getPreference('ALLOW_USER_THEMES') && array_key_exists($theme, Theme::themeNames())) {
245
		Session::put('theme_id', $theme);
246
		// Remember our selection
247
		Auth::user()->setPreference('theme', $theme);
248
	} else {
249
		// Request for a non-existant theme.
250
		http_response_code(406);
251
	}
252
	break;
253
}
254