Test Failed
Branch master (4a3c5b)
by Greg
12:31
created

RelativesTabModule   F

Complexity

Total Complexity 62

Size/Duplication

Total Lines 428
Duplicated Lines 24.77 %

Importance

Changes 0
Metric Value
dl 106
loc 428
rs 3.8461
c 0
b 0
f 0
wmc 62

10 Methods

Rating   Name   Duplication   Size   Complexity  
A getDescription() 0 2 1
A getPreLoadContent() 0 2 1
F printFamily() 53 213 34
F getTabContent() 53 123 11
A getTitle() 0 2 1
A canLoadAjax() 0 2 1
C ageDifference() 0 23 10
A isGrayedOut() 0 2 1
A defaultTabOrder() 0 2 1
A hasTabContent() 0 2 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like RelativesTabModule often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use RelativesTabModule, and based on these observations, apply Extract Interface, too.

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\Module;
17
18
use Fisharebest\Webtrees\Auth;
19
use Fisharebest\Webtrees\Date;
20
use Fisharebest\Webtrees\Family;
21
use Fisharebest\Webtrees\Functions\Functions;
22
use Fisharebest\Webtrees\GedcomTag;
23
use Fisharebest\Webtrees\I18N;
24
use Fisharebest\Webtrees\Individual;
25
use Fisharebest\Webtrees\Theme;
26
27
/**
28
 * Class RelativesTabModule
29
 */
30
class RelativesTabModule extends AbstractModule implements ModuleTabInterface {
31
	/**
32
	 * How should this module be labelled on tabs, menus, etc.?
33
	 *
34
	 * @return string
35
	 */
36
	public function getTitle() {
37
		return /* I18N: Name of a module */ I18N::translate('Families');
38
	}
39
40
	/**
41
	 * A sentence describing what this module does.
42
	 *
43
	 * @return string
44
	 */
45
	public function getDescription() {
46
		return /* I18N: Description of the “Families” module */ I18N::translate('A tab showing the close relatives of an individual.');
47
	}
48
49
	/**
50
	 * The user can re-arrange the tab order, but until they do, this
51
	 * is the order in which tabs are shown.
52
	 *
53
	 * @return int
54
	 */
55
	public function defaultTabOrder() {
56
		return 20;
57
	}
58
59
	/**
60
	 * Display the age difference between marriages and the births of children.
61
	 *
62
	 * @param Date $prev
63
	 * @param Date $next
64
	 * @param int  $child_number
65
	 *
66
	 * @return string
67
	 */
68
	private static function ageDifference(Date $prev, Date $next, $child_number = 0) {
69
		if ($prev->isOK() && $next->isOK()) {
70
			$days = $next->maximumJulianDay() - $prev->minimumJulianDay();
71
			if ($days < 0) {
72
				// Show warning triangle if dates in reverse order
73
				$diff = '<i class="icon-warning"></i> ';
74
			} elseif ($child_number > 1 && $days > 1 && $days < 240) {
75
				// Show warning triangle if children born too close together
76
				$diff = '<i class="icon-warning"></i> ';
77
			} else {
78
				$diff = '';
79
			}
80
81
			$months = round($days * 12 / 365.25); // Approximate - we do not know the calendar
82
			if (abs($months) == 12 || abs($months) >= 24) {
83
				$diff .= I18N::plural('%s year', '%s years', round($months / 12), I18N::number(round($months / 12)));
84
			} elseif ($months != 0) {
85
				$diff .= I18N::plural('%s month', '%s months', $months, I18N::number($months));
86
			}
87
88
			return '<div class="elderdate age">' . $diff . '</div>';
89
		} else {
90
			return '';
91
		}
92
	}
93
94
	/**
95
	 * Print a family group.
96
	 *
97
	 * @param Family $family
98
	 * @param string $type
99
	 * @param string $label
100
	 */
101
	private function printFamily(Family $family, $type, $label) {
102
		global $controller;
103
104
		if ($family->getTree()->getPreference('SHOW_PRIVATE_RELATIONSHIPS')) {
105
			$access_level = Auth::PRIV_HIDE;
106
		} else {
107
			$access_level = Auth::accessLevel($family->getTree());
108
		}
109
110
		?>
111
		<table>
112
			<tr>
113
				<td>
114
					<i class="icon-cfamily"></i>
115
				</td>
116
				<td>
117
					<span class="subheaders"> <?= $label ?></span>
118
					<a href="<?= $family->getHtmlUrl() ?>"> - <?= I18N::translate('View this family') ?></a>
119
				</td>
120
			</tr>
121
		</table>
122
123
		<table class="table table-sm wt-facts-table">
124
		<caption></caption>
125
		<tbody>
126
		<?php
127
128
		///// HUSB /////
129
		$found = false;
130 View Code Duplication
		foreach ($family->getFacts('HUSB', false, $access_level) as $fact) {
131
			$found |= !$fact->isPendingDeletion();
132
			$person = $fact->getTarget();
133
			if ($person instanceof Individual) {
134
				$row_class = 'wt-gender-' . $person->getSex();
135
				if ($fact->isPendingAddition()) {
136
					$row_class .= ' new';
137
				} elseif ($fact->isPendingDeletion()) {
138
					$row_class .= ' old';
139
				}
140
				$icon = $controller->record === $person ? '<i class="icon-selected"></i>' : '';
141
				?>
142
					<tr class="<?= $row_class ?>">
143
						<th scope="row">
144
							<?= $icon ?>
145
							<?= Functions::getCloseRelationshipName($controller->record, $person) ?>
146
						</th>
147
						<td class="border-0 p-0">
148
							<?= Theme::theme()->individualBoxLarge($person) ?>
149
						</td>
150
					</tr>
151
				<?php
152
			}
153
		}
154
		if (!$found && $family->canEdit()) {
155
			?>
156
			<tr>
157
				<th></th>
158
				<td scope="row">
159
					<a href="edit_interface.php?action=add_spouse_to_family&amp;ged=<?= $family->getTree()->getNameHtml() ?>&amp;xref=<?= $family->getXref() ?>&amp;famtag=HUSB">
160
						<?= I18N::translate('Add a husband to this family') ?>
161
					</a>
162
					</td>
163
			</tr>
164
			<?php
165
		}
166
167
		///// WIFE /////
168
		$found = false;
169 View Code Duplication
		foreach ($family->getFacts('WIFE', false, $access_level) as $fact) {
170
			$person = $fact->getTarget();
171
			if ($person instanceof Individual) {
172
				$found |= !$fact->isPendingDeletion();
173
				$row_class = 'wt-gender-' . $person->getSex();
174
				if ($fact->isPendingAddition()) {
175
					$row_class .= ' new';
176
				} elseif ($fact->isPendingDeletion()) {
177
					$row_class .= ' old';
178
				}
179
				$icon = $controller->record === $person ? '<i class="icon-selected"></i>' : '';
180
        ?>
181
				<tr class="<?= $row_class ?>">
182
					<th scope="row">
183
					<?= $icon ?>
184
					<?= Functions::getCloseRelationshipName($controller->record, $person) ?>
185
					</th>
186
					<td class="border-0 p-0">
187
						<?= Theme::theme()->individualBoxLarge($person) ?>
188
					</td>
189
				</tr>
190
				<?php
191
			}
192
		}
193
		if (!$found && $family->canEdit()) {
194
			?>
195
			<tr>
196
				<th scope="row"></th>
197
				<td>
198
					<a href="edit_interface.php?action=add_spouse_to_family&amp;ged=<?= $family->getTree()->getNameHtml() ?>&amp;xref=<?= $family->getXref() ?>&amp;famtag=WIFE">
199
						<?= I18N::translate('Add a wife to this family') ?>
200
					</a>
201
				</td>
202
			</tr>
203
			<?php
204
		}
205
206
		///// MARR /////
207
		$found = false;
208
		$prev  = new Date('');
209
		foreach ($family->getFacts(WT_EVENTS_MARR . '|' . WT_EVENTS_DIV, true) as $fact) {
210
			$found |= !$fact->isPendingDeletion();
211
			if ($fact->isPendingAddition()) {
212
				$row_class = 'new';
213
			} elseif ($fact->isPendingDeletion()) {
214
				$row_class = 'old';
215
			} else {
216
				$row_class = '';
217
			}
218
			?>
219
			<tr class="<?= $row_class ?>">
220
				<th scope="row">
221
				</th>
222
				<td>
223
					<?= GedcomTag::getLabelValue($fact->getTag(), $fact->getDate()->display() . ' — ' . $fact->getPlace()->getFullName()) ?>
224
				</td>
225
			</tr>
226
			<?php
227
			if (!$prev->isOK() && $fact->getDate()->isOK()) {
228
				$prev = $fact->getDate();
229
			}
230
		}
231
		if (!$found && $family->canShow() && $family->canEdit()) {
232
			// Add a new marriage
233
			?>
234
			<tr>
235
				<th scope="row">
236
				</th>
237
				<td>
238
					<a href="edit_interface.php?action=add&amp;ged=<?= $family->getTree()->getNameHtml() ?>&amp;xref=<?= $family->getXref() ?>&amp;fact=MARR">
239
						<?= I18N::translate('Add marriage details') ?>
240
					</a>
241
				</td>
242
			</tr>
243
			<?php
244
		}
245
246
		///// CHIL /////
247
		$child_number = 0;
248
		foreach ($family->getFacts('CHIL', false, $access_level) as $fact) {
249
			$person = $fact->getTarget();
250
			if ($person instanceof Individual) {
251
				$row_class = 'wt-gender-' . $person->getSex();
252
				if ($fact->isPendingAddition()) {
253
					$child_number++;
254
					$row_class .= ' new';
255
				} elseif ($fact->isPendingDeletion()) {
256
					$row_class .= ' old';
257
				} else {
258
					$child_number++;
259
				}
260
				$next = new Date('');
261
				foreach ($person->getFacts(WT_EVENTS_BIRT, true) as $bfact) {
262
					if ($bfact->getDate()->isOK()) {
263
						$next = $bfact->getDate();
264
						break;
265
					}
266
				}
267
				$icon = $controller->record === $person ? '<i class="icon-selected"></i>' : '';
268
				?>
269
				<tr class="<?= $row_class ?>">
270
					<th scope="row">
271
						<?= $icon ?>
272
						<?= self::ageDifference($prev, $next, $child_number) ?>
273
						<?= Functions::getCloseRelationshipName($controller->record, $person) ?>
274
					</th>
275
					<td class="border-0 p-0">
276
						<?= Theme::theme()->individualBoxLarge($person) ?>
277
					</td>
278
				</tr>
279
				<?php
280
				$prev = $next;
281
			}
282
		}
283
		// Re-order children / add a new child
284
		if ($family->canEdit()) {
285
			if ($type == 'FAMS') {
286
				$add_child_text = I18N::translate('Add a son or daughter');
287
			} else {
288
				$add_child_text = I18N::translate('Add a brother or sister');
289
			}
290
			?>
291
			<tr>
292
				<th scope="row">
293 View Code Duplication
					<?php if (count($family->getChildren()) > 1): ?>
294
					<a href="edit_interface.php?action=reorder-children&amp;ged=<?= $family->getTree()->getNameHtml() ?>&amp;xref=<?= $family->getXref() ?>">
295
						<i class="icon-media-shuffle"></i> <?= I18N::translate('Re-order children') ?>
296
					</a>
297
					<?php endif; ?>
298
				</th>
299
				<td>
300
					<a href="edit_interface.php?action=add_child_to_family&amp;ged=<?= $family->getTree()->getNameHtml() ?>&amp;xref=<?= $family->getXref() ?>&amp;gender=U">
301
						<?= $add_child_text ?>
302
					</a>
303
					<span style='white-space:nowrap;'>
304
						<a href="edit_interface.php?action=add_child_to_family&amp;ged=<?= $family->getTree()->getNameHtml() ?>&amp;xref=<?= $family->getXref() ?>&amp;gender=M" class="icon-sex_m_15x15"></a>
305
						<a href="edit_interface.php?action=add_child_to_family&amp;ged=<?= $family->getTree()->getNameHtml() ?>&amp;xref=<?= $family->getXref() ?>&amp;gender=F" class="icon-sex_f_15x15"></a>
306
					</span>
307
				</td>
308
			</tr>
309
			<?php
310
		}
311
312
		echo '</tbody>';
313
		echo '</table>';
314
	}
315
316
	/** {@inheritdoc} */
317
	public function getTabContent() {
318
		global $controller;
319
320
		ob_start();
321
		?>
322
		<table class="table table-sm wt-facts-table" role="presentation">
323
			<tbody>
324
				<tr>
325
					<td>
326
						<label>
327
							<input id="show-date-differences" type="checkbox" checked>
328
							<?= I18N::translate('Date differences') ?>
329
						</label>
330
					</td>
331
				</tr>
332
			</tbody>
333
		</table>
334
		<?php
335
		$families = $controller->record->getChildFamilies();
336 View Code Duplication
		if (!$families && $controller->record->canEdit()) {
337
			?>
338
			<table class="table table-sm wt-facts-table">
339
				<tbody>
340
					<tr>
341
						<td>
342
							<a href="edit_interface.php?action=add_parent_to_individual&amp;ged=<?= $controller->record->getTree()->getNameHtml() ?>&amp;xref=<?= $controller->record->getXref() ?>&amp;gender=M">
343
								<?= I18N::translate('Add a father') ?>
344
							</a>
345
						</td>
346
					</tr>
347
					<tr>
348
						<td>
349
							<a href="edit_interface.php?action=add_parent_to_individual&amp;ged=<?= $controller->record->getTree()->getNameHtml() ?>&amp;xref=<?= $controller->record->getXref() ?>&amp;gender=F">
350
								<?= I18N::translate('Add a mother') ?>
351
							</a>
352
						</td>
353
					</tr>
354
				</tbody>
355
			</table>
356
			<?php
357
		}
358
359
		// parents
360
		foreach ($families as $family) {
361
			$this->printFamily($family, 'FAMC', $controller->record->getChildFamilyLabel($family));
362
		}
363
364
		// step-parents
365
		foreach ($controller->record->getChildStepFamilies() as $family) {
366
			$this->printFamily($family, 'FAMC', $controller->record->getStepFamilyLabel($family));
367
		}
368
369
		// spouses
370
		$families = $controller->record->getSpouseFamilies();
371
		foreach ($families as $family) {
372
			$this->printFamily($family, 'FAMS', $controller->getSpouseFamilyLabel($family, $controller->record));
373
		}
374
375
		// step-children
376
		foreach ($controller->record->getSpouseStepFamilies() as $family) {
377
			$this->printFamily($family, 'FAMS', $family->getFullName());
378
		}
379
380
		if ($controller->record->canEdit()) {
381
		?>
382
		<br>
383
		<table class="table table-sm wt-facts-table">
384
			<tbody>
385 View Code Duplication
				<?php if (count($families) > 1) { ?>
386
				<tr>
387
					<td>
388
						<a href="edit_interface.php?action=reorder-spouses&amp;ged=<?= $controller->record->getTree()->getNameHtml() ?>&amp;xref=<?= $controller->record->getXref() ?>">
389
						<?= I18N::translate('Re-order families') ?>
390
						</a>
391
					</td>
392
				</tr>
393
			<?php } ?>
394
				<tr>
395
					<td>
396
					<a href="edit_interface.php?action=addfamlink&amp;ged=<?= $controller->record->getTree()->getNameHtml() ?>&amp;xref=<?= $controller->record->getXref() ?>"><?= I18N::translate('Link this individual to an existing family as a child') ?></a>
397
					</td>
398
				</tr>
399 View Code Duplication
				<?php if ($controller->record->getSex() !== 'F') { ?>
400
				<tr>
401
					<td>
402
					<a href="edit_interface.php?action=add_spouse_to_individual&amp;ged=<?= $controller->record->getTree()->getNameHtml() ?>&amp;xref=<?= $controller->record->getXref() ?>&amp;sex=F"><?= I18N::translate('Add a wife') ?></a>
403
					</td>
404
				</tr>
405
				<tr>
406
					<td>
407
					<a href="edit_interface.php?action=linkspouse&amp;ged=<?= $controller->record->getTree()->getNameHtml() ?>&amp;xref=<?= $controller->record->getXref() ?>&amp;famtag=WIFE"><?= I18N::translate('Add a wife using an existing individual') ?></a>
408
					</td>
409
				</tr>
410
				<?php } ?>
411 View Code Duplication
				<?php if ($controller->record->getSex() !== 'M') { ?>
412
				<tr>
413
					<td>
414
					<a href="edit_interface.php?action=add_spouse_to_individual&amp;ged=<?= $controller->record->getTree()->getNameHtml() ?>&amp;xref=<?= $controller->record->getXref() ?>&amp;sex=M"><?= I18N::translate('Add a husband') ?></a>
415
					</td>
416
				</tr>
417
				<tr>
418
					<td>
419
					<a href="edit_interface.php?action=linkspouse&amp;ged=<?= $controller->record->getTree()->getNameHtml() ?>&amp;xref=<?= $controller->record->getXref() ?>&amp;famtag=HUSB"><?= I18N::translate('Add a husband using an existing individual') ?></a>
420
					</td>
421
				</tr>
422
				<?php } ?>
423
				<tr>
424
					<td>
425
						<a href="edit_interface.php?action=add_child_to_individual&amp;ged=<?= $controller->record->getTree()->getNameHtml() ?>&amp;xref=<?= $controller->record->getXref() ?>&amp;gender=U">
426
							<?= I18N::translate('Add a child to create a one-parent family') ?>
427
						</a>
428
					</td>
429
				</tr>
430
			</tbody>
431
		</table>
432
		<?php } ?>
433
		<br>
434
		<script>
435
			//persistent_toggle("show-date-differences", ".elderdate");
436
		</script>
437
		<?php
438
439
		return '<div id="' . $this->getName() . '_content">' . ob_get_clean() . '</div>';
440
	}
441
442
	/** {@inheritdoc} */
443
	public function hasTabContent() {
444
		return true;
445
	}
446
	/** {@inheritdoc} */
447
	public function isGrayedOut() {
448
		return false;
449
	}
450
	/** {@inheritdoc} */
451
	public function canLoadAjax() {
452
		return false;
453
	}
454
455
	/** {@inheritdoc} */
456
	public function getPreLoadContent() {
457
		return '';
458
	}
459
}
460