1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
|
<?php
// Class that defines an event details object
//
// webtrees: Web based Family History software
// Copyright (C) 2013 webtrees development team.
//
// Derived from PhpGedView
// Copyright (C) 2008 PGV Development Team. All rights reserved.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// $Id$
if (!defined('WT_WEBTREES')) {
header('HTTP/1.0 403 Forbidden');
exit;
}
class WT_Fact {
private $fact_id = null; // Unique identifier for this fact
private $parent = null; // The GEDCOM record from which this fact is taken.
private $gedcom = null; // The raw GEDCOM data for this fact
private $tag = null; // The GEDCOM tag for this record
private $is_old = false; // Is this a pending record?
private $is_new = false; // Is this a pending record?
private $date = null; // The WT_Date object for the "2 DATE ..." attribute
private $place = null; // The WT_Place object for the "2 PLAC ..." attribute
// Temporary(!) variables that are used by other scripts
public $temp = null; // Timeline controller
public $sortOrder = 0; // sort_facts()
// Create an event objects from a gedcom fragment.
// We need the parent object (to check privacy) and a (pseudo) fact ID to
// identify the fact within the record.
function __construct($gedcom, WT_GedcomRecord $parent, $fact_id) {
if (preg_match('/^1 ('.WT_REGEX_TAG.')/', $gedcom, $match)) {
$this->gedcom = $gedcom;
$this->parent = $parent;
$this->fact_id = $fact_id;
$this->tag = $match[1];
} else {
// TODO need to rewrite code that passes dummy data to this function
//throw new Exception('Invalid GEDCOM data passed to WT_Fact::_construct('.$gedcom.')');
}
}
// Get the value of level 1 data in the fact
// Allow for multi-line values
function getValue() {
if (preg_match('/^1 (?:' . $this->tag . ') ?(.*(?:(?:\n2 CONT .*)*))/', $this->gedcom, $match)) {
return str_replace("\n2 CONT ", "\n", $match[1]);
} else {
return null;
}
}
// Get the record to which this fact links
function getTarget() {
$xref = trim($this->getValue(), '@');
switch ($this->tag) {
case 'FAMC':
case 'FAMS':
return WT_Family::getInstance($xref);
case 'HUSB':
case 'WIFE':
case 'CHIL':
return WT_Individual::getInstance($xref);
case 'SOUR':
return WT_Source::getInstance($xref);
case 'OBJE':
return WT_Media::getInstance($xref);
case 'REPO':
return WT_Repository::getInstance($xref);
case 'NOTE':
return WT_Note::getInstance($xref);
default:
return WT_GedcomRecord::getInstance($xref);
}
}
// Get the value of level 2 data in the fact
function getAttribute($tag) {
if (preg_match('/\n2 (?:' . $tag . ') ?(.*(?:(?:\n3 CONT .*)*)*)/', $this->gedcom, $match)) {
return str_replace("\n3 CONT ", "\n", $match[1]);
} else {
return null;
}
}
// Do the privacy rules allow us to display this fact to the current user
function canShow($access_level=WT_USER_ACCESS_LEVEL) {
// TODO - use the privacy settings for $this->gedcom_id, not the default gedcom.
global $person_facts, $global_facts;
// Does this record have an explicit RESN?
if (strpos($this->gedcom, "\n2 RESN confidential")) {
return WT_PRIV_NONE >= $access_level;
}
if (strpos($this->gedcom, "\n2 RESN privacy")) {
return WT_PRIV_USER >= $access_level;
}
if (strpos($this->gedcom, "\n2 RESN none")) {
return true;
}
// Does this record have a default RESN?
if (!$this->parent) die($eek);
$xref = $this->parent->getXref();
if (isset($person_facts[$xref][$this->tag])) {
return $person_facts[$xref][$this->tag] >= $access_level;
}
if (isset($global_facts[$this->tag])) {
return $global_facts[$this->tag] >= $access_level;
}
// No restrictions - it must be public
return true;
}
// Check whether this fact is protected against edit
public function canEdit() {
// Managers can edit anything
// Members cannot edit RESN, CHAN and locked records
return
$this->parent->canEdit() && !$this->isOld() && (
WT_USER_GEDCOM_ADMIN ||
WT_USER_CAN_EDIT && strpos($this->gedcom, "\n2 RESN locked")===false && $this->getTag()!='RESN' && $this->getTag()!='CHAN'
);
}
// The place where the event occured.
function getPlace() {
if ($this->place === null) {
$this->place = $this->getAttribute('PLAC');
}
return $this->place;
}
// We can call this function many times, especially when sorting,
// so keep a copy of the date.
function getDate() {
if ($this->date === null) {
$this->date = new WT_Date($this->getAttribute('DATE'));
}
return $this->date;
}
// The raw GEDCOM data for this fact
function getGedcom() {
return $this->gedcom;
}
// Unique identifier for the fact
function getFactId() {
return $this->fact_id;
}
// What sort of fact is this?
function getTag() {
return $this->tag;
}
// Used to convert a real fact (e.g. BIRT) into a close-relative’s fact (e.g. _BIRT_CHIL)
function setTag($tag) {
$this->tag = $tag;
}
// The Person/Family record where this WT_Fact came from
function getParent() {
return $this->parent;
}
function getLabel($abbreviate=false) {
if ($abbreviate) {
return WT_Gedcom_Tag::getAbbreviation($this->tag);
} else {
switch($this->tag) {
case 'EVEN':
case 'FACT':
if ($this->getType()) {
// Custom FACT/EVEN - with a TYPE
return WT_I18N::translate(htmlspecialchars($this->type));
}
// no break - drop into next case
default:
return WT_Gedcom_Tag::getLabel($this->tag, $this->parent);
}
}
}
// Is this a pending edit?
public function setIsOld() {
$this->is_old = true;
$this->is_new = false;
}
public function isOld() {
return $this->is_old;
}
public function setIsNew() {
$this->is_new = true;
$this->is_old = false;
}
public function isNew() {
return $this->is_new;
}
// Print a simple fact version of this event
function print_simple_fact($return=false, $anchor=false) {
global $ABBREVIATE_CHART_LABELS;
$value = $this->getValue();
$data = '<span class="details_label">'.$this->getLabel($ABBREVIATE_CHART_LABELS).'</span>';
// Don't display "yes", because format_fact_date() does this for us. (Should it?)
if ($value && $value != 'Y') {
$data .= ' <span dir="auto">' . htmlspecialchars($value) . '</span>';
}
$data .= ' '.format_fact_date($this, $this->getParent(), $anchor, false);
$data .= ' '.format_fact_place($this, $anchor, false, false);
$data .= '<br>';
if ($return) {
return $data;
} else {
echo $data;
}
}
// Display an icon for this fact.
// Icons are held in a theme subfolder. Not all themes provide icons.
function Icon() {
$dir=WT_THEME_DIR.'images/facts/';
$tag=$this->getTag();
$file=$tag.'.png';
if (file_exists($dir.$file)) {
return '<img src="'.WT_STATIC_URL.$dir.$file.'" title="'.WT_Gedcom_Tag::getLabel($tag).'">';
} elseif (file_exists($dir.'NULL.png')) {
// Spacer image - for alignment - until we move to a sprite.
return '<img src="'.WT_STATIC_URL.$dir.'NULL.png">';
} else {
return '';
}
}
// Static Helper functions to sort events
static function CompareDate($a, $b) {
if ($a->getDate()->isOK() && $b->getDate()->isOK()) {
// If both events have dates, compare by date
$ret=WT_Date::Compare($a->getDate(), $b->getDate());
if ($ret==0) {
// If dates are the same, compare by fact type
$ret=self::CompareType($a, $b);
// If the fact type is also the same, retain the initial order
if ($ret==0) {
$ret=$a->sortOrder - $b->sortOrder;
}
}
return $ret;
} else {
// One or both events have no date - retain the initial orde
return $a->sortOrder - $b->sortOrder;
}
}
// Static method to Compare two events by their type
static function CompareType($a, $b) {
global $factsort;
if (empty($factsort))
$factsort=array_flip(array(
"BIRT",
"_HNM",
"ALIA", "_AKA", "_AKAN",
"ADOP", "_ADPF", "_ADPF",
"_BRTM",
"CHR", "BAPM",
"FCOM",
"CONF",
"BARM", "BASM",
"EDUC",
"GRAD",
"_DEG",
"EMIG", "IMMI",
"NATU",
"_MILI", "_MILT",
"ENGA",
"MARB", "MARC", "MARL", "_MARI", "_MBON",
"MARR", "MARR_CIVIL", "MARR_RELIGIOUS", "MARR_PARTNERS", "MARR_UNKNOWN", "_COML",
"_STAT",
"_SEPR",
"DIVF",
"MARS",
"_BIRT_CHIL",
"DIV", "ANUL",
"_BIRT_", "_MARR_", "_DEAT_","_BURI_", // other events of close relatives
"CENS",
"OCCU",
"RESI",
"PROP",
"CHRA",
"RETI",
"FACT", "EVEN",
"_NMR", "_NMAR", "NMR",
"NCHI",
"WILL",
"_HOL",
"_????_",
"DEAT",
"_FNRL", "BURI", "CREM", "_INTE",
"_YART",
"_NLIV",
"PROB",
"TITL",
"COMM",
"NATI",
"CITN",
"CAST",
"RELI",
"SSN", "IDNO",
"TEMP",
"SLGC", "BAPL", "CONL", "ENDL", "SLGS",
"ADDR", "PHON", "EMAIL", "_EMAIL", "EMAL", "FAX", "WWW", "URL", "_URL",
"AFN", "REFN", "_PRMN", "REF", "RIN", "_UID",
"CHAN", "_TODO",
"NOTE", "SOUR", "OBJE"
));
// Facts from same families stay grouped together
// Keep MARR and DIV from the same families from mixing with events from other FAMs
// Use the original order in which the facts were added
if ($a->parent instanceof WT_Family && $b->parent instanceof WT_Family && $a->parent !== $b->parent) {
return $a->sortOrder - $b->sortOrder;
}
$atag = $a->getTag();
$btag = $b->getTag();
// Events not in the above list get mapped onto one that is.
if (!array_key_exists($atag, $factsort)) {
if (preg_match('/^(_(BIRT|MARR|DEAT|BURI)_)/', $atag, $match)) {
$atag=$match[1];
} else {
$atag="_????_";
}
}
if (!array_key_exists($btag, $factsort)) {
if (preg_match('/^(_(BIRT|MARR|DEAT|BURI)_)/', $btag, $match)) {
$btag=$match[1];
} else {
$btag="_????_";
}
}
//-- don't let dated after DEAT/BURI facts sort non-dated facts before DEAT/BURI
//-- treat dated after BURI facts as BURI instead
if ($a->getAttribute('DATE')!=NULL && $factsort[$atag]>$factsort['BURI'] && $factsort[$atag]<$factsort['CHAN']) $atag='BURI';
if ($b->getAttribute('DATE')!=NULL && $factsort[$btag]>$factsort['BURI'] && $factsort[$btag]<$factsort['CHAN']) $btag='BURI';
$ret = $factsort[$atag]-$factsort[$btag];
//-- if facts are the same then put dated facts before non-dated facts
if ($ret==0) {
if ($a->getAttribute('DATE')!=NULL && $b->getAttribute('DATE')==NULL) return -1;
if ($b->getAttribute('DATE')!=NULL && $a->getAttribute('DATE')==NULL) return 1;
//-- if no sorting preference, then keep original ordering
$ret = $a->sortOrder - $b->sortOrder;
}
return $ret;
}
}
|