Completed
Push — 1.7 ( ce7091...0d97cf )
by Greg
14:14 queued 07:41
created
app/Database.php 3 patches
Spacing   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -85,8 +85,7 @@
 block discarded – undo
85 85
 		// Create the underlying PDO object
86 86
 		self::$pdo = new PDO(
87 87
 			(substr($DBHOST, 0, 1) === '/' ?
88
-				"mysql:unix_socket={$DBHOST};dbname={$DBNAME}" :
89
-				"mysql:host={$DBHOST};dbname={$DBNAME};port={$DBPORT}"
88
+				"mysql:unix_socket={$DBHOST};dbname={$DBNAME}" : "mysql:host={$DBHOST};dbname={$DBNAME};port={$DBPORT}"
90 89
 			),
91 90
 			$DBUSER, $DBPASS,
92 91
 			array(
Please login to merge, or discard this patch.
Indentation   +268 added lines, -268 removed lines patch added patch discarded remove patch
@@ -23,299 +23,299 @@
 block discarded – undo
23 23
  * Extend PHP's native PDO class.
24 24
  */
25 25
 class Database {
26
-	/** @var Database Implement the singleton pattern */
27
-	private static $instance;
26
+    /** @var Database Implement the singleton pattern */
27
+    private static $instance;
28 28
 
29
-	/** @var PDO Native PHP database driver */
30
-	private static $pdo;
29
+    /** @var PDO Native PHP database driver */
30
+    private static $pdo;
31 31
 
32
-	/** @var array Keep a log of all the SQL statements that we execute */
33
-	private static $log;
32
+    /** @var array Keep a log of all the SQL statements that we execute */
33
+    private static $log;
34 34
 
35
-	/** @var Statement[] Cache of prepared statements */
36
-	private static $prepared = array();
35
+    /** @var Statement[] Cache of prepared statements */
36
+    private static $prepared = array();
37 37
 
38
-	/**
39
-	 * Prevent instantiation via new Database
40
-	 */
41
-	private function __construct() {
42
-		self::$log = array();
43
-	}
38
+    /**
39
+     * Prevent instantiation via new Database
40
+     */
41
+    private function __construct() {
42
+        self::$log = array();
43
+    }
44 44
 
45
-	/**
46
-	 * Begin a transaction.
47
-	 *
48
-	 * @return bool
49
-	 */
50
-	public static function beginTransaction() {
51
-		return self::$pdo->beginTransaction();
52
-	}
45
+    /**
46
+     * Begin a transaction.
47
+     *
48
+     * @return bool
49
+     */
50
+    public static function beginTransaction() {
51
+        return self::$pdo->beginTransaction();
52
+    }
53 53
 
54
-	/**
55
-	 * Commit this transaction.
56
-	 *
57
-	 * @return bool
58
-	 */
59
-	public static function commit() {
60
-		return self::$pdo->commit();
61
-	}
54
+    /**
55
+     * Commit this transaction.
56
+     *
57
+     * @return bool
58
+     */
59
+    public static function commit() {
60
+        return self::$pdo->commit();
61
+    }
62 62
 
63
-	/**
64
-	 * Disconnect from the server, so we can connect to another one
65
-	 */
66
-	public static function disconnect() {
67
-		self::$pdo = null;
68
-	}
63
+    /**
64
+     * Disconnect from the server, so we can connect to another one
65
+     */
66
+    public static function disconnect() {
67
+        self::$pdo = null;
68
+    }
69 69
 
70
-	/**
71
-	 * Implement the singleton pattern, using a static accessor.
72
-	 *
73
-	 * @param string $DBHOST
74
-	 * @param string $DBPORT
75
-	 * @param string $DBNAME
76
-	 * @param string $DBUSER
77
-	 * @param string $DBPASS
78
-	 *
79
-	 * @throws \Exception
80
-	 */
81
-	public static function createInstance($DBHOST, $DBPORT, $DBNAME, $DBUSER, $DBPASS) {
82
-		if (self::$pdo instanceof PDO) {
83
-			throw new \Exception('Database::createInstance() can only be called once.');
84
-		}
85
-		// Create the underlying PDO object
86
-		self::$pdo = new PDO(
87
-			(substr($DBHOST, 0, 1) === '/' ?
88
-				"mysql:unix_socket={$DBHOST};dbname={$DBNAME}" :
89
-				"mysql:host={$DBHOST};dbname={$DBNAME};port={$DBPORT}"
90
-			),
91
-			$DBUSER, $DBPASS,
92
-			array(
93
-				PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
94
-				PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
95
-				PDO::ATTR_CASE               => PDO::CASE_LOWER,
96
-				PDO::ATTR_AUTOCOMMIT         => true,
97
-			)
98
-		);
99
-		self::$pdo->exec("SET NAMES UTF8");
100
-		self::$pdo->prepare("SET time_zone = :time_zone")->execute(array('time_zone' => date('P')));
70
+    /**
71
+     * Implement the singleton pattern, using a static accessor.
72
+     *
73
+     * @param string $DBHOST
74
+     * @param string $DBPORT
75
+     * @param string $DBNAME
76
+     * @param string $DBUSER
77
+     * @param string $DBPASS
78
+     *
79
+     * @throws \Exception
80
+     */
81
+    public static function createInstance($DBHOST, $DBPORT, $DBNAME, $DBUSER, $DBPASS) {
82
+        if (self::$pdo instanceof PDO) {
83
+            throw new \Exception('Database::createInstance() can only be called once.');
84
+        }
85
+        // Create the underlying PDO object
86
+        self::$pdo = new PDO(
87
+            (substr($DBHOST, 0, 1) === '/' ?
88
+                "mysql:unix_socket={$DBHOST};dbname={$DBNAME}" :
89
+                "mysql:host={$DBHOST};dbname={$DBNAME};port={$DBPORT}"
90
+            ),
91
+            $DBUSER, $DBPASS,
92
+            array(
93
+                PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
94
+                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
95
+                PDO::ATTR_CASE               => PDO::CASE_LOWER,
96
+                PDO::ATTR_AUTOCOMMIT         => true,
97
+            )
98
+        );
99
+        self::$pdo->exec("SET NAMES UTF8");
100
+        self::$pdo->prepare("SET time_zone = :time_zone")->execute(array('time_zone' => date('P')));
101 101
 
102
-		self::$instance = new self;
103
-	}
102
+        self::$instance = new self;
103
+    }
104 104
 
105
-	/**
106
-	 * We don't access $instance directly, only via query(), exec() and prepare()
107
-	 *
108
-	 * @throws \Exception
109
-	 *
110
-	 * @return Database
111
-	 */
112
-	public static function getInstance() {
113
-		if (self::$pdo instanceof PDO) {
114
-			return self::$instance;
115
-		} else {
116
-			throw new \Exception('createInstance() must be called before getInstance().');
117
-		}
118
-	}
105
+    /**
106
+     * We don't access $instance directly, only via query(), exec() and prepare()
107
+     *
108
+     * @throws \Exception
109
+     *
110
+     * @return Database
111
+     */
112
+    public static function getInstance() {
113
+        if (self::$pdo instanceof PDO) {
114
+            return self::$instance;
115
+        } else {
116
+            throw new \Exception('createInstance() must be called before getInstance().');
117
+        }
118
+    }
119 119
 
120
-	/**
121
-	 * Are we currently connected to a database?
122
-	 *
123
-	 * @return bool
124
-	 */
125
-	public static function isConnected() {
126
-		return self::$pdo instanceof PDO;
127
-	}
120
+    /**
121
+     * Are we currently connected to a database?
122
+     *
123
+     * @return bool
124
+     */
125
+    public static function isConnected() {
126
+        return self::$pdo instanceof PDO;
127
+    }
128 128
 
129
-	/**
130
-	 * Log the details of a query, for debugging and analysis.
131
-	 *
132
-	 * @param string   $query
133
-	 * @param int      $rows
134
-	 * @param float    $microtime
135
-	 * @param string[] $bind_variables
136
-	 */
137
-	public static function logQuery($query, $rows, $microtime, $bind_variables) {
138
-		if (WT_DEBUG_SQL) {
139
-			// Full logging
140
-			// Trace
141
-			$trace = debug_backtrace();
142
-			array_shift($trace);
143
-			array_shift($trace);
144
-			foreach ($trace as $n => $frame) {
145
-				if (isset($frame['file']) && isset($frame['line'])) {
146
-					$trace[$n] = basename($frame['file']) . ':' . $frame['line'] . ' ' . $frame['function'];
147
-				} else {
148
-					unset($trace[$n]);
149
-				}
150
-			}
151
-			$stack = '<abbr title="' . Filter::escapeHtml(implode(" / ", $trace)) . '">' . (count(self::$log) + 1) . '</abbr>';
152
-			// Bind variables
153
-			foreach ($bind_variables as $key => $value) {
154
-				if (is_null($value)) {
155
-					$value = 'NULL';
156
-				} elseif (!is_integer($value)) {
157
-					$value = '\'' . $value . '\'';
158
-				}
159
-				if (is_integer($key)) {
160
-					$query = preg_replace('/\?/', $value, $query, 1);
161
-				} else {
162
-					$query = str_replace(':' . $key, $value, $query);
163
-				}
164
-			}
165
-			// Highlight slow queries
166
-			$microtime *= 1000; // convert to milliseconds
167
-			if ($microtime > 1000) {
168
-				$microtime = sprintf('<span style="background-color: #ff0000;">%.3f</span>', $microtime);
169
-			} elseif ($microtime > 100) {
170
-				$microtime = sprintf('<span style="background-color: #ffa500;">%.3f</span>', $microtime);
171
-			} elseif ($microtime > 1) {
172
-				$microtime = sprintf('<span style="background-color: #ffff00;">%.3f</span>', $microtime);
173
-			} else {
174
-				$microtime = sprintf('%.3f', $microtime);
175
-			}
176
-			self::$log[] = "<tr><td>{$stack}</td><td>{$query}</td><td>{$rows}</td><td>{$microtime}</td></tr>";
177
-		} else {
178
-			// Just log query count for statistics
179
-			self::$log[] = true;
180
-		}
181
-	}
129
+    /**
130
+     * Log the details of a query, for debugging and analysis.
131
+     *
132
+     * @param string   $query
133
+     * @param int      $rows
134
+     * @param float    $microtime
135
+     * @param string[] $bind_variables
136
+     */
137
+    public static function logQuery($query, $rows, $microtime, $bind_variables) {
138
+        if (WT_DEBUG_SQL) {
139
+            // Full logging
140
+            // Trace
141
+            $trace = debug_backtrace();
142
+            array_shift($trace);
143
+            array_shift($trace);
144
+            foreach ($trace as $n => $frame) {
145
+                if (isset($frame['file']) && isset($frame['line'])) {
146
+                    $trace[$n] = basename($frame['file']) . ':' . $frame['line'] . ' ' . $frame['function'];
147
+                } else {
148
+                    unset($trace[$n]);
149
+                }
150
+            }
151
+            $stack = '<abbr title="' . Filter::escapeHtml(implode(" / ", $trace)) . '">' . (count(self::$log) + 1) . '</abbr>';
152
+            // Bind variables
153
+            foreach ($bind_variables as $key => $value) {
154
+                if (is_null($value)) {
155
+                    $value = 'NULL';
156
+                } elseif (!is_integer($value)) {
157
+                    $value = '\'' . $value . '\'';
158
+                }
159
+                if (is_integer($key)) {
160
+                    $query = preg_replace('/\?/', $value, $query, 1);
161
+                } else {
162
+                    $query = str_replace(':' . $key, $value, $query);
163
+                }
164
+            }
165
+            // Highlight slow queries
166
+            $microtime *= 1000; // convert to milliseconds
167
+            if ($microtime > 1000) {
168
+                $microtime = sprintf('<span style="background-color: #ff0000;">%.3f</span>', $microtime);
169
+            } elseif ($microtime > 100) {
170
+                $microtime = sprintf('<span style="background-color: #ffa500;">%.3f</span>', $microtime);
171
+            } elseif ($microtime > 1) {
172
+                $microtime = sprintf('<span style="background-color: #ffff00;">%.3f</span>', $microtime);
173
+            } else {
174
+                $microtime = sprintf('%.3f', $microtime);
175
+            }
176
+            self::$log[] = "<tr><td>{$stack}</td><td>{$query}</td><td>{$rows}</td><td>{$microtime}</td></tr>";
177
+        } else {
178
+            // Just log query count for statistics
179
+            self::$log[] = true;
180
+        }
181
+    }
182 182
 
183
-	/**
184
-	 * Determine the number of queries executed, for the page statistics.
185
-	 *
186
-	 * @return int
187
-	 */
188
-	public static function getQueryCount() {
189
-		return count(self::$log);
190
-	}
183
+    /**
184
+     * Determine the number of queries executed, for the page statistics.
185
+     *
186
+     * @return int
187
+     */
188
+    public static function getQueryCount() {
189
+        return count(self::$log);
190
+    }
191 191
 
192
-	/**
193
-	 * Convert the query log into an HTML table.
194
-	 *
195
-	 * @return string
196
-	 */
197
-	public static function getQueryLog() {
198
-		$html      = '<table border="1" style="table-layout: fixed; width: 960px;word-wrap: break-word;"><col span="3"><col align="char"><thead><tr><th>#</th><th style="width: 800px;">Query</th><th>Rows</th><th>Time (ms)</th></tr></thead><tbody>' . implode('', self::$log) . '</tbody></table>';
199
-		self::$log = array();
192
+    /**
193
+     * Convert the query log into an HTML table.
194
+     *
195
+     * @return string
196
+     */
197
+    public static function getQueryLog() {
198
+        $html      = '<table border="1" style="table-layout: fixed; width: 960px;word-wrap: break-word;"><col span="3"><col align="char"><thead><tr><th>#</th><th style="width: 800px;">Query</th><th>Rows</th><th>Time (ms)</th></tr></thead><tbody>' . implode('', self::$log) . '</tbody></table>';
199
+        self::$log = array();
200 200
 
201
-		return $html;
202
-	}
201
+        return $html;
202
+    }
203 203
 
204
-	/**
205
-	 * Determine the most recently created value of an AUTO_INCREMENT field.
206
-	 *
207
-	 * @return string
208
-	 */
209
-	public static function lastInsertId() {
210
-		return self::$pdo->lastInsertId();
211
-	}
204
+    /**
205
+     * Determine the most recently created value of an AUTO_INCREMENT field.
206
+     *
207
+     * @return string
208
+     */
209
+    public static function lastInsertId() {
210
+        return self::$pdo->lastInsertId();
211
+    }
212 212
 
213
-	/**
214
-	 * Quote a string for embedding in a MySQL statement.
215
-	 *
216
-	 * The native quote() function does not convert PHP nulls to DB nulls
217
-	 *
218
-	 * @param  string $string
219
-	 *
220
-	 * @return string
221
-	 *
222
-	 * @deprecated We should use bind-variables instead.
223
-	 */
224
-	public static function quote($string) {
225
-		if (is_null($string)) {
226
-			return 'NULL';
227
-		} else {
228
-			return self::$pdo->quote($string, PDO::PARAM_STR);
229
-		}
230
-	}
213
+    /**
214
+     * Quote a string for embedding in a MySQL statement.
215
+     *
216
+     * The native quote() function does not convert PHP nulls to DB nulls
217
+     *
218
+     * @param  string $string
219
+     *
220
+     * @return string
221
+     *
222
+     * @deprecated We should use bind-variables instead.
223
+     */
224
+    public static function quote($string) {
225
+        if (is_null($string)) {
226
+            return 'NULL';
227
+        } else {
228
+            return self::$pdo->quote($string, PDO::PARAM_STR);
229
+        }
230
+    }
231 231
 
232
-	/**
233
-	 * Execute an SQL statement, and log the result.
234
-	 *
235
-	 * @param string $sql The SQL statement to execute
236
-	 *
237
-	 * @return int The number of rows affected by this SQL query
238
-	 */
239
-	public static function exec($sql) {
240
-		$sql   = str_replace('##', WT_TBLPREFIX, $sql);
241
-		$start = microtime(true);
242
-		$rows  = self::$pdo->exec($sql);
243
-		$end   = microtime(true);
244
-		self::logQuery($sql, $rows, $end - $start, array());
232
+    /**
233
+     * Execute an SQL statement, and log the result.
234
+     *
235
+     * @param string $sql The SQL statement to execute
236
+     *
237
+     * @return int The number of rows affected by this SQL query
238
+     */
239
+    public static function exec($sql) {
240
+        $sql   = str_replace('##', WT_TBLPREFIX, $sql);
241
+        $start = microtime(true);
242
+        $rows  = self::$pdo->exec($sql);
243
+        $end   = microtime(true);
244
+        self::logQuery($sql, $rows, $end - $start, array());
245 245
 
246
-		return $rows;
247
-	}
246
+        return $rows;
247
+    }
248 248
 
249
-	/**
250
-	 * Prepare an SQL statement for execution.
251
-	 *
252
-	 * @param $sql
253
-	 *
254
-	 * @throws \Exception
255
-	 *
256
-	 * @return Statement
257
-	 */
258
-	public static function prepare($sql) {
259
-		if (!self::$pdo instanceof PDO) {
260
-			throw new \Exception("No Connection Established");
261
-		}
262
-		$sql = str_replace('##', WT_TBLPREFIX, $sql);
249
+    /**
250
+     * Prepare an SQL statement for execution.
251
+     *
252
+     * @param $sql
253
+     *
254
+     * @throws \Exception
255
+     *
256
+     * @return Statement
257
+     */
258
+    public static function prepare($sql) {
259
+        if (!self::$pdo instanceof PDO) {
260
+            throw new \Exception("No Connection Established");
261
+        }
262
+        $sql = str_replace('##', WT_TBLPREFIX, $sql);
263 263
 
264
-		$hash = md5($sql);
265
-		if (!array_key_exists($hash, self::$prepared)) {
266
-			self::$prepared[$hash] = new Statement(self::$pdo->prepare($sql));
267
-		}
264
+        $hash = md5($sql);
265
+        if (!array_key_exists($hash, self::$prepared)) {
266
+            self::$prepared[$hash] = new Statement(self::$pdo->prepare($sql));
267
+        }
268 268
 
269
-		return self::$prepared[$hash];
270
-	}
269
+        return self::$prepared[$hash];
270
+    }
271 271
 
272
-	/**
273
-	 * Roll back this transaction.
274
-	 *
275
-	 * @return bool
276
-	 */
277
-	public static function rollBack() {
278
-		return self::$pdo->rollBack();
279
-	}
272
+    /**
273
+     * Roll back this transaction.
274
+     *
275
+     * @return bool
276
+     */
277
+    public static function rollBack() {
278
+        return self::$pdo->rollBack();
279
+    }
280 280
 
281
-	/**
282
-	 * Run a series of scripts to bring the database schema up to date.
283
-	 *
284
-	 * @param string $namespace      Where to find our MigrationXXX classes
285
-	 * @param string $schema_name    Where to find our MigrationXXX classes
286
-	 * @param int    $target_version updade/downgrade to this version
287
-	 *
288
-	 * @throws PDOException
289
-	 *
290
-	 * @return bool  Were any updates applied
291
-	 */
292
-	public static function updateSchema($namespace, $schema_name, $target_version) {
293
-		try {
294
-			$current_version = (int) Site::getPreference($schema_name);
295
-		} catch (PDOException $e) {
296
-			// During initial installation, the site_preference table won’t exist.
297
-			$current_version = 0;
298
-		}
281
+    /**
282
+     * Run a series of scripts to bring the database schema up to date.
283
+     *
284
+     * @param string $namespace      Where to find our MigrationXXX classes
285
+     * @param string $schema_name    Where to find our MigrationXXX classes
286
+     * @param int    $target_version updade/downgrade to this version
287
+     *
288
+     * @throws PDOException
289
+     *
290
+     * @return bool  Were any updates applied
291
+     */
292
+    public static function updateSchema($namespace, $schema_name, $target_version) {
293
+        try {
294
+            $current_version = (int) Site::getPreference($schema_name);
295
+        } catch (PDOException $e) {
296
+            // During initial installation, the site_preference table won’t exist.
297
+            $current_version = 0;
298
+        }
299 299
 
300
-		$updates_applied = false;
300
+        $updates_applied = false;
301 301
 
302
-		try {
303
-			// Update the schema, one version at a time.
304
-			while ($current_version < $target_version) {
305
-				$class = $namespace . '\\Migration' . $current_version;
306
-				/** @var MigrationInterface $migration */
307
-				$migration = new $class;
308
-				$migration->upgrade();
309
-				Site::setPreference($schema_name, ++$current_version);
310
-				$updates_applied = true;
311
-			}
312
-		} catch (PDOException $ex) {
313
-			// The schema update scripts should never fail. If they do, there is no clean recovery.
314
-			FlashMessages::addMessage($ex->getMessage(), 'danger');
315
-			header('Location: ' . WT_BASE_URL . 'site-unavailable.php');
316
-			throw $ex;
317
-		}
302
+        try {
303
+            // Update the schema, one version at a time.
304
+            while ($current_version < $target_version) {
305
+                $class = $namespace . '\\Migration' . $current_version;
306
+                /** @var MigrationInterface $migration */
307
+                $migration = new $class;
308
+                $migration->upgrade();
309
+                Site::setPreference($schema_name, ++$current_version);
310
+                $updates_applied = true;
311
+            }
312
+        } catch (PDOException $ex) {
313
+            // The schema update scripts should never fail. If they do, there is no clean recovery.
314
+            FlashMessages::addMessage($ex->getMessage(), 'danger');
315
+            header('Location: ' . WT_BASE_URL . 'site-unavailable.php');
316
+            throw $ex;
317
+        }
318 318
 
319
-		return $updates_applied;
320
-	}
319
+        return $updates_applied;
320
+    }
321 321
 }
Please login to merge, or discard this patch.
Braces   +34 added lines, -17 removed lines patch added patch discarded remove patch
@@ -22,7 +22,8 @@  discard block
 block discarded – undo
22 22
 /**
23 23
  * Extend PHP's native PDO class.
24 24
  */
25
-class Database {
25
+class Database
26
+{
26 27
 	/** @var Database Implement the singleton pattern */
27 28
 	private static $instance;
28 29
 
@@ -38,7 +39,8 @@  discard block
 block discarded – undo
38 39
 	/**
39 40
 	 * Prevent instantiation via new Database
40 41
 	 */
41
-	private function __construct() {
42
+	private function __construct()
43
+	{
42 44
 		self::$log = array();
43 45
 	}
44 46
 
@@ -47,7 +49,8 @@  discard block
 block discarded – undo
47 49
 	 *
48 50
 	 * @return bool
49 51
 	 */
50
-	public static function beginTransaction() {
52
+	public static function beginTransaction()
53
+	{
51 54
 		return self::$pdo->beginTransaction();
52 55
 	}
53 56
 
@@ -56,14 +59,16 @@  discard block
 block discarded – undo
56 59
 	 *
57 60
 	 * @return bool
58 61
 	 */
59
-	public static function commit() {
62
+	public static function commit()
63
+	{
60 64
 		return self::$pdo->commit();
61 65
 	}
62 66
 
63 67
 	/**
64 68
 	 * Disconnect from the server, so we can connect to another one
65 69
 	 */
66
-	public static function disconnect() {
70
+	public static function disconnect()
71
+	{
67 72
 		self::$pdo = null;
68 73
 	}
69 74
 
@@ -78,7 +83,8 @@  discard block
 block discarded – undo
78 83
 	 *
79 84
 	 * @throws \Exception
80 85
 	 */
81
-	public static function createInstance($DBHOST, $DBPORT, $DBNAME, $DBUSER, $DBPASS) {
86
+	public static function createInstance($DBHOST, $DBPORT, $DBNAME, $DBUSER, $DBPASS)
87
+	{
82 88
 		if (self::$pdo instanceof PDO) {
83 89
 			throw new \Exception('Database::createInstance() can only be called once.');
84 90
 		}
@@ -109,7 +115,8 @@  discard block
 block discarded – undo
109 115
 	 *
110 116
 	 * @return Database
111 117
 	 */
112
-	public static function getInstance() {
118
+	public static function getInstance()
119
+	{
113 120
 		if (self::$pdo instanceof PDO) {
114 121
 			return self::$instance;
115 122
 		} else {
@@ -122,7 +129,8 @@  discard block
 block discarded – undo
122 129
 	 *
123 130
 	 * @return bool
124 131
 	 */
125
-	public static function isConnected() {
132
+	public static function isConnected()
133
+	{
126 134
 		return self::$pdo instanceof PDO;
127 135
 	}
128 136
 
@@ -134,7 +142,8 @@  discard block
 block discarded – undo
134 142
 	 * @param float    $microtime
135 143
 	 * @param string[] $bind_variables
136 144
 	 */
137
-	public static function logQuery($query, $rows, $microtime, $bind_variables) {
145
+	public static function logQuery($query, $rows, $microtime, $bind_variables)
146
+	{
138 147
 		if (WT_DEBUG_SQL) {
139 148
 			// Full logging
140 149
 			// Trace
@@ -185,7 +194,8 @@  discard block
 block discarded – undo
185 194
 	 *
186 195
 	 * @return int
187 196
 	 */
188
-	public static function getQueryCount() {
197
+	public static function getQueryCount()
198
+	{
189 199
 		return count(self::$log);
190 200
 	}
191 201
 
@@ -194,7 +204,8 @@  discard block
 block discarded – undo
194 204
 	 *
195 205
 	 * @return string
196 206
 	 */
197
-	public static function getQueryLog() {
207
+	public static function getQueryLog()
208
+	{
198 209
 		$html      = '<table border="1" style="table-layout: fixed; width: 960px;word-wrap: break-word;"><col span="3"><col align="char"><thead><tr><th>#</th><th style="width: 800px;">Query</th><th>Rows</th><th>Time (ms)</th></tr></thead><tbody>' . implode('', self::$log) . '</tbody></table>';
199 210
 		self::$log = array();
200 211
 
@@ -206,7 +217,8 @@  discard block
 block discarded – undo
206 217
 	 *
207 218
 	 * @return string
208 219
 	 */
209
-	public static function lastInsertId() {
220
+	public static function lastInsertId()
221
+	{
210 222
 		return self::$pdo->lastInsertId();
211 223
 	}
212 224
 
@@ -221,7 +233,8 @@  discard block
 block discarded – undo
221 233
 	 *
222 234
 	 * @deprecated We should use bind-variables instead.
223 235
 	 */
224
-	public static function quote($string) {
236
+	public static function quote($string)
237
+	{
225 238
 		if (is_null($string)) {
226 239
 			return 'NULL';
227 240
 		} else {
@@ -236,7 +249,8 @@  discard block
 block discarded – undo
236 249
 	 *
237 250
 	 * @return int The number of rows affected by this SQL query
238 251
 	 */
239
-	public static function exec($sql) {
252
+	public static function exec($sql)
253
+	{
240 254
 		$sql   = str_replace('##', WT_TBLPREFIX, $sql);
241 255
 		$start = microtime(true);
242 256
 		$rows  = self::$pdo->exec($sql);
@@ -255,7 +269,8 @@  discard block
 block discarded – undo
255 269
 	 *
256 270
 	 * @return Statement
257 271
 	 */
258
-	public static function prepare($sql) {
272
+	public static function prepare($sql)
273
+	{
259 274
 		if (!self::$pdo instanceof PDO) {
260 275
 			throw new \Exception("No Connection Established");
261 276
 		}
@@ -274,7 +289,8 @@  discard block
 block discarded – undo
274 289
 	 *
275 290
 	 * @return bool
276 291
 	 */
277
-	public static function rollBack() {
292
+	public static function rollBack()
293
+	{
278 294
 		return self::$pdo->rollBack();
279 295
 	}
280 296
 
@@ -289,7 +305,8 @@  discard block
 block discarded – undo
289 305
 	 *
290 306
 	 * @return bool  Were any updates applied
291 307
 	 */
292
-	public static function updateSchema($namespace, $schema_name, $target_version) {
308
+	public static function updateSchema($namespace, $schema_name, $target_version)
309
+	{
293 310
 		try {
294 311
 			$current_version = (int) Site::getPreference($schema_name);
295 312
 		} catch (PDOException $e) {
Please login to merge, or discard this patch.
app/File.php 3 patches
Upper-Lower-Casing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -47,7 +47,7 @@
 block discarded – undo
47 47
 		try {
48 48
 			$fp = fsockopen($scheme . $host, $port, $errno, $errstr, 5);
49 49
 
50
-			fputs($fp, "GET $path?$query HTTP/1.0\r\nHost: $host\r\nConnection: Close\r\n\r\n");
50
+			fputs($fp, "get $path?$query HTTP/1.0\r\nHost: $host\r\nConnection: Close\r\n\r\n");
51 51
 
52 52
 			// The first part of the response include the HTTP headers
53 53
 			$response = fread($fp, 65536);
Please login to merge, or discard this patch.
Indentation   +105 added lines, -105 removed lines patch added patch discarded remove patch
@@ -19,123 +19,123 @@
 block discarded – undo
19 19
  * File manipulation utilities.
20 20
  */
21 21
 class File {
22
-	/**
23
-	 * Fetch a remote file
24
-	 * Note that fopen() and file_get_contents() are often unvailable, as they
25
-	 * can easily be exploited by application bugs, and are therefore disabled.
26
-	 * Hence we use fsockopen().
27
-	 * To allow arbitrarily large downloads with small memory limits, we either
28
-	 * write output to a stream or return it.
29
-	 *
30
-	 * @param string        $url
31
-	 * @param resource|null $stream
32
-	 *
33
-	 * @return null|string
34
-	 */
35
-	public static function fetchUrl($url, $stream = null) {
36
-		$host  = parse_url($url, PHP_URL_HOST);
37
-		$port  = parse_url($url, PHP_URL_PORT);
38
-		$path  = parse_url($url, PHP_URL_PATH);
39
-		$query = parse_url($url, PHP_URL_QUERY);
22
+    /**
23
+     * Fetch a remote file
24
+     * Note that fopen() and file_get_contents() are often unvailable, as they
25
+     * can easily be exploited by application bugs, and are therefore disabled.
26
+     * Hence we use fsockopen().
27
+     * To allow arbitrarily large downloads with small memory limits, we either
28
+     * write output to a stream or return it.
29
+     *
30
+     * @param string        $url
31
+     * @param resource|null $stream
32
+     *
33
+     * @return null|string
34
+     */
35
+    public static function fetchUrl($url, $stream = null) {
36
+        $host  = parse_url($url, PHP_URL_HOST);
37
+        $port  = parse_url($url, PHP_URL_PORT);
38
+        $path  = parse_url($url, PHP_URL_PATH);
39
+        $query = parse_url($url, PHP_URL_QUERY);
40 40
 
41
-		if (!$port) {
42
-			$port = parse_url($url, PHP_URL_SCHEME) === 'https' ? 443 : 80;
43
-		}
41
+        if (!$port) {
42
+            $port = parse_url($url, PHP_URL_SCHEME) === 'https' ? 443 : 80;
43
+        }
44 44
 
45
-		$scheme = $port === 443 ? 'ssl://' : '';
45
+        $scheme = $port === 443 ? 'ssl://' : '';
46 46
 
47
-		try {
48
-			$fp = fsockopen($scheme . $host, $port, $errno, $errstr, 5);
47
+        try {
48
+            $fp = fsockopen($scheme . $host, $port, $errno, $errstr, 5);
49 49
 
50
-			fputs($fp, "GET $path?$query HTTP/1.0\r\nHost: $host\r\nConnection: Close\r\n\r\n");
50
+            fputs($fp, "GET $path?$query HTTP/1.0\r\nHost: $host\r\nConnection: Close\r\n\r\n");
51 51
 
52
-			// The first part of the response include the HTTP headers
53
-			$response = fread($fp, 65536);
52
+            // The first part of the response include the HTTP headers
53
+            $response = fread($fp, 65536);
54 54
 
55
-			// The file has moved? Follow it.
56
-			if (preg_match('/^HTTP\/1.[01] 30[123].+\nLocation: ([^\r\n]+)/s', $response, $match)) {
57
-				fclose($fp);
55
+            // The file has moved? Follow it.
56
+            if (preg_match('/^HTTP\/1.[01] 30[123].+\nLocation: ([^\r\n]+)/s', $response, $match)) {
57
+                fclose($fp);
58 58
 
59
-				return self::fetchUrl($match[1], $stream);
60
-			} else {
61
-				// The response includes headers, a blank line, then the content
62
-				$response = substr($response, strpos($response, "\r\n\r\n") + 4);
63
-			}
59
+                return self::fetchUrl($match[1], $stream);
60
+            } else {
61
+                // The response includes headers, a blank line, then the content
62
+                $response = substr($response, strpos($response, "\r\n\r\n") + 4);
63
+            }
64 64
 
65
-			if ($stream) {
66
-				fwrite($stream, $response);
67
-				while ($tmp = fread($fp, 8192)) {
68
-					fwrite($stream, $tmp);
69
-				}
70
-				fclose($fp);
65
+            if ($stream) {
66
+                fwrite($stream, $response);
67
+                while ($tmp = fread($fp, 8192)) {
68
+                    fwrite($stream, $tmp);
69
+                }
70
+                fclose($fp);
71 71
 
72
-				return null;
73
-			} else {
74
-				while ($tmp = fread($fp, 8192)) {
75
-					$response .= $tmp;
76
-				}
77
-				fclose($fp);
72
+                return null;
73
+            } else {
74
+                while ($tmp = fread($fp, 8192)) {
75
+                    $response .= $tmp;
76
+                }
77
+                fclose($fp);
78 78
 
79
-				return $response;
80
-			}
81
-		} catch (\ErrorException $ex) {
82
-			return null;
83
-		}
84
-	}
79
+                return $response;
80
+            }
81
+        } catch (\ErrorException $ex) {
82
+            return null;
83
+        }
84
+    }
85 85
 
86
-	/**
87
-	 * Recursively delete a folder or file
88
-	 *
89
-	 * @param string $path
90
-	 *
91
-	 * @return bool Was the file deleted
92
-	 */
93
-	public static function delete($path) {
94
-		if (is_dir($path)) {
95
-			$dir = opendir($path);
96
-			while ($dir !== false && (($file = readdir($dir)) !== false)) {
97
-				if ($file !== '.' && $file !== '..') {
98
-					self::delete($path . DIRECTORY_SEPARATOR . $file);
99
-				}
100
-			}
101
-			closedir($dir);
102
-			try {
103
-				rmdir($path);
104
-			} catch (\ErrorException $ex) {
105
-				// Continue, in case there are other files/folders that we can delete.
106
-			}
107
-		} else {
108
-			try {
109
-				unlink($path);
110
-			} catch (\ErrorException $ex) {
111
-				// Continue, in case there are other files/folders that we can delete.
112
-			}
113
-		}
86
+    /**
87
+     * Recursively delete a folder or file
88
+     *
89
+     * @param string $path
90
+     *
91
+     * @return bool Was the file deleted
92
+     */
93
+    public static function delete($path) {
94
+        if (is_dir($path)) {
95
+            $dir = opendir($path);
96
+            while ($dir !== false && (($file = readdir($dir)) !== false)) {
97
+                if ($file !== '.' && $file !== '..') {
98
+                    self::delete($path . DIRECTORY_SEPARATOR . $file);
99
+                }
100
+            }
101
+            closedir($dir);
102
+            try {
103
+                rmdir($path);
104
+            } catch (\ErrorException $ex) {
105
+                // Continue, in case there are other files/folders that we can delete.
106
+            }
107
+        } else {
108
+            try {
109
+                unlink($path);
110
+            } catch (\ErrorException $ex) {
111
+                // Continue, in case there are other files/folders that we can delete.
112
+            }
113
+        }
114 114
 
115
-		return !file_exists($path);
116
-	}
115
+        return !file_exists($path);
116
+    }
117 117
 
118
-	/**
119
-	 * Create a folder, and sub-folders, if it does not already exist
120
-	 *
121
-	 * @param string $path
122
-	 *
123
-	 * @return bool Does the folder now exist
124
-	 */
125
-	public static function mkdir($path) {
126
-		if (is_dir($path)) {
127
-			return true;
128
-		} else {
129
-			if (dirname($path) && !is_dir(dirname($path))) {
130
-				self::mkdir(dirname($path));
131
-			}
132
-			try {
133
-				mkdir($path);
118
+    /**
119
+     * Create a folder, and sub-folders, if it does not already exist
120
+     *
121
+     * @param string $path
122
+     *
123
+     * @return bool Does the folder now exist
124
+     */
125
+    public static function mkdir($path) {
126
+        if (is_dir($path)) {
127
+            return true;
128
+        } else {
129
+            if (dirname($path) && !is_dir(dirname($path))) {
130
+                self::mkdir(dirname($path));
131
+            }
132
+            try {
133
+                mkdir($path);
134 134
 
135
-				return true;
136
-			} catch (\ErrorException $ex) {
137
-				return false;
138
-			}
139
-		}
140
-	}
135
+                return true;
136
+            } catch (\ErrorException $ex) {
137
+                return false;
138
+            }
139
+        }
140
+    }
141 141
 }
Please login to merge, or discard this patch.
Braces   +8 added lines, -4 removed lines patch added patch discarded remove patch
@@ -18,7 +18,8 @@  discard block
 block discarded – undo
18 18
 /**
19 19
  * File manipulation utilities.
20 20
  */
21
-class File {
21
+class File
22
+{
22 23
 	/**
23 24
 	 * Fetch a remote file
24 25
 	 * Note that fopen() and file_get_contents() are often unvailable, as they
@@ -32,7 +33,8 @@  discard block
 block discarded – undo
32 33
 	 *
33 34
 	 * @return null|string
34 35
 	 */
35
-	public static function fetchUrl($url, $stream = null) {
36
+	public static function fetchUrl($url, $stream = null)
37
+	{
36 38
 		$host  = parse_url($url, PHP_URL_HOST);
37 39
 		$port  = parse_url($url, PHP_URL_PORT);
38 40
 		$path  = parse_url($url, PHP_URL_PATH);
@@ -90,7 +92,8 @@  discard block
 block discarded – undo
90 92
 	 *
91 93
 	 * @return bool Was the file deleted
92 94
 	 */
93
-	public static function delete($path) {
95
+	public static function delete($path)
96
+	{
94 97
 		if (is_dir($path)) {
95 98
 			$dir = opendir($path);
96 99
 			while ($dir !== false && (($file = readdir($dir)) !== false)) {
@@ -122,7 +125,8 @@  discard block
 block discarded – undo
122 125
 	 *
123 126
 	 * @return bool Does the folder now exist
124 127
 	 */
125
-	public static function mkdir($path) {
128
+	public static function mkdir($path)
129
+	{
126 130
 		if (is_dir($path)) {
127 131
 			return true;
128 132
 		} else {
Please login to merge, or discard this patch.
calendar.php 4 patches
Upper-Lower-Casing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -67,14 +67,14 @@
 block discarded – undo
67 67
 	if (strlen($match[1]) > strlen($match[2])) {
68 68
 		$match[2] = substr($match[1], 0, strlen($match[1]) - strlen($match[2])) . $match[2];
69 69
 	}
70
-	$ged_date = new Date("FROM {$cal} {$match[1]} TO {$cal} {$match[2]}");
70
+	$ged_date = new Date("from {$cal} {$match[1]} TO {$cal} {$match[2]}");
71 71
 	$view     = 'year';
72 72
 } else {
73 73
 	// advanced-year "decade/century wildcard"
74 74
 	if (preg_match('/^(\d+)(\?+)$/', $year, $match)) {
75 75
 		$y1       = $match[1] . str_replace('?', '0', $match[2]);
76 76
 		$y2       = $match[1] . str_replace('?', '9', $match[2]);
77
-		$ged_date = new Date("FROM {$cal} {$y1} TO {$cal} {$y2}");
77
+		$ged_date = new Date("from {$cal} {$y1} TO {$cal} {$y2}");
78 78
 		$view     = 'year';
79 79
 	} else {
80 80
 		if ($year < 0) {
Please login to merge, or discard this patch.
Indentation   +336 added lines, -336 removed lines patch added patch discarded remove patch
@@ -47,57 +47,57 @@  discard block
 block discarded – undo
47 47
 $filtersx = Filter::get('filtersx', '[MF]', '');
48 48
 
49 49
 if ($cal . $day . $month . $year === '') {
50
-	// No date specified? Use the most likely calendar
51
-	$cal = I18N::defaultCalendar()->gedcomCalendarEscape();
50
+    // No date specified? Use the most likely calendar
51
+    $cal = I18N::defaultCalendar()->gedcomCalendarEscape();
52 52
 }
53 53
 
54 54
 // Create a CalendarDate from the parameters
55 55
 
56 56
 // We cannot display new-style/old-style years, so convert to new style
57 57
 if (preg_match('/^(\d\d)\d\d\/(\d\d)$/', $year, $match)) {
58
-	$year = $match[1] . $match[2];
58
+    $year = $match[1] . $match[2];
59 59
 }
60 60
 
61 61
 // advanced-year "year range"
62 62
 if (preg_match('/^(\d+)-(\d+)$/', $year, $match)) {
63
-	if (strlen($match[1]) > strlen($match[2])) {
64
-		$match[2] = substr($match[1], 0, strlen($match[1]) - strlen($match[2])) . $match[2];
65
-	}
66
-	$ged_date = new Date("FROM {$cal} {$match[1]} TO {$cal} {$match[2]}");
67
-	$view     = 'year';
63
+    if (strlen($match[1]) > strlen($match[2])) {
64
+        $match[2] = substr($match[1], 0, strlen($match[1]) - strlen($match[2])) . $match[2];
65
+    }
66
+    $ged_date = new Date("FROM {$cal} {$match[1]} TO {$cal} {$match[2]}");
67
+    $view     = 'year';
68 68
 } else {
69
-	// advanced-year "decade/century wildcard"
70
-	if (preg_match('/^(\d+)(\?+)$/', $year, $match)) {
71
-		$y1       = $match[1] . str_replace('?', '0', $match[2]);
72
-		$y2       = $match[1] . str_replace('?', '9', $match[2]);
73
-		$ged_date = new Date("FROM {$cal} {$y1} TO {$cal} {$y2}");
74
-		$view     = 'year';
75
-	} else {
76
-		if ($year < 0) {
77
-			$year = (-$year) . ' B.C.';
78
-		} // need BC to parse date
79
-		$ged_date = new Date("{$cal} {$day} {$month} {$year}");
80
-		$year     = $ged_date->minimumDate()->y; // need negative year for year entry field.
81
-	}
69
+    // advanced-year "decade/century wildcard"
70
+    if (preg_match('/^(\d+)(\?+)$/', $year, $match)) {
71
+        $y1       = $match[1] . str_replace('?', '0', $match[2]);
72
+        $y2       = $match[1] . str_replace('?', '9', $match[2]);
73
+        $ged_date = new Date("FROM {$cal} {$y1} TO {$cal} {$y2}");
74
+        $view     = 'year';
75
+    } else {
76
+        if ($year < 0) {
77
+            $year = (-$year) . ' B.C.';
78
+        } // need BC to parse date
79
+        $ged_date = new Date("{$cal} {$day} {$month} {$year}");
80
+        $year     = $ged_date->minimumDate()->y; // need negative year for year entry field.
81
+    }
82 82
 }
83 83
 $cal_date = $ged_date->minimumDate();
84 84
 
85 85
 // Fill in any missing bits with todays date
86 86
 $today = $cal_date->today();
87 87
 if ($cal_date->d === 0) {
88
-	$cal_date->d = $today->d;
88
+    $cal_date->d = $today->d;
89 89
 }
90 90
 if ($cal_date->m === 0) {
91
-	$cal_date->m = $today->m;
91
+    $cal_date->m = $today->m;
92 92
 }
93 93
 if ($cal_date->y === 0) {
94
-	$cal_date->y = $today->y;
94
+    $cal_date->y = $today->y;
95 95
 }
96 96
 
97 97
 $cal_date->setJdFromYmd();
98 98
 
99 99
 if ($year === 0) {
100
-	$year = $cal_date->y;
100
+    $year = $cal_date->y;
101 101
 }
102 102
 
103 103
 // Extract values from date
@@ -108,7 +108,7 @@  discard block
 block discarded – undo
108 108
 
109 109
 // Invalid dates? Go to monthly view, where they'll be found.
110 110
 if ($cal_date->d > $days_in_month && $view === 'day') {
111
-	$view = 'month';
111
+    $view = 'month';
112 112
 }
113 113
 
114 114
 // All further uses of $cal are to generate URLs
@@ -119,14 +119,14 @@  discard block
 block discarded – undo
119 119
 
120 120
 switch ($view) {
121 121
 case 'day':
122
-	$controller->setPageTitle(I18N::translate('On this day…') . ' ' . $ged_date->display(false));
123
-	break;
122
+    $controller->setPageTitle(I18N::translate('On this day…') . ' ' . $ged_date->display(false));
123
+    break;
124 124
 case 'month':
125
-	$controller->setPageTitle(I18N::translate('In this month…') . ' ' . $ged_date->display(false, '%F %Y'));
126
-	break;
125
+    $controller->setPageTitle(I18N::translate('In this month…') . ' ' . $ged_date->display(false, '%F %Y'));
126
+    break;
127 127
 case 'year':
128
-	$controller->setPageTitle(I18N::translate('In this year…') . ' ' . $ged_date->display(false, '%Y'));
129
-	break;
128
+    $controller->setPageTitle(I18N::translate('In this year…') . ' ' . $ged_date->display(false, '%Y'));
129
+    break;
130 130
 }
131 131
 
132 132
 $controller->pageHeader();
@@ -152,18 +152,18 @@  discard block
 block discarded – undo
152 152
 				</td>
153 153
 				<td colspan="3" class="optionbox">
154 154
 					<?php
155
-					for ($d = 1; $d <= $days_in_month; $d++) {
156
-						// Format the day number using the calendar
157
-						$tmp   = new Date($cal_date->format("%@ {$d} %O %E"));
158
-						$d_fmt = $tmp->minimumDate()->format('%j');
159
-						if ($d === $cal_date->d) {
160
-							echo '<span class="error">', $d_fmt, '</span>';
161
-						} else {
162
-							echo '<a href="?cal=', $cal, '&amp;day=', $d, '&amp;month=', $cal_month, '&amp;year=', $cal_date->y, '&amp;filterev=', $filterev, '&amp;filterof=', $filterof, '&amp;filtersx=', $filtersx, '&amp;view=', $view, '">', $d_fmt, '</a>';
163
-						}
164
-						echo ' | ';
165
-					}
166
-					?>
155
+                    for ($d = 1; $d <= $days_in_month; $d++) {
156
+                        // Format the day number using the calendar
157
+                        $tmp   = new Date($cal_date->format("%@ {$d} %O %E"));
158
+                        $d_fmt = $tmp->minimumDate()->format('%j');
159
+                        if ($d === $cal_date->d) {
160
+                            echo '<span class="error">', $d_fmt, '</span>';
161
+                        } else {
162
+                            echo '<a href="?cal=', $cal, '&amp;day=', $d, '&amp;month=', $cal_month, '&amp;year=', $cal_date->y, '&amp;filterev=', $filterev, '&amp;filterof=', $filterof, '&amp;filtersx=', $filtersx, '&amp;view=', $view, '">', $d_fmt, '</a>';
163
+                        }
164
+                        echo ' | ';
165
+                    }
166
+                    ?>
167 167
 					<a href="?cal=<?php echo $cal ?>&amp;day=<?php echo $today->d ?>&amp;month=<?php echo $today_month ?>&amp;year=<?php echo $today->y ?>&amp;filterev=<?php echo $filterev ?>&amp;filterof=<?php echo $filterof ?>&amp;filtersx=<?php echo $filtersx ?>&amp;view=<?php echo $view ?>">
168 168
 						<b><?php $tmp = new Date($today->format('%@ %A %O %E')); echo $tmp->display() ?></b>
169 169
 					</a>
@@ -175,24 +175,24 @@  discard block
 block discarded – undo
175 175
 				</td>
176 176
 				<td class="optionbox" colspan="3">
177 177
 					<?php
178
-					for ($n = 1, $months_in_year = $cal_date->monthsInYear(); $n <= $months_in_year; ++$n) {
179
-						$month_name = $cal_date->monthNameNominativeCase($n, $cal_date->isLeapYear());
180
-						$m          = array_search($n, $cal_date::$MONTH_ABBREV);
181
-						if ($n === 6 && $cal_date instanceof JewishDate && !$cal_date->isLeapYear()) {
182
-							// No month 6 in Jewish non-leap years.
183
-							continue;
184
-						}
185
-						if ($n === 7 && $cal_date instanceof JewishDate && !$cal_date->isLeapYear()) {
186
-							// Month 7 is ADR in Jewish non-leap years.
187
-							$m = 'ADR';
188
-						}
189
-						if ($n === $cal_date->m) {
190
-							$month_name = '<span class="error">' . $month_name . '</span>';
191
-						}
192
-						echo '<a href="?cal=', $cal, '&amp;day=', $cal_date->d, '&amp;month=', $m, '&amp;year=', $cal_date->y, '&amp;filterev=', $filterev, '&amp;filterof=', $filterof, '&amp;filtersx=', $filtersx, '&amp;view=', $view, '">', $month_name, '</a>';
193
-						echo ' | ';
194
-					}
195
-					?>
178
+                    for ($n = 1, $months_in_year = $cal_date->monthsInYear(); $n <= $months_in_year; ++$n) {
179
+                        $month_name = $cal_date->monthNameNominativeCase($n, $cal_date->isLeapYear());
180
+                        $m          = array_search($n, $cal_date::$MONTH_ABBREV);
181
+                        if ($n === 6 && $cal_date instanceof JewishDate && !$cal_date->isLeapYear()) {
182
+                            // No month 6 in Jewish non-leap years.
183
+                            continue;
184
+                        }
185
+                        if ($n === 7 && $cal_date instanceof JewishDate && !$cal_date->isLeapYear()) {
186
+                            // Month 7 is ADR in Jewish non-leap years.
187
+                            $m = 'ADR';
188
+                        }
189
+                        if ($n === $cal_date->m) {
190
+                            $month_name = '<span class="error">' . $month_name . '</span>';
191
+                        }
192
+                        echo '<a href="?cal=', $cal, '&amp;day=', $cal_date->d, '&amp;month=', $m, '&amp;year=', $cal_date->y, '&amp;filterev=', $filterev, '&amp;filterof=', $filterof, '&amp;filtersx=', $filtersx, '&amp;view=', $view, '">', $month_name, '</a>';
193
+                        echo ' | ';
194
+                    }
195
+                    ?>
196 196
 					<a href="?cal=<?php echo $cal ?>&amp;day=<?php echo min($cal_date->d, $today->daysInMonth()) ?>&amp;month=<?php echo $today_month ?>&amp;year=<?php echo $today->y ?>&amp;filterev=<?php echo $filterev ?>&amp;filterof=<?php echo $filterof ?>&amp;filtersx=<?php echo $filtersx ?>&amp;view=<?php echo $view ?>">
197 197
 						<b><?php echo $today->format('%F %Y') ?></b>
198 198
 					</a>
@@ -302,23 +302,23 @@  discard block
 block discarded – undo
302 302
 				</td>
303 303
 				<td class="topbottombar width50">
304 304
 					<?php
305
-					$n = 0;
306
-					foreach (Date::calendarNames() as $newcal => $cal_name) {
307
-						$tmp = $cal_date->convertToCalendar($newcal);
308
-						if ($tmp->inValidRange()) {
309
-							if ($n++) {
310
-								echo ' | ';
311
-							}
312
-							if (get_class($tmp) === get_class($cal_date)) {
313
-								echo '<span class="error">', $cal_name, '</span>';
314
-							} else {
315
-								$newcalesc = urlencode($tmp->format('%@'));
316
-								$tmpmonth  = $tmp->format('%O');
317
-								echo '<a href="?cal=', $newcalesc, '&amp;day=', $tmp->d, '&amp;month=', $tmpmonth, '&amp;year=', $tmp->y, '&amp;filterev=', $filterev, '&amp;filterof=', $filterof, '&amp;filtersx=', $filtersx, '&amp;view=', $view, '">', $cal_name, '</a>';
318
-							}
319
-						}
320
-					}
321
-					?>
305
+                    $n = 0;
306
+                    foreach (Date::calendarNames() as $newcal => $cal_name) {
307
+                        $tmp = $cal_date->convertToCalendar($newcal);
308
+                        if ($tmp->inValidRange()) {
309
+                            if ($n++) {
310
+                                echo ' | ';
311
+                            }
312
+                            if (get_class($tmp) === get_class($cal_date)) {
313
+                                echo '<span class="error">', $cal_name, '</span>';
314
+                            } else {
315
+                                $newcalesc = urlencode($tmp->format('%@'));
316
+                                $tmpmonth  = $tmp->format('%O');
317
+                                echo '<a href="?cal=', $newcalesc, '&amp;day=', $tmp->d, '&amp;month=', $tmpmonth, '&amp;year=', $tmp->y, '&amp;filterev=', $filterev, '&amp;filterof=', $filterof, '&amp;filtersx=', $filtersx, '&amp;view=', $view, '">', $cal_name, '</a>';
318
+                            }
319
+                        }
320
+                    }
321
+                    ?>
322 322
 				</td>
323 323
 			</tr>
324 324
 		</table>
@@ -330,37 +330,37 @@  discard block
 block discarded – undo
330 330
 
331 331
 switch ($view) {
332 332
 case 'day':
333
-	$found_facts = apply_filter(FunctionsDb::getAnniversaryEvents($cal_date->minJD, $filterev, $WT_TREE), $filterof, $filtersx);
334
-	break;
333
+    $found_facts = apply_filter(FunctionsDb::getAnniversaryEvents($cal_date->minJD, $filterev, $WT_TREE), $filterof, $filtersx);
334
+    break;
335 335
 case 'month':
336
-	$cal_date->d = 0;
337
-	$cal_date->setJdFromYmd();
338
-	// Make a separate list for each day. Unspecified/invalid days go in day 0.
339
-	for ($d = 0; $d <= $days_in_month; ++$d) {
340
-		$found_facts[$d] = array();
341
-	}
342
-	// Fetch events for each day
343
-	for ($jd = $cal_date->minJD; $jd <= $cal_date->maxJD; ++$jd) {
344
-		foreach (apply_filter(FunctionsDb::getAnniversaryEvents($jd, $filterev, $WT_TREE), $filterof, $filtersx) as $fact) {
345
-			$tmp = $fact->getDate()->minimumDate();
346
-			if ($tmp->d >= 1 && $tmp->d <= $tmp->daysInMonth()) {
347
-				// If the day is valid (for its own calendar), display it in the
348
-				// anniversary day (for the display calendar).
349
-				$found_facts[$jd - $cal_date->minJD + 1][] = $fact;
350
-			} else {
351
-				// Otherwise, display it in the "Day not set" box.
352
-				$found_facts[0][] = $fact;
353
-			}
354
-		}
355
-	}
356
-	break;
336
+    $cal_date->d = 0;
337
+    $cal_date->setJdFromYmd();
338
+    // Make a separate list for each day. Unspecified/invalid days go in day 0.
339
+    for ($d = 0; $d <= $days_in_month; ++$d) {
340
+        $found_facts[$d] = array();
341
+    }
342
+    // Fetch events for each day
343
+    for ($jd = $cal_date->minJD; $jd <= $cal_date->maxJD; ++$jd) {
344
+        foreach (apply_filter(FunctionsDb::getAnniversaryEvents($jd, $filterev, $WT_TREE), $filterof, $filtersx) as $fact) {
345
+            $tmp = $fact->getDate()->minimumDate();
346
+            if ($tmp->d >= 1 && $tmp->d <= $tmp->daysInMonth()) {
347
+                // If the day is valid (for its own calendar), display it in the
348
+                // anniversary day (for the display calendar).
349
+                $found_facts[$jd - $cal_date->minJD + 1][] = $fact;
350
+            } else {
351
+                // Otherwise, display it in the "Day not set" box.
352
+                $found_facts[0][] = $fact;
353
+            }
354
+        }
355
+    }
356
+    break;
357 357
 case 'year':
358
-	$cal_date->m = 0;
359
-	$cal_date->setJdFromYmd();
360
-	$found_facts = apply_filter(FunctionsDb::getCalendarEvents($ged_date->minimumJulianDay(), $ged_date->maximumJulianDay(), $filterev, $WT_TREE), $filterof, $filtersx);
361
-	// Eliminate duplicates (e.g. BET JUL 1900 AND SEP 1900 will appear twice in 1900)
362
-	$found_facts = array_unique($found_facts);
363
-	break;
358
+    $cal_date->m = 0;
359
+    $cal_date->setJdFromYmd();
360
+    $found_facts = apply_filter(FunctionsDb::getCalendarEvents($ged_date->minimumJulianDay(), $ged_date->maximumJulianDay(), $filterev, $WT_TREE), $filterof, $filtersx);
361
+    // Eliminate duplicates (e.g. BET JUL 1900 AND SEP 1900 will appear twice in 1900)
362
+    $found_facts = array_unique($found_facts);
363
+    break;
364 364
 }
365 365
 
366 366
 // Group the facts by family/individual
@@ -371,172 +371,172 @@  discard block
 block discarded – undo
371 371
 switch ($view) {
372 372
 case 'year':
373 373
 case 'day':
374
-	foreach ($found_facts as $fact) {
375
-		$record = $fact->getParent();
376
-		$xref   = $record->getXref();
377
-		if ($record instanceof Individual) {
378
-			if (empty($indis[$xref])) {
379
-				$indis[$xref] = calendar_fact_text($fact, true);
380
-			} else {
381
-				$indis[$xref] .= '<br>' . calendar_fact_text($fact, true);
382
-			}
383
-		} elseif ($record instanceof Family) {
384
-			if (empty($indis[$xref])) {
385
-				$fams[$xref] = calendar_fact_text($fact, true);
386
-			} else {
387
-				$fams[$xref] .= '<br>' . calendar_fact_text($fact, true);
388
-			}
389
-		}
390
-	}
391
-	break;
374
+    foreach ($found_facts as $fact) {
375
+        $record = $fact->getParent();
376
+        $xref   = $record->getXref();
377
+        if ($record instanceof Individual) {
378
+            if (empty($indis[$xref])) {
379
+                $indis[$xref] = calendar_fact_text($fact, true);
380
+            } else {
381
+                $indis[$xref] .= '<br>' . calendar_fact_text($fact, true);
382
+            }
383
+        } elseif ($record instanceof Family) {
384
+            if (empty($indis[$xref])) {
385
+                $fams[$xref] = calendar_fact_text($fact, true);
386
+            } else {
387
+                $fams[$xref] .= '<br>' . calendar_fact_text($fact, true);
388
+            }
389
+        }
390
+    }
391
+    break;
392 392
 case 'month':
393
-	foreach ($found_facts as $d => $facts) {
394
-		$cal_facts[$d] = array();
395
-		foreach ($facts as $fact) {
396
-			$xref = $fact->getParent()->getXref();
397
-			if (empty($cal_facts[$d][$xref])) {
398
-				$cal_facts[$d][$xref] = calendar_fact_text($fact, false);
399
-			} else {
400
-				$cal_facts[$d][$xref] .= '<br>' . calendar_fact_text($fact, false);
401
-			}
402
-		}
403
-	}
404
-	break;
393
+    foreach ($found_facts as $d => $facts) {
394
+        $cal_facts[$d] = array();
395
+        foreach ($facts as $fact) {
396
+            $xref = $fact->getParent()->getXref();
397
+            if (empty($cal_facts[$d][$xref])) {
398
+                $cal_facts[$d][$xref] = calendar_fact_text($fact, false);
399
+            } else {
400
+                $cal_facts[$d][$xref] .= '<br>' . calendar_fact_text($fact, false);
401
+            }
402
+        }
403
+    }
404
+    break;
405 405
 }
406 406
 
407 407
 switch ($view) {
408 408
 case 'year':
409 409
 case 'day':
410
-	$males   = 0;
411
-	$females = 0;
412
-	echo '<table class="width100"><tr>';
413
-	echo '<td class="descriptionbox center width50"><i class="icon-indis"></i>', I18N::translate('Individuals'), '</td>';
414
-	echo '<td class="descriptionbox center width50"><i class="icon-cfamily"></i>', I18N::translate('Families'), '</td>';
415
-	echo '</tr><tr>';
416
-	echo '<td class="optionbox wrap">';
417
-
418
-	$content = calendar_list_text($indis, '<li>', '</li>', true);
419
-	if ($content) {
420
-		echo '<ul>', $content, '</ul>';
421
-	}
422
-
423
-	echo '</td>';
424
-	echo '<td class="optionbox wrap">';
425
-
426
-	$content = calendar_list_text($fams, '<li>', '</li>', true);
427
-	if ($content) {
428
-		echo '<ul>', $content, '</ul>';
429
-	}
430
-
431
-	echo '</td>';
432
-	echo '</tr><tr>';
433
-	echo '<td class="descriptionbox">', I18N::translate('Total individuals: %s', count($indis));
434
-	echo '<br>';
435
-	echo '<i class="icon-sex_m_15x15" title="', I18N::translate('Males'), '"></i> ', $males, ' ';
436
-	echo '<i class="icon-sex_f_15x15" title="', I18N::translate('Females'), '"></i> ', $females, ' ';
437
-	if (count($indis) !== $males + $females) {
438
-		echo '<i class="icon-sex_u_15x15" title="', I18N::translate('All individuals'), '"></i> ', count($indis) - $males - $females;
439
-	}
440
-	echo '</td>';
441
-	echo '<td class="descriptionbox">', I18N::translate('Total families: %s', count($fams)), '</td>';
442
-	echo '</tr></table>';
443
-
444
-	break;
410
+    $males   = 0;
411
+    $females = 0;
412
+    echo '<table class="width100"><tr>';
413
+    echo '<td class="descriptionbox center width50"><i class="icon-indis"></i>', I18N::translate('Individuals'), '</td>';
414
+    echo '<td class="descriptionbox center width50"><i class="icon-cfamily"></i>', I18N::translate('Families'), '</td>';
415
+    echo '</tr><tr>';
416
+    echo '<td class="optionbox wrap">';
417
+
418
+    $content = calendar_list_text($indis, '<li>', '</li>', true);
419
+    if ($content) {
420
+        echo '<ul>', $content, '</ul>';
421
+    }
422
+
423
+    echo '</td>';
424
+    echo '<td class="optionbox wrap">';
425
+
426
+    $content = calendar_list_text($fams, '<li>', '</li>', true);
427
+    if ($content) {
428
+        echo '<ul>', $content, '</ul>';
429
+    }
430
+
431
+    echo '</td>';
432
+    echo '</tr><tr>';
433
+    echo '<td class="descriptionbox">', I18N::translate('Total individuals: %s', count($indis));
434
+    echo '<br>';
435
+    echo '<i class="icon-sex_m_15x15" title="', I18N::translate('Males'), '"></i> ', $males, ' ';
436
+    echo '<i class="icon-sex_f_15x15" title="', I18N::translate('Females'), '"></i> ', $females, ' ';
437
+    if (count($indis) !== $males + $females) {
438
+        echo '<i class="icon-sex_u_15x15" title="', I18N::translate('All individuals'), '"></i> ', count($indis) - $males - $females;
439
+    }
440
+    echo '</td>';
441
+    echo '<td class="descriptionbox">', I18N::translate('Total families: %s', count($fams)), '</td>';
442
+    echo '</tr></table>';
443
+
444
+    break;
445 445
 case 'month':
446 446
 // We use JD%7 = 0/Mon…6/Sun. Standard definitions use 0/Sun…6/Sat.
447
-	$week_start    = (I18N::firstDay() + 6) % 7;
448
-	$weekend_start = (I18N::weekendStart() + 6) % 7;
449
-	$weekend_end   = (I18N::weekendEnd() + 6) % 7;
450
-	// The french  calendar has a 10-day week, which starts on primidi
451
-	if ($days_in_week === 10) {
452
-		$week_start    = 0;
453
-		$weekend_start = -1;
454
-		$weekend_end   = -1;
455
-	}
456
-	echo '<table class="width100"><thead><tr>';
457
-	for ($week_day = 0; $week_day < $days_in_week; ++$week_day) {
458
-		$day_name = $cal_date->dayNames(($week_day + $week_start) % $days_in_week);
459
-		if ($week_day == $weekend_start || $week_day == $weekend_end) {
460
-			echo '<th class="descriptionbox weekend" width="' . (100 / $days_in_week) . '%">', $day_name, '</th>';
461
-		} else {
462
-			echo '<th class="descriptionbox" width="' . (100 / $days_in_week) . '%">', $day_name, '</th>';
463
-		}
464
-	}
465
-	echo '</tr>';
466
-	echo '</thead>';
467
-	echo '<tbody>';
468
-	// Print days 1 to n of the month, but extend to cover "empty" days before/after the month to make whole weeks.
469
-	// e.g. instead of 1 -> 30 (=30 days), we might have -1 -> 33 (=35 days)
470
-	$start_d = 1 - ($cal_date->minJD - $week_start) % $days_in_week;
471
-	$end_d   = $days_in_month + ($days_in_week - ($cal_date->maxJD - $week_start + 1) % $days_in_week) % $days_in_week;
472
-	// Make sure that there is an empty box for any leap/missing days
473
-	if ($start_d === 1 && $end_d === $days_in_month && count($found_facts[0]) > 0) {
474
-		$end_d += $days_in_week;
475
-	}
476
-	for ($d = $start_d; $d <= $end_d; ++$d) {
477
-		if (($d + $cal_date->minJD - $week_start) % $days_in_week === 1) {
478
-			echo '<tr>';
479
-		}
480
-		echo '<td class="optionbox wrap">';
481
-		if ($d < 1 || $d > $days_in_month) {
482
-			if (count($cal_facts[0]) > 0) {
483
-				echo '<span class="cal_day">', I18N::translate('Day not set'), '</span><br style="clear: both;">';
484
-				echo '<div class="details1" style="height: 180px; overflow: auto;">';
485
-				echo calendar_list_text($cal_facts[0], '', '', false);
486
-				echo '</div>';
487
-				$cal_facts[0] = array();
488
-			}
489
-		} else {
490
-			// Format the day number using the calendar
491
-			$tmp   = new Date($cal_date->format("%@ {$d} %O %E"));
492
-			$d_fmt = $tmp->minimumDate()->format('%j');
493
-			if ($d === $today->d && $cal_date->m === $today->m) {
494
-				echo '<span class="cal_day current_day">', $d_fmt, '</span>';
495
-			} else {
496
-				echo '<span class="cal_day">', $d_fmt, '</span>';
497
-			}
498
-			// Show a converted date
499
-			foreach (explode('_and_', $CALENDAR_FORMAT) as $convcal) {
500
-				switch ($convcal) {
501
-				case 'french':
502
-					$alt_date = new FrenchDate($cal_date->minJD + $d - 1);
503
-					break;
504
-				case 'gregorian':
505
-					$alt_date = new GregorianDate($cal_date->minJD + $d - 1);
506
-					break;
507
-				case 'jewish':
508
-					$alt_date = new JewishDate($cal_date->minJD + $d - 1);
509
-					break;
510
-				case 'julian':
511
-					$alt_date = new JulianDate($cal_date->minJD + $d - 1);
512
-					break;
513
-				case 'hijri':
514
-					$alt_date = new HijriDate($cal_date->minJD + $d - 1);
515
-					break;
516
-				case 'jalali':
517
-					$alt_date = new JalaliDate($cal_date->minJD + $d - 1);
518
-					break;
519
-				default:
520
-					break 2;
521
-				}
522
-				if (get_class($alt_date) !== get_class($cal_date) && $alt_date->inValidRange()) {
523
-					echo '<span class="rtl_cal_day">' . $alt_date->format("%j %M") . '</span>';
524
-					// Just show the first conversion
525
-					break;
526
-				}
527
-			}
528
-			echo '<br style="clear: both;"><div class="details1" style="height: 180px; overflow: auto;">';
529
-			echo calendar_list_text($cal_facts[$d], '', '', false);
530
-			echo '</div>';
531
-		}
532
-		echo '</td>';
533
-		if (($d + $cal_date->minJD - $week_start) % $days_in_week === 0) {
534
-			echo '</tr>';
535
-		}
536
-	}
537
-	echo '</tbody>';
538
-	echo '</table>';
539
-	break;
447
+    $week_start    = (I18N::firstDay() + 6) % 7;
448
+    $weekend_start = (I18N::weekendStart() + 6) % 7;
449
+    $weekend_end   = (I18N::weekendEnd() + 6) % 7;
450
+    // The french  calendar has a 10-day week, which starts on primidi
451
+    if ($days_in_week === 10) {
452
+        $week_start    = 0;
453
+        $weekend_start = -1;
454
+        $weekend_end   = -1;
455
+    }
456
+    echo '<table class="width100"><thead><tr>';
457
+    for ($week_day = 0; $week_day < $days_in_week; ++$week_day) {
458
+        $day_name = $cal_date->dayNames(($week_day + $week_start) % $days_in_week);
459
+        if ($week_day == $weekend_start || $week_day == $weekend_end) {
460
+            echo '<th class="descriptionbox weekend" width="' . (100 / $days_in_week) . '%">', $day_name, '</th>';
461
+        } else {
462
+            echo '<th class="descriptionbox" width="' . (100 / $days_in_week) . '%">', $day_name, '</th>';
463
+        }
464
+    }
465
+    echo '</tr>';
466
+    echo '</thead>';
467
+    echo '<tbody>';
468
+    // Print days 1 to n of the month, but extend to cover "empty" days before/after the month to make whole weeks.
469
+    // e.g. instead of 1 -> 30 (=30 days), we might have -1 -> 33 (=35 days)
470
+    $start_d = 1 - ($cal_date->minJD - $week_start) % $days_in_week;
471
+    $end_d   = $days_in_month + ($days_in_week - ($cal_date->maxJD - $week_start + 1) % $days_in_week) % $days_in_week;
472
+    // Make sure that there is an empty box for any leap/missing days
473
+    if ($start_d === 1 && $end_d === $days_in_month && count($found_facts[0]) > 0) {
474
+        $end_d += $days_in_week;
475
+    }
476
+    for ($d = $start_d; $d <= $end_d; ++$d) {
477
+        if (($d + $cal_date->minJD - $week_start) % $days_in_week === 1) {
478
+            echo '<tr>';
479
+        }
480
+        echo '<td class="optionbox wrap">';
481
+        if ($d < 1 || $d > $days_in_month) {
482
+            if (count($cal_facts[0]) > 0) {
483
+                echo '<span class="cal_day">', I18N::translate('Day not set'), '</span><br style="clear: both;">';
484
+                echo '<div class="details1" style="height: 180px; overflow: auto;">';
485
+                echo calendar_list_text($cal_facts[0], '', '', false);
486
+                echo '</div>';
487
+                $cal_facts[0] = array();
488
+            }
489
+        } else {
490
+            // Format the day number using the calendar
491
+            $tmp   = new Date($cal_date->format("%@ {$d} %O %E"));
492
+            $d_fmt = $tmp->minimumDate()->format('%j');
493
+            if ($d === $today->d && $cal_date->m === $today->m) {
494
+                echo '<span class="cal_day current_day">', $d_fmt, '</span>';
495
+            } else {
496
+                echo '<span class="cal_day">', $d_fmt, '</span>';
497
+            }
498
+            // Show a converted date
499
+            foreach (explode('_and_', $CALENDAR_FORMAT) as $convcal) {
500
+                switch ($convcal) {
501
+                case 'french':
502
+                    $alt_date = new FrenchDate($cal_date->minJD + $d - 1);
503
+                    break;
504
+                case 'gregorian':
505
+                    $alt_date = new GregorianDate($cal_date->minJD + $d - 1);
506
+                    break;
507
+                case 'jewish':
508
+                    $alt_date = new JewishDate($cal_date->minJD + $d - 1);
509
+                    break;
510
+                case 'julian':
511
+                    $alt_date = new JulianDate($cal_date->minJD + $d - 1);
512
+                    break;
513
+                case 'hijri':
514
+                    $alt_date = new HijriDate($cal_date->minJD + $d - 1);
515
+                    break;
516
+                case 'jalali':
517
+                    $alt_date = new JalaliDate($cal_date->minJD + $d - 1);
518
+                    break;
519
+                default:
520
+                    break 2;
521
+                }
522
+                if (get_class($alt_date) !== get_class($cal_date) && $alt_date->inValidRange()) {
523
+                    echo '<span class="rtl_cal_day">' . $alt_date->format("%j %M") . '</span>';
524
+                    // Just show the first conversion
525
+                    break;
526
+                }
527
+            }
528
+            echo '<br style="clear: both;"><div class="details1" style="height: 180px; overflow: auto;">';
529
+            echo calendar_list_text($cal_facts[$d], '', '', false);
530
+            echo '</div>';
531
+        }
532
+        echo '</td>';
533
+        if (($d + $cal_date->minJD - $week_start) % $days_in_week === 0) {
534
+            echo '</tr>';
535
+        }
536
+    }
537
+    echo '</tbody>';
538
+    echo '</table>';
539
+    break;
540 540
 }
541 541
 echo '</div>'; //close "calendar-page"
542 542
 
@@ -550,41 +550,41 @@  discard block
 block discarded – undo
550 550
  * @return array
551 551
  */
552 552
 function apply_filter($facts, $filterof, $filtersx) {
553
-	$filtered      = array();
554
-	$hundred_years = WT_CLIENT_JD - 36525;
555
-	foreach ($facts as $fact) {
556
-		$record = $fact->getParent();
557
-		if ($filtersx) {
558
-			// Filter on sex
559
-			if ($record instanceof Individual && $filtersx !== $record->getSex()) {
560
-				continue;
561
-			}
562
-			// Can't display families if the sex filter is on.
563
-			if ($record instanceof Family) {
564
-				continue;
565
-			}
566
-		}
567
-		// Filter living individuals
568
-		if ($filterof === 'living') {
569
-			if ($record instanceof Individual && $record->isDead()) {
570
-				continue;
571
-			}
572
-			if ($record instanceof Family) {
573
-				$husb = $record->getHusband();
574
-				$wife = $record->getWife();
575
-				if ($husb && $husb->isDead() || $wife && $wife->isDead()) {
576
-					continue;
577
-				}
578
-			}
579
-		}
580
-		// Filter on recent events
581
-		if ($filterof === 'recent' && $fact->getDate()->maximumJulianDay() < $hundred_years) {
582
-			continue;
583
-		}
584
-		$filtered[] = $fact;
585
-	}
586
-
587
-	return $filtered;
553
+    $filtered      = array();
554
+    $hundred_years = WT_CLIENT_JD - 36525;
555
+    foreach ($facts as $fact) {
556
+        $record = $fact->getParent();
557
+        if ($filtersx) {
558
+            // Filter on sex
559
+            if ($record instanceof Individual && $filtersx !== $record->getSex()) {
560
+                continue;
561
+            }
562
+            // Can't display families if the sex filter is on.
563
+            if ($record instanceof Family) {
564
+                continue;
565
+            }
566
+        }
567
+        // Filter living individuals
568
+        if ($filterof === 'living') {
569
+            if ($record instanceof Individual && $record->isDead()) {
570
+                continue;
571
+            }
572
+            if ($record instanceof Family) {
573
+                $husb = $record->getHusband();
574
+                $wife = $record->getWife();
575
+                if ($husb && $husb->isDead() || $wife && $wife->isDead()) {
576
+                    continue;
577
+                }
578
+            }
579
+        }
580
+        // Filter on recent events
581
+        if ($filterof === 'recent' && $fact->getDate()->maximumJulianDay() < $hundred_years) {
582
+            continue;
583
+        }
584
+        $filtered[] = $fact;
585
+    }
586
+
587
+    return $filtered;
588 588
 }
589 589
 
590 590
 /**
@@ -596,15 +596,15 @@  discard block
 block discarded – undo
596 596
  * @return string
597 597
  */
598 598
 function calendar_fact_text(Fact $fact, $show_places) {
599
-	$text = $fact->getLabel() . ' — ' . $fact->getDate()->display(true, null, false);
600
-	if ($fact->anniv) {
601
-		$text .= ' (' . I18N::translate('%s year anniversary', $fact->anniv) . ')';
602
-	}
603
-	if ($show_places && $fact->getAttribute('PLAC')) {
604
-		$text .= ' — ' . $fact->getAttribute('PLAC');
605
-	}
606
-
607
-	return $text;
599
+    $text = $fact->getLabel() . ' — ' . $fact->getDate()->display(true, null, false);
600
+    if ($fact->anniv) {
601
+        $text .= ' (' . I18N::translate('%s year anniversary', $fact->anniv) . ')';
602
+    }
603
+    if ($show_places && $fact->getAttribute('PLAC')) {
604
+        $text .= ' — ' . $fact->getAttribute('PLAC');
605
+    }
606
+
607
+    return $text;
608 608
 }
609 609
 
610 610
 /**
@@ -618,30 +618,30 @@  discard block
 block discarded – undo
618 618
  * @return string
619 619
  */
620 620
 function calendar_list_text($list, $tag1, $tag2, $show_sex_symbols) {
621
-	global $males, $females, $WT_TREE;
622
-
623
-	$html = '';
624
-
625
-	foreach ($list as $id => $facts) {
626
-		$tmp = GedcomRecord::getInstance($id, $WT_TREE);
627
-		$html .= $tag1 . '<a href="' . $tmp->getHtmlUrl() . '">' . $tmp->getFullName() . '</a> ';
628
-		if ($show_sex_symbols && $tmp instanceof Individual) {
629
-			switch ($tmp->getSex()) {
630
-			case 'M':
631
-				$html .= '<i class="icon-sex_m_9x9" title="' . I18N::translate('Male') . '"></i>';
632
-				++$males;
633
-				break;
634
-			case 'F':
635
-				$html .= '<i class="icon-sex_f_9x9" title="' . I18N::translate('Female') . '"></i>';
636
-				++$females;
637
-				break;
638
-			default:
639
-				$html .= '<i class="icon-sex_u_9x9" title="' . I18N::translateContext('unknown gender', 'Unknown') . '"></i>';
640
-				break;
641
-			}
642
-		}
643
-		$html .= '<div class="indent">' . $facts . '</div>' . $tag2;
644
-	}
645
-
646
-	return $html;
621
+    global $males, $females, $WT_TREE;
622
+
623
+    $html = '';
624
+
625
+    foreach ($list as $id => $facts) {
626
+        $tmp = GedcomRecord::getInstance($id, $WT_TREE);
627
+        $html .= $tag1 . '<a href="' . $tmp->getHtmlUrl() . '">' . $tmp->getFullName() . '</a> ';
628
+        if ($show_sex_symbols && $tmp instanceof Individual) {
629
+            switch ($tmp->getSex()) {
630
+            case 'M':
631
+                $html .= '<i class="icon-sex_m_9x9" title="' . I18N::translate('Male') . '"></i>';
632
+                ++$males;
633
+                break;
634
+            case 'F':
635
+                $html .= '<i class="icon-sex_f_9x9" title="' . I18N::translate('Female') . '"></i>';
636
+                ++$females;
637
+                break;
638
+            default:
639
+                $html .= '<i class="icon-sex_u_9x9" title="' . I18N::translateContext('unknown gender', 'Unknown') . '"></i>';
640
+                break;
641
+            }
642
+        }
643
+        $html .= '<div class="indent">' . $facts . '</div>' . $tag2;
644
+    }
645
+
646
+    return $html;
647 647
 }
Please login to merge, or discard this patch.
Switch Indentation   +105 added lines, -105 removed lines patch added patch discarded remove patch
@@ -118,15 +118,15 @@  discard block
 block discarded – undo
118 118
 $controller->setPageTitle(I18N::translate('Anniversary calendar'));
119 119
 
120 120
 switch ($view) {
121
-case 'day':
122
-	$controller->setPageTitle(I18N::translate('On this day…') . ' ' . $ged_date->display(false));
123
-	break;
124
-case 'month':
125
-	$controller->setPageTitle(I18N::translate('In this month…') . ' ' . $ged_date->display(false, '%F %Y'));
126
-	break;
127
-case 'year':
128
-	$controller->setPageTitle(I18N::translate('In this year…') . ' ' . $ged_date->display(false, '%Y'));
129
-	break;
121
+    case 'day':
122
+	    $controller->setPageTitle(I18N::translate('On this day…') . ' ' . $ged_date->display(false));
123
+	    break;
124
+    case 'month':
125
+	    $controller->setPageTitle(I18N::translate('In this month…') . ' ' . $ged_date->display(false, '%F %Y'));
126
+	    break;
127
+    case 'year':
128
+	    $controller->setPageTitle(I18N::translate('In this year…') . ' ' . $ged_date->display(false, '%Y'));
129
+	    break;
130 130
 }
131 131
 
132 132
 $controller->pageHeader();
@@ -329,38 +329,38 @@  discard block
 block discarded – undo
329 329
 $found_facts = array();
330 330
 
331 331
 switch ($view) {
332
-case 'day':
333
-	$found_facts = apply_filter(FunctionsDb::getAnniversaryEvents($cal_date->minJD, $filterev, $WT_TREE), $filterof, $filtersx);
334
-	break;
335
-case 'month':
336
-	$cal_date->d = 0;
337
-	$cal_date->setJdFromYmd();
338
-	// Make a separate list for each day. Unspecified/invalid days go in day 0.
339
-	for ($d = 0; $d <= $days_in_month; ++$d) {
340
-		$found_facts[$d] = array();
341
-	}
342
-	// Fetch events for each day
343
-	for ($jd = $cal_date->minJD; $jd <= $cal_date->maxJD; ++$jd) {
344
-		foreach (apply_filter(FunctionsDb::getAnniversaryEvents($jd, $filterev, $WT_TREE), $filterof, $filtersx) as $fact) {
345
-			$tmp = $fact->getDate()->minimumDate();
346
-			if ($tmp->d >= 1 && $tmp->d <= $tmp->daysInMonth()) {
347
-				// If the day is valid (for its own calendar), display it in the
348
-				// anniversary day (for the display calendar).
349
-				$found_facts[$jd - $cal_date->minJD + 1][] = $fact;
350
-			} else {
351
-				// Otherwise, display it in the "Day not set" box.
352
-				$found_facts[0][] = $fact;
353
-			}
354
-		}
355
-	}
356
-	break;
357
-case 'year':
358
-	$cal_date->m = 0;
359
-	$cal_date->setJdFromYmd();
360
-	$found_facts = apply_filter(FunctionsDb::getCalendarEvents($ged_date->minimumJulianDay(), $ged_date->maximumJulianDay(), $filterev, $WT_TREE), $filterof, $filtersx);
361
-	// Eliminate duplicates (e.g. BET JUL 1900 AND SEP 1900 will appear twice in 1900)
362
-	$found_facts = array_unique($found_facts);
363
-	break;
332
+    case 'day':
333
+	    $found_facts = apply_filter(FunctionsDb::getAnniversaryEvents($cal_date->minJD, $filterev, $WT_TREE), $filterof, $filtersx);
334
+	    break;
335
+    case 'month':
336
+	    $cal_date->d = 0;
337
+	    $cal_date->setJdFromYmd();
338
+	    // Make a separate list for each day. Unspecified/invalid days go in day 0.
339
+	    for ($d = 0; $d <= $days_in_month; ++$d) {
340
+		    $found_facts[$d] = array();
341
+	    }
342
+	    // Fetch events for each day
343
+	    for ($jd = $cal_date->minJD; $jd <= $cal_date->maxJD; ++$jd) {
344
+		    foreach (apply_filter(FunctionsDb::getAnniversaryEvents($jd, $filterev, $WT_TREE), $filterof, $filtersx) as $fact) {
345
+			    $tmp = $fact->getDate()->minimumDate();
346
+			    if ($tmp->d >= 1 && $tmp->d <= $tmp->daysInMonth()) {
347
+				    // If the day is valid (for its own calendar), display it in the
348
+				    // anniversary day (for the display calendar).
349
+				    $found_facts[$jd - $cal_date->minJD + 1][] = $fact;
350
+			    } else {
351
+				    // Otherwise, display it in the "Day not set" box.
352
+				    $found_facts[0][] = $fact;
353
+			    }
354
+		    }
355
+	    }
356
+	    break;
357
+    case 'year':
358
+	    $cal_date->m = 0;
359
+	    $cal_date->setJdFromYmd();
360
+	    $found_facts = apply_filter(FunctionsDb::getCalendarEvents($ged_date->minimumJulianDay(), $ged_date->maximumJulianDay(), $filterev, $WT_TREE), $filterof, $filtersx);
361
+	    // Eliminate duplicates (e.g. BET JUL 1900 AND SEP 1900 will appear twice in 1900)
362
+	    $found_facts = array_unique($found_facts);
363
+	    break;
364 364
 }
365 365
 
366 366
 // Group the facts by family/individual
@@ -369,39 +369,39 @@  discard block
 block discarded – undo
369 369
 $cal_facts = array();
370 370
 
371 371
 switch ($view) {
372
-case 'year':
373
-case 'day':
374
-	foreach ($found_facts as $fact) {
375
-		$record = $fact->getParent();
376
-		$xref   = $record->getXref();
377
-		if ($record instanceof Individual) {
378
-			if (empty($indis[$xref])) {
379
-				$indis[$xref] = calendar_fact_text($fact, true);
380
-			} else {
381
-				$indis[$xref] .= '<br>' . calendar_fact_text($fact, true);
382
-			}
383
-		} elseif ($record instanceof Family) {
384
-			if (empty($indis[$xref])) {
385
-				$fams[$xref] = calendar_fact_text($fact, true);
386
-			} else {
387
-				$fams[$xref] .= '<br>' . calendar_fact_text($fact, true);
388
-			}
389
-		}
390
-	}
391
-	break;
392
-case 'month':
393
-	foreach ($found_facts as $d => $facts) {
394
-		$cal_facts[$d] = array();
395
-		foreach ($facts as $fact) {
396
-			$xref = $fact->getParent()->getXref();
397
-			if (empty($cal_facts[$d][$xref])) {
398
-				$cal_facts[$d][$xref] = calendar_fact_text($fact, false);
399
-			} else {
400
-				$cal_facts[$d][$xref] .= '<br>' . calendar_fact_text($fact, false);
401
-			}
402
-		}
403
-	}
404
-	break;
372
+    case 'year':
373
+    case 'day':
374
+	    foreach ($found_facts as $fact) {
375
+		    $record = $fact->getParent();
376
+		    $xref   = $record->getXref();
377
+		    if ($record instanceof Individual) {
378
+			    if (empty($indis[$xref])) {
379
+				    $indis[$xref] = calendar_fact_text($fact, true);
380
+			    } else {
381
+				    $indis[$xref] .= '<br>' . calendar_fact_text($fact, true);
382
+			    }
383
+		    } elseif ($record instanceof Family) {
384
+			    if (empty($indis[$xref])) {
385
+				    $fams[$xref] = calendar_fact_text($fact, true);
386
+			    } else {
387
+				    $fams[$xref] .= '<br>' . calendar_fact_text($fact, true);
388
+			    }
389
+		    }
390
+	    }
391
+	    break;
392
+    case 'month':
393
+	    foreach ($found_facts as $d => $facts) {
394
+		    $cal_facts[$d] = array();
395
+		    foreach ($facts as $fact) {
396
+			    $xref = $fact->getParent()->getXref();
397
+			    if (empty($cal_facts[$d][$xref])) {
398
+				    $cal_facts[$d][$xref] = calendar_fact_text($fact, false);
399
+			    } else {
400
+				    $cal_facts[$d][$xref] .= '<br>' . calendar_fact_text($fact, false);
401
+			    }
402
+		    }
403
+	    }
404
+	    break;
405 405
 }
406 406
 
407 407
 switch ($view) {
@@ -498,26 +498,26 @@  discard block
 block discarded – undo
498 498
 			// Show a converted date
499 499
 			foreach (explode('_and_', $CALENDAR_FORMAT) as $convcal) {
500 500
 				switch ($convcal) {
501
-				case 'french':
502
-					$alt_date = new FrenchDate($cal_date->minJD + $d - 1);
503
-					break;
504
-				case 'gregorian':
505
-					$alt_date = new GregorianDate($cal_date->minJD + $d - 1);
506
-					break;
507
-				case 'jewish':
508
-					$alt_date = new JewishDate($cal_date->minJD + $d - 1);
509
-					break;
510
-				case 'julian':
511
-					$alt_date = new JulianDate($cal_date->minJD + $d - 1);
512
-					break;
513
-				case 'hijri':
514
-					$alt_date = new HijriDate($cal_date->minJD + $d - 1);
515
-					break;
516
-				case 'jalali':
517
-					$alt_date = new JalaliDate($cal_date->minJD + $d - 1);
518
-					break;
519
-				default:
520
-					break 2;
501
+				    case 'french':
502
+					    $alt_date = new FrenchDate($cal_date->minJD + $d - 1);
503
+					    break;
504
+				    case 'gregorian':
505
+					    $alt_date = new GregorianDate($cal_date->minJD + $d - 1);
506
+					    break;
507
+				    case 'jewish':
508
+					    $alt_date = new JewishDate($cal_date->minJD + $d - 1);
509
+					    break;
510
+				    case 'julian':
511
+					    $alt_date = new JulianDate($cal_date->minJD + $d - 1);
512
+					    break;
513
+				    case 'hijri':
514
+					    $alt_date = new HijriDate($cal_date->minJD + $d - 1);
515
+					    break;
516
+				    case 'jalali':
517
+					    $alt_date = new JalaliDate($cal_date->minJD + $d - 1);
518
+					    break;
519
+				    default:
520
+					    break 2;
521 521
 				}
522 522
 				if (get_class($alt_date) !== get_class($cal_date) && $alt_date->inValidRange()) {
523 523
 					echo '<span class="rtl_cal_day">' . $alt_date->format("%j %M") . '</span>';
@@ -627,17 +627,17 @@  discard block
 block discarded – undo
627 627
 		$html .= $tag1 . '<a href="' . $tmp->getHtmlUrl() . '">' . $tmp->getFullName() . '</a> ';
628 628
 		if ($show_sex_symbols && $tmp instanceof Individual) {
629 629
 			switch ($tmp->getSex()) {
630
-			case 'M':
631
-				$html .= '<i class="icon-sex_m_9x9" title="' . I18N::translate('Male') . '"></i>';
632
-				++$males;
633
-				break;
634
-			case 'F':
635
-				$html .= '<i class="icon-sex_f_9x9" title="' . I18N::translate('Female') . '"></i>';
636
-				++$females;
637
-				break;
638
-			default:
639
-				$html .= '<i class="icon-sex_u_9x9" title="' . I18N::translateContext('unknown gender', 'Unknown') . '"></i>';
640
-				break;
630
+			    case 'M':
631
+				    $html .= '<i class="icon-sex_m_9x9" title="' . I18N::translate('Male') . '"></i>';
632
+				    ++$males;
633
+				    break;
634
+			    case 'F':
635
+				    $html .= '<i class="icon-sex_f_9x9" title="' . I18N::translate('Female') . '"></i>';
636
+				    ++$females;
637
+				    break;
638
+			    default:
639
+				    $html .= '<i class="icon-sex_u_9x9" title="' . I18N::translateContext('unknown gender', 'Unknown') . '"></i>';
640
+				    break;
641 641
 			}
642 642
 		}
643 643
 		$html .= '<div class="indent">' . $facts . '</div>' . $tag2;
Please login to merge, or discard this patch.
Braces   +6 added lines, -3 removed lines patch added patch discarded remove patch
@@ -549,7 +549,8 @@  discard block
 block discarded – undo
549 549
  *
550 550
  * @return array
551 551
  */
552
-function apply_filter($facts, $filterof, $filtersx) {
552
+function apply_filter($facts, $filterof, $filtersx)
553
+{
553 554
 	$filtered      = array();
554 555
 	$hundred_years = WT_CLIENT_JD - 36525;
555 556
 	foreach ($facts as $fact) {
@@ -595,7 +596,8 @@  discard block
 block discarded – undo
595 596
  *
596 597
  * @return string
597 598
  */
598
-function calendar_fact_text(Fact $fact, $show_places) {
599
+function calendar_fact_text(Fact $fact, $show_places)
600
+{
599 601
 	$text = $fact->getLabel() . ' — ' . $fact->getDate()->display(true, null, false);
600 602
 	if ($fact->anniv) {
601 603
 		$text .= ' (' . I18N::translate('%s year anniversary', $fact->anniv) . ')';
@@ -617,7 +619,8 @@  discard block
 block discarded – undo
617 619
  *
618 620
  * @return string
619 621
  */
620
-function calendar_list_text($list, $tag1, $tag2, $show_sex_symbols) {
622
+function calendar_list_text($list, $tag1, $tag2, $show_sex_symbols)
623
+{
621 624
 	global $males, $females, $WT_TREE;
622 625
 
623 626
 	$html = '';
Please login to merge, or discard this patch.
app/Report/ReportParserGenerate.php 4 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -2743,7 +2743,7 @@
 block discarded – undo
2743 2743
 		$tags      = explode(':', $tag);
2744 2744
 		$origlevel = $level;
2745 2745
 		if ($level == 0) {
2746
-			$level = $gedrec{0} + 1;
2746
+			$level = $gedrec{0} +1;
2747 2747
 		}
2748 2748
 
2749 2749
 		$subrec = $gedrec;
Please login to merge, or discard this patch.
Indentation   +2790 added lines, -2790 removed lines patch added patch discarded remove patch
@@ -35,2799 +35,2799 @@
 block discarded – undo
35 35
  * Class ReportParserGenerate - parse a report.xml file and generate the report.
36 36
  */
37 37
 class ReportParserGenerate extends ReportParserBase {
38
-	/** @var bool Are we collecting data from <Footnote> elements  */
39
-	private $process_footnote = true;
38
+    /** @var bool Are we collecting data from <Footnote> elements  */
39
+    private $process_footnote = true;
40 40
 
41
-	/** @var bool Are we currently outputing data? */
42
-	private $print_data = false;
41
+    /** @var bool Are we currently outputing data? */
42
+    private $print_data = false;
43 43
 
44
-	/** @var bool[] Push-down stack of $print_data */
45
-	private $print_data_stack = array();
44
+    /** @var bool[] Push-down stack of $print_data */
45
+    private $print_data_stack = array();
46 46
 
47
-	/** @var int Are we processing GEDCOM data */
48
-	private $process_gedcoms = 0;
47
+    /** @var int Are we processing GEDCOM data */
48
+    private $process_gedcoms = 0;
49 49
 
50
-	/** @var int Are we processing conditionals */
51
-	private $process_ifs = 0;
50
+    /** @var int Are we processing conditionals */
51
+    private $process_ifs = 0;
52 52
 
53
-	/** @var int Are we processing repeats*/
54
-	private $process_repeats = 0;
55
-
56
-	/** @var int Quantity of data to repeat during loops */
57
-	private $repeat_bytes = 0;
58
-
59
-	/** @var array[] Repeated data when iterating over loops */
60
-	private $repeats = array();
61
-
62
-	/** @var array[] Nested repeating data */
63
-	private $repeats_stack = array();
64
-
65
-	/** @var ReportBase[] Nested repeating data */
66
-	private $wt_report_stack = array();
67
-
68
-	/** @var resource Nested repeating data */
69
-	private $parser;
70
-
71
-	/** @var resource[] Nested repeating data */
72
-	private $parser_stack = array();
73
-
74
-	/** @var string The current GEDCOM record */
75
-	private $gedrec = '';
76
-
77
-	/** @var string[] Nested GEDCOM records */
78
-	private $gedrec_stack = array();
79
-
80
-	/** @var ReportBaseElement The currently processed element */
81
-	private $current_element;
82
-
83
-	/** @var ReportBaseElement The currently processed element */
84
-	private $footnote_element;
85
-
86
-	/** @var string The GEDCOM fact currently being processed */
87
-	private $fact = '';
88
-
89
-	/** @var string The GEDCOM value currently being processed */
90
-	private $desc = '';
91
-
92
-	/** @var string The GEDCOM type currently being processed */
93
-	private $type = '';
94
-
95
-	/** @var int The current generational level */
96
-	private $generation = 1;
97
-
98
-	/** @var array Source data for processing lists */
99
-	private $list = array();
100
-
101
-	/** @var int Number of items in lists */
102
-	private $list_total = 0;
103
-
104
-	/** @var int Number of items filtered from lists */
105
-	private $list_private = 0;
106
-
107
-	/** @var ReportBase A factory for creating report elements */
108
-	private $report_root;
109
-
110
-	/** @var ReportBase Nested report elements */
111
-	private $wt_report;
112
-
113
-	/** @todo This attribute is public to support the PHP5.3 closure workaround. */
114
-	/** @var string[][] Variables defined in the report at run-time */
115
-	public $vars;
116
-
117
-	/**
118
-	 * Create a parser for a report
119
-	 *
120
-	 * @param string     $report     The XML filename
121
-	 * @param ReportBase $report_root
122
-	 * @param string[][] $vars
123
-	 */
124
-	public function __construct($report, ReportBase $report_root = null, array $vars = array()) {
125
-		$this->report_root     = $report_root;
126
-		$this->wt_report       = $report_root;
127
-		$this->current_element = new ReportBaseElement;
128
-		$this->vars            = $vars;
129
-		parent::__construct($report);
130
-	}
131
-
132
-	/**
133
-	 * XML start element handler
134
-	 *
135
-	 * This function is called whenever a starting element is reached
136
-	 * The element handler will be called if found, otherwise it must be HTML
137
-	 *
138
-	 * @param resource $parser the resource handler for the XML parser
139
-	 * @param string   $name   the name of the XML element parsed
140
-	 * @param array    $attrs  an array of key value pairs for the attributes
141
-	 */
142
-	protected function startElement($parser, $name, $attrs) {
143
-		$newattrs = array();
144
-
145
-		foreach ($attrs as $key => $value) {
146
-			if (preg_match("/^\\$(\w+)$/", $value, $match)) {
147
-				if ((isset($this->vars[$match[1]]['id'])) && (!isset($this->vars[$match[1]]['gedcom']))) {
148
-					$value = $this->vars[$match[1]]['id'];
149
-				}
150
-			}
151
-			$newattrs[$key] = $value;
152
-		}
153
-		$attrs = $newattrs;
154
-		if ($this->process_footnote && ($this->process_ifs === 0 || $name === "if") && ($this->process_gedcoms === 0 || $name === "Gedcom") && ($this->process_repeats === 0 || $name === "Facts" || $name === "RepeatTag")) {
155
-			$start_method = $name . 'StartHandler';
156
-			$end_method   = $name . 'EndHandler';
157
-			if (method_exists($this, $start_method)) {
158
-				$this->$start_method($attrs);
159
-			} elseif (!method_exists($this, $end_method)) {
160
-				$this->htmlStartHandler($name, $attrs);
161
-			}
162
-		}
163
-	}
164
-
165
-	/**
166
-	 * XML end element handler
167
-	 *
168
-	 * This function is called whenever an ending element is reached
169
-	 * The element handler will be called if found, otherwise it must be HTML
170
-	 *
171
-	 * @param resource $parser the resource handler for the XML parser
172
-	 * @param string   $name   the name of the XML element parsed
173
-	 */
174
-	protected function endElement($parser, $name) {
175
-		if (($this->process_footnote || $name === "Footnote") && ($this->process_ifs === 0 || $name === "if") && ($this->process_gedcoms === 0 || $name === "Gedcom") && ($this->process_repeats === 0 || $name === "Facts" || $name === "RepeatTag" || $name === "List" || $name === "Relatives")) {
176
-			$start_method = $name . 'StartHandler';
177
-			$end_method   = $name . 'EndHandler';
178
-			if (method_exists($this, $end_method)) {
179
-				$this->$end_method();
180
-			} elseif (!method_exists($this, $start_method)) {
181
-				$this->htmlEndHandler($name);
182
-			}
183
-		}
184
-	}
185
-
186
-	/**
187
-	 * XML character data handler
188
-	 *
189
-	 * @param resource $parser the resource handler for the XML parser
190
-	 * @param string   $data   the name of the XML element parsed
191
-	 */
192
-	protected function characterData($parser, $data) {
193
-		if ($this->print_data && $this->process_gedcoms === 0 && $this->process_ifs === 0 && $this->process_repeats === 0) {
194
-			$this->current_element->addText($data);
195
-		}
196
-	}
197
-
198
-	/**
199
-	 * XML <style>
200
-	 *
201
-	 * @param array $attrs an array of key value pairs for the attributes
202
-	 */
203
-	private function styleStartHandler($attrs) {
204
-		if (empty($attrs['name'])) {
205
-			throw new \DomainException('REPORT ERROR Style: The "name" of the style is missing or not set in the XML file.');
206
-		}
207
-
208
-		// array Style that will be passed on
209
-		$s = array();
210
-
211
-		// string Name af the style
212
-		$s['name'] = $attrs['name'];
213
-
214
-		// string Name of the DEFAULT font
215
-		$s['font'] = $this->wt_report->defaultFont;
216
-		if (!empty($attrs['font'])) {
217
-			$s['font'] = $attrs['font'];
218
-		}
219
-
220
-		// int The size of the font in points
221
-		$s['size'] = $this->wt_report->defaultFontSize;
222
-		if (!empty($attrs['size'])) {
223
-			$s['size'] = (int) $attrs['size'];
224
-		} // Get it as int to ignore all decimal points or text (if any text then int(0))
225
-
226
-		// string B: bold, I: italic, U: underline, D: line trough, The default value is regular.
227
-		$s['style'] = "";
228
-		if (!empty($attrs['style'])) {
229
-			$s['style'] = $attrs['style'];
230
-		}
231
-
232
-		$this->wt_report->addStyle($s);
233
-	}
234
-
235
-	/**
236
-	 * XML <Doc>
237
-	 *
238
-	 * Sets up the basics of the document proparties
239
-	 *
240
-	 * @param array $attrs an array of key value pairs for the attributes
241
-	 */
242
-	private function docStartHandler($attrs) {
243
-		$this->parser = $this->xml_parser;
244
-
245
-		// Custom page width
246
-		if (!empty($attrs['customwidth'])) {
247
-			$this->wt_report->pagew = (int) $attrs['customwidth'];
248
-		} // Get it as int to ignore all decimal points or text (if any text then int(0))
249
-		// Custom Page height
250
-		if (!empty($attrs['customheight'])) {
251
-			$this->wt_report->pageh = (int) $attrs['customheight'];
252
-		} // Get it as int to ignore all decimal points or text (if any text then int(0))
253
-
254
-		// Left Margin
255
-		if (isset($attrs['leftmargin'])) {
256
-			if ($attrs['leftmargin'] === "0") {
257
-				$this->wt_report->leftmargin = 0;
258
-			} elseif (!empty($attrs['leftmargin'])) {
259
-				$this->wt_report->leftmargin = (int) $attrs['leftmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
260
-			}
261
-		}
262
-		// Right Margin
263
-		if (isset($attrs['rightmargin'])) {
264
-			if ($attrs['rightmargin'] === "0") {
265
-				$this->wt_report->rightmargin = 0;
266
-			} elseif (!empty($attrs['rightmargin'])) {
267
-				$this->wt_report->rightmargin = (int) $attrs['rightmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
268
-			}
269
-		}
270
-		// Top Margin
271
-		if (isset($attrs['topmargin'])) {
272
-			if ($attrs['topmargin'] === "0") {
273
-				$this->wt_report->topmargin = 0;
274
-			} elseif (!empty($attrs['topmargin'])) {
275
-				$this->wt_report->topmargin = (int) $attrs['topmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
276
-			}
277
-		}
278
-		// Bottom Margin
279
-		if (isset($attrs['bottommargin'])) {
280
-			if ($attrs['bottommargin'] === "0") {
281
-				$this->wt_report->bottommargin = 0;
282
-			} elseif (!empty($attrs['bottommargin'])) {
283
-				$this->wt_report->bottommargin = (int) $attrs['bottommargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
284
-			}
285
-		}
286
-		// Header Margin
287
-		if (isset($attrs['headermargin'])) {
288
-			if ($attrs['headermargin'] === "0") {
289
-				$this->wt_report->headermargin = 0;
290
-			} elseif (!empty($attrs['headermargin'])) {
291
-				$this->wt_report->headermargin = (int) $attrs['headermargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
292
-			}
293
-		}
294
-		// Footer Margin
295
-		if (isset($attrs['footermargin'])) {
296
-			if ($attrs['footermargin'] === "0") {
297
-				$this->wt_report->footermargin = 0;
298
-			} elseif (!empty($attrs['footermargin'])) {
299
-				$this->wt_report->footermargin = (int) $attrs['footermargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
300
-			}
301
-		}
302
-
303
-		// Page Orientation
304
-		if (!empty($attrs['orientation'])) {
305
-			if ($attrs['orientation'] == "landscape") {
306
-				$this->wt_report->orientation = "landscape";
307
-			} elseif ($attrs['orientation'] == "portrait") {
308
-				$this->wt_report->orientation = "portrait";
309
-			}
310
-		}
311
-		// Page Size
312
-		if (!empty($attrs['pageSize'])) {
313
-			$this->wt_report->pageFormat = strtoupper($attrs['pageSize']);
314
-		}
315
-
316
-		// Show Generated By...
317
-		if (isset($attrs['showGeneratedBy'])) {
318
-			if ($attrs['showGeneratedBy'] === "0") {
319
-				$this->wt_report->showGenText = false;
320
-			} elseif ($attrs['showGeneratedBy'] === "1") {
321
-				$this->wt_report->showGenText = true;
322
-			}
323
-		}
324
-
325
-		$this->wt_report->setup();
326
-	}
327
-
328
-	/**
329
-	 * XML </Doc>
330
-	 */
331
-	private function docEndHandler() {
332
-		$this->wt_report->run();
333
-	}
334
-
335
-	/**
336
-	 * XML <Header>
337
-	 */
338
-	private function headerStartHandler() {
339
-		// Clear the Header before any new elements are added
340
-		$this->wt_report->clearHeader();
341
-		$this->wt_report->setProcessing("H");
342
-	}
343
-
344
-	/**
345
-	 * XML <PageHeader>
346
-	 */
347
-	private function pageHeaderStartHandler() {
348
-		array_push($this->print_data_stack, $this->print_data);
349
-		$this->print_data = false;
350
-		array_push($this->wt_report_stack, $this->wt_report);
351
-		$this->wt_report = $this->report_root->createPageHeader();
352
-	}
353
-
354
-	/**
355
-	 * XML <pageHeaderEndHandler>
356
-	 */
357
-	private function pageHeaderEndHandler() {
358
-		$this->print_data        = array_pop($this->print_data_stack);
359
-		$this->current_element   = $this->wt_report;
360
-		$this->wt_report         = array_pop($this->wt_report_stack);
361
-		$this->wt_report->addElement($this->current_element);
362
-	}
363
-
364
-	/**
365
-	 * XML <bodyStartHandler>
366
-	 */
367
-	private function bodyStartHandler() {
368
-		$this->wt_report->setProcessing("B");
369
-	}
370
-
371
-	/**
372
-	 * XML <footerStartHandler>
373
-	 */
374
-	private function footerStartHandler() {
375
-		$this->wt_report->setProcessing("F");
376
-	}
377
-
378
-	/**
379
-	 * XML <Cell>
380
-	 *
381
-	 * @param array $attrs an array of key value pairs for the attributes
382
-	 */
383
-	private function cellStartHandler($attrs) {
384
-		// string The text alignment of the text in this box.
385
-		$align = "";
386
-		if (!empty($attrs['align'])) {
387
-			$align = $attrs['align'];
388
-			// RTL supported left/right alignment
389
-			if ($align == "rightrtl") {
390
-				if ($this->wt_report->rtl) {
391
-					$align = "left";
392
-				} else {
393
-					$align = "right";
394
-				}
395
-			} elseif ($align == "leftrtl") {
396
-				if ($this->wt_report->rtl) {
397
-					$align = "right";
398
-				} else {
399
-					$align = "left";
400
-				}
401
-			}
402
-		}
403
-
404
-		// string The color to fill the background of this cell
405
-		$bgcolor = "";
406
-		if (!empty($attrs['bgcolor'])) {
407
-			$bgcolor = $attrs['bgcolor'];
408
-		}
409
-
410
-		// int Whether or not the background should be painted
411
-		$fill = 1;
412
-		if (isset($attrs['fill'])) {
413
-			if ($attrs['fill'] === "0") {
414
-				$fill = 0;
415
-			} elseif ($attrs['fill'] === "1") {
416
-				$fill = 1;
417
-			}
418
-		}
419
-
420
-		$reseth = true;
421
-		// boolean   if true reset the last cell height (default true)
422
-		if (isset($attrs['reseth'])) {
423
-			if ($attrs['reseth'] === "0") {
424
-				$reseth = false;
425
-			} elseif ($attrs['reseth'] === "1") {
426
-				$reseth = true;
427
-			}
428
-		}
429
-
430
-		// mixed Whether or not a border should be printed around this box
431
-		$border = 0;
432
-		if (!empty($attrs['border'])) {
433
-			$border = $attrs['border'];
434
-		}
435
-		// string Border color in HTML code
436
-		$bocolor = "";
437
-		if (!empty($attrs['bocolor'])) {
438
-			$bocolor = $attrs['bocolor'];
439
-		}
440
-
441
-		// int Cell height (expressed in points) The starting height of this cell. If the text wraps the height will automatically be adjusted.
442
-		$height = 0;
443
-		if (!empty($attrs['height'])) {
444
-			$height = (int) $attrs['height'];
445
-		}
446
-		// int Cell width (expressed in points) Setting the width to 0 will make it the width from the current location to the right margin.
447
-		$width = 0;
448
-		if (!empty($attrs['width'])) {
449
-			$width = (int) $attrs['width'];
450
-		}
451
-
452
-		// int Stretch carachter mode
453
-		$stretch = 0;
454
-		if (!empty($attrs['stretch'])) {
455
-			$stretch = (int) $attrs['stretch'];
456
-		}
457
-
458
-		// mixed Position the left corner of this box on the page. The default is the current position.
459
-		$left = ".";
460
-		if (isset($attrs['left'])) {
461
-			if ($attrs['left'] === ".") {
462
-				$left = ".";
463
-			} elseif (!empty($attrs['left'])) {
464
-				$left = (int) $attrs['left'];
465
-			} elseif ($attrs['left'] === "0") {
466
-				$left = 0;
467
-			}
468
-		}
469
-		// mixed Position the top corner of this box on the page. the default is the current position
470
-		$top = ".";
471
-		if (isset($attrs['top'])) {
472
-			if ($attrs['top'] === ".") {
473
-				$top = ".";
474
-			} elseif (!empty($attrs['top'])) {
475
-				$top = (int) $attrs['top'];
476
-			} elseif ($attrs['top'] === "0") {
477
-				$top = 0;
478
-			}
479
-		}
480
-
481
-		// string The name of the Style that should be used to render the text.
482
-		$style = "";
483
-		if (!empty($attrs['style'])) {
484
-			$style = $attrs['style'];
485
-		}
486
-
487
-		// string Text color in html code
488
-		$tcolor = "";
489
-		if (!empty($attrs['tcolor'])) {
490
-			$tcolor = $attrs['tcolor'];
491
-		}
492
-
493
-		// int Indicates where the current position should go after the call.
494
-		$ln = 0;
495
-		if (isset($attrs['newline'])) {
496
-			if (!empty($attrs['newline'])) {
497
-				$ln = (int) $attrs['newline'];
498
-			} elseif ($attrs['newline'] === "0") {
499
-				$ln = 0;
500
-			}
501
-		}
502
-
503
-		if ($align == "left") {
504
-			$align = "L";
505
-		} elseif ($align == "right") {
506
-			$align = "R";
507
-		} elseif ($align == "center") {
508
-			$align = "C";
509
-		} elseif ($align == "justify") {
510
-			$align = "J";
511
-		}
512
-
513
-		array_push($this->print_data_stack, $this->print_data);
514
-		$this->print_data = true;
515
-
516
-		$this->current_element = $this->report_root->createCell(
517
-			$width,
518
-			$height,
519
-			$border,
520
-			$align,
521
-			$bgcolor,
522
-			$style,
523
-			$ln,
524
-			$top,
525
-			$left,
526
-			$fill,
527
-			$stretch,
528
-			$bocolor,
529
-			$tcolor,
530
-			$reseth
531
-		);
532
-	}
533
-
534
-	/**
535
-	 * XML </Cell>
536
-	 */
537
-	private function cellEndHandler() {
538
-		$this->print_data = array_pop($this->print_data_stack);
539
-		$this->wt_report->addElement($this->current_element);
540
-	}
541
-
542
-	/**
543
-	 * XML <Now /> element handler
544
-	 */
545
-	private function nowStartHandler() {
546
-		$g = FunctionsDate::timestampToGedcomDate(WT_TIMESTAMP + WT_TIMESTAMP_OFFSET);
547
-		$this->current_element->addText($g->display());
548
-	}
549
-
550
-	/**
551
-	 * XML <PageNum /> element handler
552
-	 */
553
-	private function pageNumStartHandler() {
554
-		$this->current_element->addText("#PAGENUM#");
555
-	}
556
-
557
-	/**
558
-	 * XML <TotalPages /> element handler
559
-	 */
560
-	private function totalPagesStartHandler() {
561
-		$this->current_element->addText("{{:ptp:}}");
562
-	}
563
-
564
-	/**
565
-	 * Called at the start of an element.
566
-	 *
567
-	 * @param array $attrs an array of key value pairs for the attributes
568
-	 */
569
-	private function gedcomStartHandler($attrs) {
570
-		global $WT_TREE;
571
-
572
-		if ($this->process_gedcoms > 0) {
573
-			$this->process_gedcoms++;
574
-
575
-			return;
576
-		}
577
-
578
-		$tag       = $attrs['id'];
579
-		$tag       = str_replace("@fact", $this->fact, $tag);
580
-		$tags      = explode(":", $tag);
581
-		$newgedrec = '';
582
-		if (count($tags) < 2) {
583
-			$tmp       = GedcomRecord::getInstance($attrs['id'], $WT_TREE);
584
-			$newgedrec = $tmp ? $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE)) : '';
585
-		}
586
-		if (empty($newgedrec)) {
587
-			$tgedrec   = $this->gedrec;
588
-			$newgedrec = '';
589
-			foreach ($tags as $tag) {
590
-				if (preg_match("/\\$(.+)/", $tag, $match)) {
591
-					if (isset($this->vars[$match[1]]['gedcom'])) {
592
-						$newgedrec = $this->vars[$match[1]]['gedcom'];
593
-					} else {
594
-						$tmp       = GedcomRecord::getInstance($match[1], $WT_TREE);
595
-						$newgedrec = $tmp ? $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE)) : '';
596
-					}
597
-				} else {
598
-					if (preg_match("/@(.+)/", $tag, $match)) {
599
-						$gmatch = array();
600
-						if (preg_match("/\d $match[1] @([^@]+)@/", $tgedrec, $gmatch)) {
601
-							$tmp       = GedcomRecord::getInstance($gmatch[1], $WT_TREE);
602
-							$newgedrec = $tmp ? $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE)) : '';
603
-							$tgedrec   = $newgedrec;
604
-						} else {
605
-							$newgedrec = '';
606
-							break;
607
-						}
608
-					} else {
609
-						$temp      = explode(" ", trim($tgedrec));
610
-						$level     = $temp[0] + 1;
611
-						$newgedrec = Functions::getSubRecord($level, "$level $tag", $tgedrec);
612
-						$tgedrec   = $newgedrec;
613
-					}
614
-				}
615
-			}
616
-		}
617
-		if (!empty($newgedrec)) {
618
-			array_push($this->gedrec_stack, array($this->gedrec, $this->fact, $this->desc));
619
-			$this->gedrec = $newgedrec;
620
-			if (preg_match("/(\d+) (_?[A-Z0-9]+) (.*)/", $this->gedrec, $match)) {
621
-				$this->fact = $match[2];
622
-				$this->desc = trim($match[3]);
623
-			}
624
-		} else {
625
-			$this->process_gedcoms++;
626
-		}
627
-	}
628
-
629
-	/**
630
-	 * Called at the end of an element.
631
-	 */
632
-	private function gedcomEndHandler() {
633
-		if ($this->process_gedcoms > 0) {
634
-			$this->process_gedcoms--;
635
-		} else {
636
-			list($this->gedrec, $this->fact, $this->desc) = array_pop($this->gedrec_stack);
637
-		}
638
-	}
639
-
640
-	/**
641
-	 * XML <textBoxStartHandler>
642
-	 *
643
-	 * @param array $attrs an array of key value pairs for the attributes
644
-	 */
645
-	private function textBoxStartHandler($attrs) {
646
-		// string Background color code
647
-		$bgcolor = "";
648
-		if (!empty($attrs['bgcolor'])) {
649
-			$bgcolor = $attrs['bgcolor'];
650
-		}
651
-
652
-		// boolean Wether or not fill the background color
653
-		$fill = true;
654
-		if (isset($attrs['fill'])) {
655
-			if ($attrs['fill'] === "0") {
656
-				$fill = false;
657
-			} elseif ($attrs['fill'] === "1") {
658
-				$fill = true;
659
-			}
660
-		}
661
-
662
-		// var boolean Whether or not a border should be printed around this box. 0 = no border, 1 = border. Default is 0
663
-		$border = false;
664
-		if (isset($attrs['border'])) {
665
-			if ($attrs['border'] === "1") {
666
-				$border = true;
667
-			} elseif ($attrs['border'] === "0") {
668
-				$border = false;
669
-			}
670
-		}
671
-
672
-		// int The starting height of this cell. If the text wraps the height will automatically be adjusted
673
-		$height = 0;
674
-		if (!empty($attrs['height'])) {
675
-			$height = (int) $attrs['height'];
676
-		}
677
-		// int Setting the width to 0 will make it the width from the current location to the margin
678
-		$width = 0;
679
-		if (!empty($attrs['width'])) {
680
-			$width = (int) $attrs['width'];
681
-		}
682
-
683
-		// mixed Position the left corner of this box on the page. The default is the current position.
684
-		$left = ".";
685
-		if (isset($attrs['left'])) {
686
-			if ($attrs['left'] === ".") {
687
-				$left = ".";
688
-			} elseif (!empty($attrs['left'])) {
689
-				$left = (int) $attrs['left'];
690
-			} elseif ($attrs['left'] === "0") {
691
-				$left = 0;
692
-			}
693
-		}
694
-		// mixed Position the top corner of this box on the page. the default is the current position
695
-		$top = ".";
696
-		if (isset($attrs['top'])) {
697
-			if ($attrs['top'] === ".") {
698
-				$top = ".";
699
-			} elseif (!empty($attrs['top'])) {
700
-				$top = (int) $attrs['top'];
701
-			} elseif ($attrs['top'] === "0") {
702
-				$top = 0;
703
-			}
704
-		}
705
-		// boolean After this box is finished rendering, should the next section of text start immediately after the this box or should it start on a new line under this box. 0 = no new line, 1 = force new line. Default is 0
706
-		$newline = false;
707
-		if (isset($attrs['newline'])) {
708
-			if ($attrs['newline'] === "1") {
709
-				$newline = true;
710
-			} elseif ($attrs['newline'] === "0") {
711
-				$newline = false;
712
-			}
713
-		}
714
-		// boolean
715
-		$pagecheck = true;
716
-		if (isset($attrs['pagecheck'])) {
717
-			if ($attrs['pagecheck'] === "0") {
718
-				$pagecheck = false;
719
-			} elseif ($attrs['pagecheck'] === "1") {
720
-				$pagecheck = true;
721
-			}
722
-		}
723
-		// boolean Cell padding
724
-		$padding = true;
725
-		if (isset($attrs['padding'])) {
726
-			if ($attrs['padding'] === "0") {
727
-				$padding = false;
728
-			} elseif ($attrs['padding'] === "1") {
729
-				$padding = true;
730
-			}
731
-		}
732
-		// boolean Reset this box Height
733
-		$reseth = false;
734
-		if (isset($attrs['reseth'])) {
735
-			if ($attrs['reseth'] === "1") {
736
-				$reseth = true;
737
-			} elseif ($attrs['reseth'] === "0") {
738
-				$reseth = false;
739
-			}
740
-		}
741
-
742
-		// string Style of rendering
743
-		$style = "";
744
-
745
-		array_push($this->print_data_stack, $this->print_data);
746
-		$this->print_data = false;
747
-
748
-		array_push($this->wt_report_stack, $this->wt_report);
749
-		$this->wt_report = $this->report_root->createTextBox(
750
-			$width,
751
-			$height,
752
-			$border,
753
-			$bgcolor,
754
-			$newline,
755
-			$left,
756
-			$top,
757
-			$pagecheck,
758
-			$style,
759
-			$fill,
760
-			$padding,
761
-			$reseth
762
-		);
763
-	}
764
-
765
-	/**
766
-	 * XML <textBoxEndHandler>
767
-	 */
768
-	private function textBoxEndHandler() {
769
-		$this->print_data      = array_pop($this->print_data_stack);
770
-		$this->current_element = $this->wt_report;
771
-		$this->wt_report       = array_pop($this->wt_report_stack);
772
-		$this->wt_report->addElement($this->current_element);
773
-	}
774
-
775
-	/**
776
-	 * XLM <Text>.
777
-	 *
778
-	 * @param array $attrs an array of key value pairs for the attributes
779
-	 */
780
-	private function textStartHandler($attrs) {
781
-		array_push($this->print_data_stack, $this->print_data);
782
-		$this->print_data = true;
783
-
784
-		// string The name of the Style that should be used to render the text.
785
-		$style = "";
786
-		if (!empty($attrs['style'])) {
787
-			$style = $attrs['style'];
788
-		}
789
-
790
-		// string  The color of the text - Keep the black color as default
791
-		$color = "";
792
-		if (!empty($attrs['color'])) {
793
-			$color = $attrs['color'];
794
-		}
795
-
796
-		$this->current_element = $this->report_root->createText($style, $color);
797
-	}
798
-
799
-	/**
800
-	 * XML </Text>
801
-	 */
802
-	private function textEndHandler() {
803
-		$this->print_data = array_pop($this->print_data_stack);
804
-		$this->wt_report->addElement($this->current_element);
805
-	}
806
-
807
-	/**
808
-	 * XML <GetPersonName/>
809
-	 *
810
-	 * Get the name
811
-	 * 1. id is empty - current GEDCOM record
812
-	 * 2. id is set with a record id
813
-	 *
814
-	 * @param array $attrs an array of key value pairs for the attributes
815
-	 */
816
-	private function getPersonNameStartHandler($attrs) {
817
-		global $WT_TREE;
818
-
819
-		$id    = "";
820
-		$match = array();
821
-		if (empty($attrs['id'])) {
822
-			if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
823
-				$id = $match[1];
824
-			}
825
-		} else {
826
-			if (preg_match("/\\$(.+)/", $attrs['id'], $match)) {
827
-				if (isset($this->vars[$match[1]]['id'])) {
828
-					$id = $this->vars[$match[1]]['id'];
829
-				}
830
-			} else {
831
-				if (preg_match("/@(.+)/", $attrs['id'], $match)) {
832
-					$gmatch = array();
833
-					if (preg_match("/\d $match[1] @([^@]+)@/", $this->gedrec, $gmatch)) {
834
-						$id = $gmatch[1];
835
-					}
836
-				} else {
837
-					$id = $attrs['id'];
838
-				}
839
-			}
840
-		}
841
-		if (!empty($id)) {
842
-			$record = GedcomRecord::getInstance($id, $WT_TREE);
843
-			if (is_null($record)) {
844
-				return;
845
-			}
846
-			if (!$record->canShowName()) {
847
-				$this->current_element->addText(I18N::translate('Private'));
848
-			} else {
849
-				$name = $record->getFullName();
850
-				$name = preg_replace(
851
-					array('/<span class="starredname">/', '/<\/span><\/span>/', '/<\/span>/'),
852
-					array('«', '', '»'),
853
-					$name
854
-				);
855
-				$name = strip_tags($name);
856
-				if (!empty($attrs['truncate'])) {
857
-					if (mb_strlen($name) > $attrs['truncate']) {
858
-						$name  = preg_replace("/\(.*\) ?/", '', $name); //removes () and text inbetween - what about ", [ and { etc?
859
-						$words = preg_split('/[, -]+/', $name); // names separated with space, comma or hyphen - any others?
860
-						$name  = $words[count($words) - 1];
861
-						for ($i = count($words) - 2; $i >= 0; $i--) {
862
-							$len = mb_strlen($name);
863
-							for ($j = count($words) - 3; $j >= 0; $j--) {
864
-								$len += mb_strlen($words[$j]);
865
-							}
866
-							if ($len > $attrs['truncate']) {
867
-								$first_letter = mb_substr($words[$i], 0, 1);
868
-								// Do not show " of nick-names
869
-								if ($first_letter != "\"") {
870
-									$name = mb_substr($words[$i], 0, 1) . '. ' . $name;
871
-								}
872
-							} else {
873
-								$name = $words[$i] . ' ' . $name;
874
-							}
875
-						}
876
-					}
877
-				} else {
878
-					$addname = $record->getAddName();
879
-					$addname = preg_replace(
880
-						array('/<span class="starredname">/', '/<\/span><\/span>/', '/<\/span>/'),
881
-						array('«', '', '»'),
882
-						$addname
883
-					);
884
-					$addname = strip_tags($addname);
885
-					if (!empty($addname)) {
886
-						$name .= " " . $addname;
887
-					}
888
-				}
889
-				$this->current_element->addText(trim($name));
890
-			}
891
-		}
892
-	}
893
-
894
-	/**
895
-	 * XML <GedcomValue/>
896
-	 *
897
-	 * @param array $attrs an array of key value pairs for the attributes
898
-	 */
899
-	private function gedcomValueStartHandler($attrs) {
900
-		global $WT_TREE;
901
-
902
-		$id    = "";
903
-		$match = array();
904
-		if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
905
-			$id = $match[1];
906
-		}
907
-
908
-		if (isset($attrs['newline']) && $attrs['newline'] == "1") {
909
-			$useBreak = "1";
910
-		} else {
911
-			$useBreak = "0";
912
-		}
913
-
914
-		$tag = $attrs['tag'];
915
-		if (!empty($tag)) {
916
-			if ($tag == "@desc") {
917
-				$value = $this->desc;
918
-				$value = trim($value);
919
-				$this->current_element->addText($value);
920
-			}
921
-			if ($tag == "@id") {
922
-				$this->current_element->addText($id);
923
-			} else {
924
-				$tag = str_replace("@fact", $this->fact, $tag);
925
-				if (empty($attrs['level'])) {
926
-					$temp  = explode(" ", trim($this->gedrec));
927
-					$level = $temp[0];
928
-					if ($level == 0) {
929
-						$level++;
930
-					}
931
-				} else {
932
-					$level = $attrs['level'];
933
-				}
934
-				$tags  = preg_split('/[: ]/', $tag);
935
-				$value = $this->getGedcomValue($tag, $level, $this->gedrec);
936
-				switch (end($tags)) {
937
-				case 'DATE':
938
-					$tmp   = new Date($value);
939
-					$value = $tmp->display();
940
-					break;
941
-				case 'PLAC':
942
-					$tmp   = new Place($value, $WT_TREE);
943
-					$value = $tmp->getShortName();
944
-					break;
945
-				}
946
-				if ($useBreak == "1") {
947
-					// Insert <br> when multiple dates exist.
948
-					// This works around a TCPDF bug that incorrectly wraps RTL dates on LTR pages
949
-					$value = str_replace('(', '<br>(', $value);
950
-					$value = str_replace('<span dir="ltr"><br>', '<br><span dir="ltr">', $value);
951
-					$value = str_replace('<span dir="rtl"><br>', '<br><span dir="rtl">', $value);
952
-					if (substr($value, 0, 6) == '<br>') {
953
-						$value = substr($value, 6);
954
-					}
955
-				}
956
-				$tmp = explode(':', $tag);
957
-				if (in_array(end($tmp), array('NOTE', 'TEXT'))) {
958
-					$value = Filter::formatText($value, $WT_TREE); // We'll strip HTML in addText()
959
-				}
960
-				$this->current_element->addText($value);
961
-			}
962
-		}
963
-	}
964
-
965
-	/**
966
-	 * XML <RepeatTag>
967
-	 *
968
-	 * @param array $attrs an array of key value pairs for the attributes
969
-	 */
970
-	private function repeatTagStartHandler($attrs) {
971
-		global $WT_TREE;
972
-
973
-		$this->process_repeats++;
974
-		if ($this->process_repeats > 1) {
975
-			return;
976
-		}
977
-
978
-		array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
979
-		$this->repeats      = array();
980
-		$this->repeat_bytes = xml_get_current_line_number($this->parser);
981
-
982
-		$tag = "";
983
-		if (isset($attrs['tag'])) {
984
-			$tag = $attrs['tag'];
985
-		}
986
-		if (!empty($tag)) {
987
-			if ($tag == "@desc") {
988
-				$value = $this->desc;
989
-				$value = trim($value);
990
-				$this->current_element->addText($value);
991
-			} else {
992
-				$tag   = str_replace("@fact", $this->fact, $tag);
993
-				$tags  = explode(":", $tag);
994
-				$temp  = explode(" ", trim($this->gedrec));
995
-				$level = $temp[0];
996
-				if ($level == 0) {
997
-					$level++;
998
-				}
999
-				$subrec = $this->gedrec;
1000
-				$t      = $tag;
1001
-				$count  = count($tags);
1002
-				$i      = 0;
1003
-				while ($i < $count) {
1004
-					$t = $tags[$i];
1005
-					if (!empty($t)) {
1006
-						if ($i < ($count - 1)) {
1007
-							$subrec = Functions::getSubRecord($level, "$level $t", $subrec);
1008
-							if (empty($subrec)) {
1009
-								$level--;
1010
-								$subrec = Functions::getSubRecord($level, "@ $t", $this->gedrec);
1011
-								if (empty($subrec)) {
1012
-									return;
1013
-								}
1014
-							}
1015
-						}
1016
-						$level++;
1017
-					}
1018
-					$i++;
1019
-				}
1020
-				$level--;
1021
-				$count = preg_match_all("/$level $t(.*)/", $subrec, $match, PREG_SET_ORDER);
1022
-				$i     = 0;
1023
-				while ($i < $count) {
1024
-					$i++;
1025
-					// Privacy check - is this a link, and are we allowed to view the linked object?
1026
-					$subrecord = Functions::getSubRecord($level, "$level $t", $subrec, $i);
1027
-					if (preg_match('/^\d ' . WT_REGEX_TAG . ' @(' . WT_REGEX_XREF . ')@/', $subrecord, $xref_match)) {
1028
-						$linked_object = GedcomRecord::getInstance($xref_match[1], $WT_TREE);
1029
-						if ($linked_object && !$linked_object->canShow()) {
1030
-							continue;
1031
-						}
1032
-					}
1033
-					$this->repeats[] = $subrecord;
1034
-				}
1035
-			}
1036
-		}
1037
-	}
1038
-
1039
-	/**
1040
-	 * XML </ RepeatTag>
1041
-	 */
1042
-	private function repeatTagEndHandler() {
1043
-		global $report;
1044
-
1045
-		$this->process_repeats--;
1046
-		if ($this->process_repeats > 0) {
1047
-			return;
1048
-		}
1049
-
1050
-		// Check if there is anything to repeat
1051
-		if (count($this->repeats) > 0) {
1052
-			// No need to load them if not used...
1053
-
1054
-			$lineoffset = 0;
1055
-			foreach ($this->repeats_stack as $rep) {
1056
-				$lineoffset += $rep[1];
1057
-			}
1058
-			//-- read the xml from the file
1059
-			$lines = file($report);
1060
-			while (strpos($lines[$lineoffset + $this->repeat_bytes], "<RepeatTag") === false) {
1061
-				$lineoffset--;
1062
-			}
1063
-			$lineoffset++;
1064
-			$reportxml = "<tempdoc>\n";
1065
-			$line_nr   = $lineoffset + $this->repeat_bytes;
1066
-			// RepeatTag Level counter
1067
-			$count = 1;
1068
-			while (0 < $count) {
1069
-				if (strstr($lines[$line_nr], "<RepeatTag") !== false) {
1070
-					$count++;
1071
-				} elseif (strstr($lines[$line_nr], "</RepeatTag") !== false) {
1072
-					$count--;
1073
-				}
1074
-				if (0 < $count) {
1075
-					$reportxml .= $lines[$line_nr];
1076
-				}
1077
-				$line_nr++;
1078
-			}
1079
-			// No need to drag this
1080
-			unset($lines);
1081
-			$reportxml .= "</tempdoc>\n";
1082
-			// Save original values
1083
-			array_push($this->parser_stack, $this->parser);
1084
-			$oldgedrec = $this->gedrec;
1085
-			foreach ($this->repeats as $gedrec) {
1086
-				$this->gedrec  = $gedrec;
1087
-				$repeat_parser = xml_parser_create();
1088
-				$this->parser  = $repeat_parser;
1089
-				xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
1090
-				xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
1091
-				xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
1092
-				if (!xml_parse($repeat_parser, $reportxml, true)) {
1093
-					throw new \DomainException(sprintf(
1094
-						'RepeatTagEHandler XML error: %s at line %d',
1095
-						xml_error_string(xml_get_error_code($repeat_parser)),
1096
-						xml_get_current_line_number($repeat_parser)
1097
-					));
1098
-				}
1099
-				xml_parser_free($repeat_parser);
1100
-			}
1101
-			// Restore original values
1102
-			$this->gedrec = $oldgedrec;
1103
-			$this->parser = array_pop($this->parser_stack);
1104
-		}
1105
-		list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
1106
-	}
1107
-
1108
-	/**
1109
-	 * Variable lookup
1110
-	 *
1111
-	 * Retrieve predefined variables :
1112
-	 *
1113
-	 * @ desc GEDCOM fact description, example:
1114
-	 *        1 EVEN This is a description
1115
-	 * @ fact GEDCOM fact tag, such as BIRT, DEAT etc.
1116
-	 * $ I18N::translate('....')
1117
-	 * $ language_settings[]
1118
-	 *
1119
-	 * @param array $attrs an array of key value pairs for the attributes
1120
-	 */
1121
-	private function varStartHandler($attrs) {
1122
-		if (empty($attrs['var'])) {
1123
-			throw new \DomainException('REPORT ERROR var: The attribute "var=" is missing or not set in the XML file on line: ' . xml_get_current_line_number($this->parser));
1124
-		}
1125
-
1126
-		$var = $attrs['var'];
1127
-		// SetVar element preset variables
1128
-		if (!empty($this->vars[$var]['id'])) {
1129
-			$var = $this->vars[$var]['id'];
1130
-		} else {
1131
-			$tfact = $this->fact;
1132
-			if (($this->fact === "EVEN" || $this->fact === "FACT") && $this->type !== " ") {
1133
-				// Use :
1134
-				// n TYPE This text if string
1135
-				$tfact = $this->type;
1136
-			}
1137
-			$var = str_replace(array("@fact", "@desc"), array(GedcomTag::getLabel($tfact), $this->desc), $var);
1138
-			if (preg_match('/^I18N::number\((.+)\)$/', $var, $match)) {
1139
-				$var = I18N::number($match[1]);
1140
-			} elseif (preg_match('/^I18N::translate\(\'(.+)\'\)$/', $var, $match)) {
1141
-				$var = I18N::translate($match[1]);
1142
-			} elseif (preg_match('/^I18N::translateContext\(\'(.+)\', *\'(.+)\'\)$/', $var, $match)) {
1143
-				$var = I18N::translateContext($match[1], $match[2]);
1144
-			}
1145
-		}
1146
-		// Check if variable is set as a date and reformat the date
1147
-		if (isset($attrs['date'])) {
1148
-			if ($attrs['date'] === "1") {
1149
-				$g   = new Date($var);
1150
-				$var = $g->display();
1151
-			}
1152
-		}
1153
-		$this->current_element->addText($var);
1154
-		$this->text = $var; // Used for title/descriptio
1155
-	}
1156
-
1157
-	/**
1158
-	 * XML <Facts>
1159
-	 *
1160
-	 * @param array $attrs an array of key value pairs for the attributes
1161
-	 */
1162
-	private function factsStartHandler($attrs) {
1163
-		global $WT_TREE;
1164
-
1165
-		$this->process_repeats++;
1166
-		if ($this->process_repeats > 1) {
1167
-			return;
1168
-		}
1169
-
1170
-		array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
1171
-		$this->repeats      = array();
1172
-		$this->repeat_bytes = xml_get_current_line_number($this->parser);
1173
-
1174
-		$id    = "";
1175
-		$match = array();
1176
-		if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1177
-			$id = $match[1];
1178
-		}
1179
-		$tag = "";
1180
-		if (isset($attrs['ignore'])) {
1181
-			$tag .= $attrs['ignore'];
1182
-		}
1183
-		if (preg_match("/\\$(.+)/", $tag, $match)) {
1184
-			$tag = $this->vars[$match[1]]['id'];
1185
-		}
1186
-
1187
-		$record = GedcomRecord::getInstance($id, $WT_TREE);
1188
-		if (empty($attrs['diff']) && !empty($id)) {
1189
-			$facts = $record->getFacts();
1190
-			Functions::sortFacts($facts);
1191
-			$this->repeats  = array();
1192
-			$nonfacts       = explode(',', $tag);
1193
-			foreach ($facts as $event) {
1194
-				if (!in_array($event->getTag(), $nonfacts)) {
1195
-					$this->repeats[] = $event->getGedcom();
1196
-				}
1197
-			}
1198
-		} else {
1199
-			foreach ($record->getFacts() as $fact) {
1200
-				if ($fact->isPendingAddition() && $fact->getTag() !== 'CHAN') {
1201
-					$this->repeats[] = $fact->getGedcom();
1202
-				}
1203
-			}
1204
-		}
1205
-	}
1206
-
1207
-	/**
1208
-	 * XML </Facts>
1209
-	 */
1210
-	private function factsEndHandler() {
1211
-		global $report;
1212
-
1213
-		$this->process_repeats--;
1214
-		if ($this->process_repeats > 0) {
1215
-			return;
1216
-		}
1217
-
1218
-		// Check if there is anything to repeat
1219
-		if (count($this->repeats) > 0) {
1220
-
1221
-			$line       = xml_get_current_line_number($this->parser) - 1;
1222
-			$lineoffset = 0;
1223
-			foreach ($this->repeats_stack as $rep) {
1224
-				$lineoffset += $rep[1];
1225
-			}
1226
-
1227
-			//-- read the xml from the file
1228
-			$lines = file($report);
1229
-			while ($lineoffset + $this->repeat_bytes > 0 && strpos($lines[$lineoffset + $this->repeat_bytes], '<Facts ') === false) {
1230
-				$lineoffset--;
1231
-			}
1232
-			$lineoffset++;
1233
-			$reportxml = "<tempdoc>\n";
1234
-			$i         = $line + $lineoffset;
1235
-			$line_nr   = $this->repeat_bytes + $lineoffset;
1236
-			while ($line_nr < $i) {
1237
-				$reportxml .= $lines[$line_nr];
1238
-				$line_nr++;
1239
-			}
1240
-			// No need to drag this
1241
-			unset($lines);
1242
-			$reportxml .= "</tempdoc>\n";
1243
-			// Save original values
1244
-			array_push($this->parser_stack, $this->parser);
1245
-			$oldgedrec = $this->gedrec;
1246
-			$count     = count($this->repeats);
1247
-			$i         = 0;
1248
-			while ($i < $count) {
1249
-				$this->gedrec = $this->repeats[$i];
1250
-				$this->fact   = '';
1251
-				$this->desc   = '';
1252
-				if (preg_match('/1 (\w+)(.*)/', $this->gedrec, $match)) {
1253
-					$this->fact = $match[1];
1254
-					if ($this->fact === 'EVEN' || $this->fact === 'FACT') {
1255
-						$tmatch = array();
1256
-						if (preg_match('/2 TYPE (.+)/', $this->gedrec, $tmatch)) {
1257
-							$this->type = trim($tmatch[1]);
1258
-						} else {
1259
-							$this->type = ' ';
1260
-						}
1261
-					}
1262
-					$this->desc = trim($match[2]);
1263
-					$this->desc .= Functions::getCont(2, $this->gedrec);
1264
-				}
1265
-				$repeat_parser = xml_parser_create();
1266
-				$this->parser  = $repeat_parser;
1267
-				xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
1268
-				xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
1269
-				xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
1270
-				if (!xml_parse($repeat_parser, $reportxml, true)) {
1271
-					throw new \DomainException(sprintf(
1272
-						'FactsEHandler XML error: %s at line %d',
1273
-						xml_error_string(xml_get_error_code($repeat_parser)),
1274
-						xml_get_current_line_number($repeat_parser)
1275
-					));
1276
-				}
1277
-				xml_parser_free($repeat_parser);
1278
-				$i++;
1279
-			}
1280
-			// Restore original values
1281
-			$this->parser = array_pop($this->parser_stack);
1282
-			$this->gedrec = $oldgedrec;
1283
-		}
1284
-		list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
1285
-	}
1286
-
1287
-	/**
1288
-	 * Setting upp or changing variables in the XML
1289
-	 * The XML variable name and value is stored in $this->vars
1290
-	 *
1291
-	 * @param array $attrs an array of key value pairs for the attributes
1292
-	 */
1293
-	private function setVarStartHandler($attrs) {
1294
-		if (empty($attrs['name'])) {
1295
-			throw new \DomainException('REPORT ERROR var: The attribute "name" is missing or not set in the XML file');
1296
-		}
1297
-
1298
-		$name  = $attrs['name'];
1299
-		$value = $attrs['value'];
1300
-		$match = array();
1301
-		// Current GEDCOM record strings
1302
-		if ($value == "@ID") {
1303
-			if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1304
-				$value = $match[1];
1305
-			}
1306
-		} elseif ($value == "@fact") {
1307
-			$value = $this->fact;
1308
-		} elseif ($value == "@desc") {
1309
-			$value = $this->desc;
1310
-		} elseif ($value == "@generation") {
1311
-			$value = $this->generation;
1312
-		} elseif (preg_match("/@(\w+)/", $value, $match)) {
1313
-			$gmatch = array();
1314
-			if (preg_match("/\d $match[1] (.+)/", $this->gedrec, $gmatch)) {
1315
-				$value = str_replace("@", "", trim($gmatch[1]));
1316
-			}
1317
-		}
1318
-		if (preg_match("/\\$(\w+)/", $name, $match)) {
1319
-			$name = $this->vars["'" . $match[1] . "'"]['id'];
1320
-		}
1321
-		$count = preg_match_all("/\\$(\w+)/", $value, $match, PREG_SET_ORDER);
1322
-		$i     = 0;
1323
-		while ($i < $count) {
1324
-			$t     = $this->vars[$match[$i][1]]['id'];
1325
-			$value = preg_replace("/\\$" . $match[$i][1] . "/", $t, $value, 1);
1326
-			$i++;
1327
-		}
1328
-		if (preg_match('/^I18N::number\((.+)\)$/', $value, $match)) {
1329
-			$value = I18N::number($match[1]);
1330
-		} elseif (preg_match('/^I18N::translate\(\'(.+)\'\)$/', $value, $match)) {
1331
-			$value = I18N::translate($match[1]);
1332
-		} elseif (preg_match('/^I18N::translateContext\(\'(.+)\', *\'(.+)\'\)$/', $value, $match)) {
1333
-			$value = I18N::translateContext($match[1], $match[2]);
1334
-		}
1335
-		// Arithmetic functions
1336
-		if (preg_match("/(\d+)\s*([\-\+\*\/])\s*(\d+)/", $value, $match)) {
1337
-			switch ($match[2]) {
1338
-			case "+":
1339
-				$t     = $match[1] + $match[3];
1340
-				$value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1341
-				break;
1342
-			case "-":
1343
-				$t     = $match[1] - $match[3];
1344
-				$value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1345
-				break;
1346
-			case "*":
1347
-				$t     = $match[1] * $match[3];
1348
-				$value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1349
-				break;
1350
-			case "/":
1351
-				$t     = $match[1] / $match[3];
1352
-				$value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1353
-				break;
1354
-			}
1355
-		}
1356
-		if (strpos($value, "@") !== false) {
1357
-			$value = "";
1358
-		}
1359
-		$this->vars[$name]['id'] = $value;
1360
-	}
1361
-
1362
-	/**
1363
-	 * XML <if > start element
1364
-	 *
1365
-	 * @param array $attrs an array of key value pairs for the attributes
1366
-	 */
1367
-	private function ifStartHandler($attrs) {
1368
-		if ($this->process_ifs > 0) {
1369
-			$this->process_ifs++;
1370
-
1371
-			return;
1372
-		}
1373
-
1374
-		$condition = $attrs['condition'];
1375
-		$condition = $this->substituteVars($condition, true);
1376
-		$condition = str_replace(array(" LT ", " GT "), array("<", ">"), $condition);
1377
-		// Replace the first accurance only once of @fact:DATE or in any other combinations to the current fact, such as BIRT
1378
-		$condition = str_replace("@fact:", $this->fact . ':', $condition);
1379
-		$match     = array();
1380
-		$count     = preg_match_all("/@([\w:\.]+)/", $condition, $match, PREG_SET_ORDER);
1381
-		$i         = 0;
1382
-		while ($i < $count) {
1383
-			$id    = $match[$i][1];
1384
-			$value = '""';
1385
-			if ($id == "ID") {
1386
-				if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1387
-					$value = "'" . $match[1] . "'";
1388
-				}
1389
-			} elseif ($id === "fact") {
1390
-				$value = '"' . $this->fact . '"';
1391
-			} elseif ($id === "desc") {
1392
-				$value = '"' . addslashes($this->desc) . '"';
1393
-			} elseif ($id === "generation") {
1394
-				$value = '"' . $this->generation . '"';
1395
-			} else {
1396
-
1397
-				$temp  = explode(" ", trim($this->gedrec));
1398
-				$level = $temp[0];
1399
-				if ($level == 0) {
1400
-					$level++;
1401
-				}
1402
-				$value = $this->getGedcomValue($id, $level, $this->gedrec);
1403
-				if (empty($value)) {
1404
-					$level++;
1405
-					$value = $this->getGedcomValue($id, $level, $this->gedrec);
1406
-				}
1407
-				$value = preg_replace('/^@(' . WT_REGEX_XREF . ')@$/', '$1', $value);
1408
-				$value = '"' . addslashes($value) . '"';
1409
-			}
1410
-			$condition = str_replace("@$id", $value, $condition);
1411
-			$i++;
1412
-		}
1413
-		$ret = eval("return (bool) ($condition);");
1414
-		if (!$ret) {
1415
-			$this->process_ifs++;
1416
-		}
1417
-	}
1418
-
1419
-	/**
1420
-	 * XML <if /> end element
1421
-	 */
1422
-	private function ifEndHandler() {
1423
-		if ($this->process_ifs > 0) {
1424
-			$this->process_ifs--;
1425
-		}
1426
-	}
1427
-
1428
-	/**
1429
-	 * XML <Footnote > start element
1430
-	 * Collect the Footnote links
1431
-	 * GEDCOM Records that are protected by Privacy setting will be ignore
1432
-	 *
1433
-	 * @param array $attrs an array of key value pairs for the attributes
1434
-	 */
1435
-	private function footnoteStartHandler($attrs) {
1436
-		global $WT_TREE;
1437
-
1438
-		$id = "";
1439
-		if (preg_match("/[0-9] (.+) @(.+)@/", $this->gedrec, $match)) {
1440
-			$id = $match[2];
1441
-		}
1442
-		$record = GedcomRecord::getInstance($id, $WT_TREE);
1443
-		if ($record && $record->canShow()) {
1444
-			array_push($this->print_data_stack, $this->print_data);
1445
-			$this->print_data = true;
1446
-			$style            = "";
1447
-			if (!empty($attrs['style'])) {
1448
-				$style = $attrs['style'];
1449
-			}
1450
-			$this->footnote_element = $this->current_element;
1451
-			$this->current_element  = $this->report_root->createFootnote($style);
1452
-		} else {
1453
-			$this->print_data       = false;
1454
-			$this->process_footnote = false;
1455
-		}
1456
-	}
1457
-
1458
-	/**
1459
-	 * XML <Footnote /> end element
1460
-	 * Print the collected Footnote data
1461
-	 */
1462
-	private function footnoteEndHandler() {
1463
-		if ($this->process_footnote) {
1464
-			$this->print_data = array_pop($this->print_data_stack);
1465
-			$temp             = trim($this->current_element->getValue());
1466
-			if (strlen($temp) > 3) {
1467
-				$this->wt_report->addElement($this->current_element);
1468
-			}
1469
-			$this->current_element = $this->footnote_element;
1470
-		} else {
1471
-			$this->process_footnote = true;
1472
-		}
1473
-	}
1474
-
1475
-	/**
1476
-	 * XML <FootnoteTexts /> element
1477
-	 */
1478
-	private function footnoteTextsStartHandler() {
1479
-		$temp = "footnotetexts";
1480
-		$this->wt_report->addElement($temp);
1481
-	}
1482
-
1483
-	/**
1484
-	 * XML <AgeAtDeath /> element handler
1485
-	 */
1486
-	private function ageAtDeathStartHandler() {
1487
-		// This duplicates functionality in FunctionsPrint::format_fact_date()
1488
-		global $factrec, $WT_TREE;
1489
-
1490
-		$match = array();
1491
-		if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1492
-			$person = Individual::getInstance($match[1], $WT_TREE);
1493
-			// Recorded age
1494
-			if (preg_match('/\n2 AGE (.+)/', $factrec, $match)) {
1495
-				$fact_age = $match[1];
1496
-			} else {
1497
-				$fact_age = '';
1498
-			}
1499
-			if (preg_match('/\n2 HUSB\n3 AGE (.+)/', $factrec, $match)) {
1500
-				$husb_age = $match[1];
1501
-			} else {
1502
-				$husb_age = '';
1503
-			}
1504
-			if (preg_match('/\n2 WIFE\n3 AGE (.+)/', $factrec, $match)) {
1505
-				$wife_age = $match[1];
1506
-			} else {
1507
-				$wife_age = '';
1508
-			}
1509
-
1510
-			// Calculated age
1511
-			$birth_date = $person->getBirthDate();
1512
-			// Can't use getDeathDate(), as this also gives BURI/CREM events, which
1513
-			// wouldn't give the correct "days after death" result for people with
1514
-			// no DEAT.
1515
-			$death_event = $person->getFirstFact('DEAT');
1516
-			if ($death_event) {
1517
-				$death_date = $death_event->getDate();
1518
-			} else {
1519
-				$death_date = new Date('');
1520
-			}
1521
-			$value = '';
1522
-			if (Date::compare($birth_date, $death_date) <= 0 || !$person->isDead()) {
1523
-				$age = Date::getAgeGedcom($birth_date, $death_date);
1524
-				// Only show calculated age if it differs from recorded age
1525
-				if ($age != '' && $age != "0d") {
1526
-					if ($fact_age != '' && $fact_age != $age || $fact_age == '' && $husb_age == '' && $wife_age == '' || $husb_age != '' && $person->getSex() == 'M' && $husb_age != $age || $wife_age != '' && $person->getSex() == 'F' && $wife_age != $age
1527
-					) {
1528
-						$value  = FunctionsDate::getAgeAtEvent($age);
1529
-						$abbrev = substr($value, 0, strpos($value, ' ') + 5);
1530
-						if ($value !== $abbrev) {
1531
-							$value = $abbrev . '.';
1532
-						}
1533
-					}
1534
-				}
1535
-			}
1536
-			$this->current_element->addText($value);
1537
-		}
1538
-	}
1539
-
1540
-	/**
1541
-	 * XML element Forced line break handler - HTML code
1542
-	 */
1543
-	private function brStartHandler() {
1544
-		if ($this->print_data && $this->process_gedcoms === 0) {
1545
-			$this->current_element->addText('<br>');
1546
-		}
1547
-	}
1548
-
1549
-	/**
1550
-	 * XML <sp />element Forced space handler
1551
-	 */
1552
-	private function spStartHandler() {
1553
-		if ($this->print_data && $this->process_gedcoms === 0) {
1554
-			$this->current_element->addText(' ');
1555
-		}
1556
-	}
1557
-
1558
-	/**
1559
-	 * XML <HighlightedImage/>
1560
-	 *
1561
-	 * @param array $attrs an array of key value pairs for the attributes
1562
-	 */
1563
-	private function highlightedImageStartHandler($attrs) {
1564
-		global $WT_TREE;
1565
-
1566
-		$id    = '';
1567
-		$match = array();
1568
-		if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1569
-			$id = $match[1];
1570
-		}
1571
-
1572
-		// mixed Position the top corner of this box on the page. the default is the current position
1573
-		$top = '.';
1574
-		if (isset($attrs['top'])) {
1575
-			if ($attrs['top'] === '0') {
1576
-				$top = 0;
1577
-			} elseif ($attrs['top'] === '.') {
1578
-				$top = '.';
1579
-			} elseif (!empty($attrs['top'])) {
1580
-				$top = (int) $attrs['top'];
1581
-			}
1582
-		}
1583
-
1584
-		// mixed Position the left corner of this box on the page. the default is the current position
1585
-		$left = '.';
1586
-		if (isset($attrs['left'])) {
1587
-			if ($attrs['left'] === '0') {
1588
-				$left = 0;
1589
-			} elseif ($attrs['left'] === '.') {
1590
-				$left = '.';
1591
-			} elseif (!empty($attrs['left'])) {
1592
-				$left = (int) $attrs['left'];
1593
-			}
1594
-		}
1595
-
1596
-		// string Align the image in left, center, right
1597
-		$align = '';
1598
-		if (!empty($attrs['align'])) {
1599
-			$align = $attrs['align'];
1600
-		}
1601
-
1602
-		// string Next Line should be T:next to the image, N:next line
1603
-		$ln = '';
1604
-		if (!empty($attrs['ln'])) {
1605
-			$ln = $attrs['ln'];
1606
-		}
1607
-
1608
-		$width  = 0;
1609
-		$height = 0;
1610
-		if (!empty($attrs['width'])) {
1611
-			$width = (int) $attrs['width'];
1612
-		}
1613
-		if (!empty($attrs['height'])) {
1614
-			$height = (int) $attrs['height'];
1615
-		}
1616
-
1617
-		$person      = Individual::getInstance($id, $WT_TREE);
1618
-		$mediaobject = $person->findHighlightedMedia();
1619
-		if ($mediaobject) {
1620
-			$attributes = $mediaobject->getImageAttributes('thumb');
1621
-			if (in_array(
1622
-					$attributes['ext'],
1623
-					array(
1624
-						'GIF',
1625
-						'JPG',
1626
-						'PNG',
1627
-						'SWF',
1628
-						'PSD',
1629
-						'BMP',
1630
-						'TIFF',
1631
-						'TIFF',
1632
-						'JPC',
1633
-						'JP2',
1634
-						'JPX',
1635
-						'JB2',
1636
-						'SWC',
1637
-						'IFF',
1638
-						'WBMP',
1639
-						'XBM',
1640
-					)
1641
-				) && $mediaobject->canShow() && $mediaobject->fileExists('thumb')
1642
-			) {
1643
-				if ($width > 0 && $height == 0) {
1644
-					$perc   = $width / $attributes['adjW'];
1645
-					$height = round($attributes['adjH'] * $perc);
1646
-				} elseif ($height > 0 && $width == 0) {
1647
-					$perc  = $height / $attributes['adjH'];
1648
-					$width = round($attributes['adjW'] * $perc);
1649
-				} else {
1650
-					$width  = $attributes['adjW'];
1651
-					$height = $attributes['adjH'];
1652
-				}
1653
-				$image = $this->report_root->createImageFromObject($mediaobject, $left, $top, $width, $height, $align, $ln);
1654
-				$this->wt_report->addElement($image);
1655
-			}
1656
-		}
1657
-	}
1658
-
1659
-	/**
1660
-	 * XML <Image/>
1661
-	 *
1662
-	 * @param array $attrs an array of key value pairs for the attributes
1663
-	 */
1664
-	private function imageStartHandler($attrs) {
1665
-		global $WT_TREE;
1666
-
1667
-		// mixed Position the top corner of this box on the page. the default is the current position
1668
-		$top = '.';
1669
-		if (isset($attrs['top'])) {
1670
-			if ($attrs['top'] === "0") {
1671
-				$top = 0;
1672
-			} elseif ($attrs['top'] === '.') {
1673
-				$top = '.';
1674
-			} elseif (!empty($attrs['top'])) {
1675
-				$top = (int) $attrs['top'];
1676
-			}
1677
-		}
1678
-
1679
-		// mixed Position the left corner of this box on the page. the default is the current position
1680
-		$left = '.';
1681
-		if (isset($attrs['left'])) {
1682
-			if ($attrs['left'] === '0') {
1683
-				$left = 0;
1684
-			} elseif ($attrs['left'] === '.') {
1685
-				$left = '.';
1686
-			} elseif (!empty($attrs['left'])) {
1687
-				$left = (int) $attrs['left'];
1688
-			}
1689
-		}
1690
-
1691
-		// string Align the image in left, center, right
1692
-		$align = '';
1693
-		if (!empty($attrs['align'])) {
1694
-			$align = $attrs['align'];
1695
-		}
1696
-
1697
-		// string Next Line should be T:next to the image, N:next line
1698
-		$ln = 'T';
1699
-		if (!empty($attrs['ln'])) {
1700
-			$ln = $attrs['ln'];
1701
-		}
1702
-
1703
-		$width  = 0;
1704
-		$height = 0;
1705
-		if (!empty($attrs['width'])) {
1706
-			$width = (int) $attrs['width'];
1707
-		}
1708
-		if (!empty($attrs['height'])) {
1709
-			$height = (int) $attrs['height'];
1710
-		}
1711
-
1712
-		$file = '';
1713
-		if (!empty($attrs['file'])) {
1714
-			$file = $attrs['file'];
1715
-		}
1716
-		if ($file == "@FILE") {
1717
-			$match = array();
1718
-			if (preg_match("/\d OBJE @(.+)@/", $this->gedrec, $match)) {
1719
-				$mediaobject = Media::getInstance($match[1], $WT_TREE);
1720
-				$attributes  = $mediaobject->getImageAttributes('thumb');
1721
-				if (in_array(
1722
-						$attributes['ext'],
1723
-						array(
1724
-							'GIF',
1725
-							'JPG',
1726
-							'PNG',
1727
-							'SWF',
1728
-							'PSD',
1729
-							'BMP',
1730
-							'TIFF',
1731
-							'TIFF',
1732
-							'JPC',
1733
-							'JP2',
1734
-							'JPX',
1735
-							'JB2',
1736
-							'SWC',
1737
-							'IFF',
1738
-							'WBMP',
1739
-							'XBM',
1740
-						)
1741
-					) && $mediaobject->canShow() && $mediaobject->fileExists('thumb')
1742
-				) {
1743
-					if ($width > 0 && $height == 0) {
1744
-						$perc   = $width / $attributes['adjW'];
1745
-						$height = round($attributes['adjH'] * $perc);
1746
-					} elseif ($height > 0 && $width == 0) {
1747
-						$perc  = $height / $attributes['adjH'];
1748
-						$width = round($attributes['adjW'] * $perc);
1749
-					} else {
1750
-						$width  = $attributes['adjW'];
1751
-						$height = $attributes['adjH'];
1752
-					}
1753
-					$image = $this->report_root->createImageFromObject($mediaobject, $left, $top, $width, $height, $align, $ln);
1754
-					$this->wt_report->addElement($image);
1755
-				}
1756
-			}
1757
-		} else {
1758
-			if (file_exists($file) && preg_match("/(jpg|jpeg|png|gif)$/i", $file)) {
1759
-				$size = getimagesize($file);
1760
-				if ($width > 0 && $height == 0) {
1761
-					$perc   = $width / $size[0];
1762
-					$height = round($size[1] * $perc);
1763
-				} elseif ($height > 0 && $width == 0) {
1764
-					$perc  = $height / $size[1];
1765
-					$width = round($size[0] * $perc);
1766
-				} else {
1767
-					$width  = $size[0];
1768
-					$height = $size[1];
1769
-				}
1770
-				$image = $this->report_root->createImage($file, $left, $top, $width, $height, $align, $ln);
1771
-				$this->wt_report->addElement($image);
1772
-			}
1773
-		}
1774
-	}
1775
-
1776
-	/**
1777
-	 * XML <Line> element handler
1778
-	 *
1779
-	 * @param array $attrs an array of key value pairs for the attributes
1780
-	 */
1781
-	private function lineStartHandler($attrs) {
1782
-		// Start horizontal position, current position (default)
1783
-		$x1 = ".";
1784
-		if (isset($attrs['x1'])) {
1785
-			if ($attrs['x1'] === "0") {
1786
-				$x1 = 0;
1787
-			} elseif ($attrs['x1'] === ".") {
1788
-				$x1 = ".";
1789
-			} elseif (!empty($attrs['x1'])) {
1790
-				$x1 = (int) $attrs['x1'];
1791
-			}
1792
-		}
1793
-		// Start vertical position, current position (default)
1794
-		$y1 = ".";
1795
-		if (isset($attrs['y1'])) {
1796
-			if ($attrs['y1'] === "0") {
1797
-				$y1 = 0;
1798
-			} elseif ($attrs['y1'] === ".") {
1799
-				$y1 = ".";
1800
-			} elseif (!empty($attrs['y1'])) {
1801
-				$y1 = (int) $attrs['y1'];
1802
-			}
1803
-		}
1804
-		// End horizontal position, maximum width (default)
1805
-		$x2 = ".";
1806
-		if (isset($attrs['x2'])) {
1807
-			if ($attrs['x2'] === "0") {
1808
-				$x2 = 0;
1809
-			} elseif ($attrs['x2'] === ".") {
1810
-				$x2 = ".";
1811
-			} elseif (!empty($attrs['x2'])) {
1812
-				$x2 = (int) $attrs['x2'];
1813
-			}
1814
-		}
1815
-		// End vertical position
1816
-		$y2 = ".";
1817
-		if (isset($attrs['y2'])) {
1818
-			if ($attrs['y2'] === "0") {
1819
-				$y2 = 0;
1820
-			} elseif ($attrs['y2'] === ".") {
1821
-				$y2 = ".";
1822
-			} elseif (!empty($attrs['y2'])) {
1823
-				$y2 = (int) $attrs['y2'];
1824
-			}
1825
-		}
1826
-
1827
-		$line = $this->report_root->createLine($x1, $y1, $x2, $y2);
1828
-		$this->wt_report->addElement($line);
1829
-	}
1830
-
1831
-	/**
1832
-	 * XML <List>
1833
-	 *
1834
-	 * @param array $attrs an array of key value pairs for the attributes
1835
-	 */
1836
-	private function listStartHandler($attrs) {
1837
-		global $WT_TREE;
1838
-
1839
-		$this->process_repeats++;
1840
-		if ($this->process_repeats > 1) {
1841
-			return;
1842
-		}
1843
-
1844
-		$match = array();
1845
-		if (isset($attrs['sortby'])) {
1846
-			$sortby = $attrs['sortby'];
1847
-			if (preg_match("/\\$(\w+)/", $sortby, $match)) {
1848
-				$sortby = $this->vars[$match[1]]['id'];
1849
-				$sortby = trim($sortby);
1850
-			}
1851
-		} else {
1852
-			$sortby = "NAME";
1853
-		}
1854
-
1855
-		if (isset($attrs['list'])) {
1856
-			$listname = $attrs['list'];
1857
-		} else {
1858
-			$listname = "individual";
1859
-		}
1860
-		// Some filters/sorts can be applied using SQL, while others require PHP
1861
-		switch ($listname) {
1862
-		case "pending":
1863
-			$rows = Database::prepare(
1864
-				"SELECT xref, CASE new_gedcom WHEN '' THEN old_gedcom ELSE new_gedcom END AS gedcom" .
1865
-				" FROM `##change`" . " WHERE (xref, change_id) IN (" .
1866
-				"  SELECT xref, MAX(change_id)" .
1867
-				"  FROM `##change`" .
1868
-				"  WHERE status = 'pending' AND gedcom_id = :tree_id" .
1869
-				"  GROUP BY xref" .
1870
-				" )"
1871
-			)->execute(array(
1872
-				'tree_id' => $WT_TREE->getTreeId(),
1873
-			))->fetchAll();
1874
-			$this->list = array();
1875
-			foreach ($rows as $row) {
1876
-				$this->list[] = GedcomRecord::getInstance($row->xref, $WT_TREE, $row->gedcom);
1877
-			}
1878
-			break;
1879
-		case 'individual':
1880
-			$sql_select   = "SELECT i_id AS xref, i_gedcom AS gedcom FROM `##individuals` ";
1881
-			$sql_join     = "";
1882
-			$sql_where    = " WHERE i_file = :tree_id";
1883
-			$sql_order_by = "";
1884
-			$sql_params   = array('tree_id' => $WT_TREE->getTreeId());
1885
-			foreach ($attrs as $attr => $value) {
1886
-				if (strpos($attr, 'filter') === 0 && $value) {
1887
-					$value = $this->substituteVars($value, false);
1888
-					// Convert the various filters into SQL
1889
-					if (preg_match('/^(\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
1890
-						$sql_join .= " JOIN `##dates` AS {$attr} ON ({$attr}.d_file=i_file AND {$attr}.d_gid=i_id)";
1891
-						$sql_where .= " AND {$attr}.d_fact = :{$attr}fact";
1892
-						$sql_params[$attr . 'fact'] = $match[1];
1893
-						$date                       = new Date($match[3]);
1894
-						if ($match[2] == "LTE") {
1895
-							$sql_where .= " AND {$attr}.d_julianday2 <= :{$attr}date";
1896
-							$sql_params[$attr . 'date'] = $date->maximumJulianDay();
1897
-						} else {
1898
-							$sql_where .= " AND {$attr}.d_julianday1 >= :{$attr}date";
1899
-							$sql_params[$attr . 'date'] = $date->minimumJulianDay();
1900
-						}
1901
-						if ($sortby == $match[1]) {
1902
-							$sortby = "";
1903
-							$sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
1904
-						}
1905
-						unset($attrs[$attr]); // This filter has been fully processed
1906
-					} elseif (preg_match('/^NAME CONTAINS (.*)$/', $value, $match)) {
1907
-						// Do nothing, unless you have to
1908
-						if ($match[1] != '' || $sortby == 'NAME') {
1909
-							$sql_join .= " JOIN `##name` AS {$attr} ON (n_file=i_file AND n_id=i_id)";
1910
-							// Search the DB only if there is any name supplied
1911
-							if ($match[1] != "") {
1912
-								$names = explode(" ", $match[1]);
1913
-								foreach ($names as $n => $name) {
1914
-									$sql_where .= " AND {$attr}.n_full LIKE CONCAT('%', :{$attr}name{$n}, '%')";
1915
-									$sql_params[$attr . 'name' . $n] = $name;
1916
-								}
1917
-							}
1918
-							// Let the DB do the name sorting even when no name was entered
1919
-							if ($sortby == "NAME") {
1920
-								$sortby = "";
1921
-								$sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.n_sort";
1922
-							}
1923
-						}
1924
-						unset($attrs[$attr]); // This filter has been fully processed
1925
-					} elseif (preg_match('/^REGEXP \/(.+)\//', $value, $match)) {
1926
-						$sql_where .= " AND i_gedcom REGEXP :{$attr}gedcom";
1927
-						// PDO helpfully escapes backslashes for us, preventing us from matching "\n1 FACT"
1928
-						$sql_params[$attr . 'gedcom'] = str_replace('\n', "\n", $match[1]);
1929
-						unset($attrs[$attr]); // This filter has been fully processed
1930
-					} elseif (preg_match('/^(?:\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
1931
-						$sql_join .= " JOIN `##places` AS {$attr}a ON ({$attr}a.p_file = i_file)";
1932
-						$sql_join .= " JOIN `##placelinks` AS {$attr}b ON ({$attr}a.p_file = {$attr}b.pl_file AND {$attr}b.pl_p_id = {$attr}a.p_id AND {$attr}b.pl_gid = i_id)";
1933
-						$sql_where .= " AND {$attr}a.p_place LIKE CONCAT('%', :{$attr}place, '%')";
1934
-						$sql_params[$attr . 'place'] = $match[1];
1935
-						// Don't unset this filter. This is just initial filtering
1936
-					} elseif (preg_match('/^(\w*):*(\w*) CONTAINS (.+)$/', $value, $match)) {
1937
-						$sql_where .= " AND i_gedcom LIKE CONCAT('%', :{$attr}contains1, '%', :{$attr}contains2, '%', :{$attr}contains3, '%')";
1938
-						$sql_params[$attr . 'contains1'] = $match[1];
1939
-						$sql_params[$attr . 'contains2'] = $match[2];
1940
-						$sql_params[$attr . 'contains3'] = $match[3];
1941
-						// Don't unset this filter. This is just initial filtering
1942
-					}
1943
-				}
1944
-			}
1945
-
1946
-			$this->list = array();
1947
-			$rows       = Database::prepare(
1948
-				$sql_select . $sql_join . $sql_where . $sql_order_by
1949
-			)->execute($sql_params)->fetchAll();
1950
-
1951
-			foreach ($rows as $row) {
1952
-				$this->list[$row->xref] = Individual::getInstance($row->xref, $WT_TREE, $row->gedcom);
1953
-			}
1954
-			break;
1955
-
1956
-		case 'family':
1957
-			$sql_select   = "SELECT f_id AS xref, f_gedcom AS gedcom FROM `##families`";
1958
-			$sql_join     = "";
1959
-			$sql_where    = " WHERE f_file = :tree_id";
1960
-			$sql_order_by = "";
1961
-			$sql_params   = array('tree_id' => $WT_TREE->getTreeId());
1962
-			foreach ($attrs as $attr => $value) {
1963
-				if (strpos($attr, 'filter') === 0 && $value) {
1964
-					$value = $this->substituteVars($value, false);
1965
-					// Convert the various filters into SQL
1966
-					if (preg_match('/^(\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
1967
-						$sql_join .= " JOIN `##dates` AS {$attr} ON ({$attr}.d_file=f_file AND {$attr}.d_gid=f_id)";
1968
-						$sql_where .= " AND {$attr}.d_fact = :{$attr}fact";
1969
-						$sql_params[$attr . 'fact'] = $match[1];
1970
-						$date                       = new Date($match[3]);
1971
-						if ($match[2] == "LTE") {
1972
-							$sql_where .= " AND {$attr}.d_julianday2 <= :{$attr}date";
1973
-							$sql_params[$attr . 'date'] = $date->maximumJulianDay();
1974
-						} else {
1975
-							$sql_where .= " AND {$attr}.d_julianday1 >= :{$attr}date";
1976
-							$sql_params[$attr . 'date'] = $date->minimumJulianDay();
1977
-						}
1978
-						if ($sortby == $match[1]) {
1979
-							$sortby = "";
1980
-							$sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
1981
-						}
1982
-						unset($attrs[$attr]); // This filter has been fully processed
1983
-					} elseif (preg_match('/^REGEXP \/(.+)\//', $value, $match)) {
1984
-						$sql_where .= " AND f_gedcom REGEXP :{$attr}gedcom";
1985
-						// PDO helpfully escapes backslashes for us, preventing us from matching "\n1 FACT"
1986
-						$sql_params[$attr . 'gedcom'] = str_replace('\n', "\n", $match[1]);
1987
-						unset($attrs[$attr]); // This filter has been fully processed
1988
-					} elseif (preg_match('/^NAME CONTAINS (.+)$/', $value, $match)) {
1989
-						// Do nothing, unless you have to
1990
-						if ($match[1] != '' || $sortby == 'NAME') {
1991
-							$sql_join .= " JOIN `##name` AS {$attr} ON n_file = f_file AND n_id IN (f_husb, f_wife)";
1992
-							// Search the DB only if there is any name supplied
1993
-							if ($match[1] != "") {
1994
-								$names = explode(" ", $match[1]);
1995
-								foreach ($names as $n => $name) {
1996
-									$sql_where .= " AND {$attr}.n_full LIKE CONCAT('%', :{$attr}name{$n}, '%')";
1997
-									$sql_params[$attr . 'name' . $n] = $name;
1998
-								}
1999
-							}
2000
-							// Let the DB do the name sorting even when no name was entered
2001
-							if ($sortby == "NAME") {
2002
-								$sortby = "";
2003
-								$sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.n_sort";
2004
-							}
2005
-						}
2006
-						unset($attrs[$attr]); // This filter has been fully processed
2007
-
2008
-					} elseif (preg_match('/^(?:\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
2009
-						$sql_join .= " JOIN `##places` AS {$attr}a ON ({$attr}a.p_file=f_file)";
2010
-						$sql_join .= " JOIN `##placelinks` AS {$attr}b ON ({$attr}a.p_file={$attr}b.pl_file AND {$attr}b.pl_p_id={$attr}a.p_id AND {$attr}b.pl_gid=f_id)";
2011
-						$sql_where .= " AND {$attr}a.p_place LIKE CONCAT('%', :{$attr}place, '%')";
2012
-						$sql_params[$attr . 'place'] = $match[1];
2013
-						// Don't unset this filter. This is just initial filtering
2014
-					} elseif (preg_match('/^(\w*):*(\w*) CONTAINS (.+)$/', $value, $match)) {
2015
-						$sql_where .= " AND f_gedcom LIKE CONCAT('%', :{$attr}contains1, '%', :{$attr}contains2, '%', :{$attr}contains3, '%')";
2016
-						$sql_params[$attr . 'contains1'] = $match[1];
2017
-						$sql_params[$attr . 'contains2'] = $match[2];
2018
-						$sql_params[$attr . 'contains3'] = $match[3];
2019
-						// Don't unset this filter. This is just initial filtering
2020
-					}
2021
-				}
2022
-			}
2023
-
2024
-			$this->list = array();
2025
-			$rows       = Database::prepare(
2026
-				$sql_select . $sql_join . $sql_where . $sql_order_by
2027
-			)->execute($sql_params)->fetchAll();
2028
-
2029
-			foreach ($rows as $row) {
2030
-				$this->list[$row->xref] = Family::getInstance($row->xref, $WT_TREE, $row->gedcom);
2031
-			}
2032
-			break;
2033
-
2034
-		default:
2035
-			throw new \DomainException('Invalid list name: ' . $listname);
2036
-		}
2037
-
2038
-		$filters  = array();
2039
-		$filters2 = array();
2040
-		if (isset($attrs['filter1']) && count($this->list) > 0) {
2041
-			foreach ($attrs as $key => $value) {
2042
-				if (preg_match("/filter(\d)/", $key)) {
2043
-					$condition = $value;
2044
-					if (preg_match("/@(\w+)/", $condition, $match)) {
2045
-						$id    = $match[1];
2046
-						$value = "''";
2047
-						if ($id == "ID") {
2048
-							if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
2049
-								$value = "'" . $match[1] . "'";
2050
-							}
2051
-						} elseif ($id == "fact") {
2052
-							$value = "'" . $this->fact . "'";
2053
-						} elseif ($id == "desc") {
2054
-							$value = "'" . $this->desc . "'";
2055
-						} else {
2056
-							if (preg_match("/\d $id (.+)/", $this->gedrec, $match)) {
2057
-								$value = "'" . str_replace("@", "", trim($match[1])) . "'";
2058
-							}
2059
-						}
2060
-						$condition = preg_replace("/@$id/", $value, $condition);
2061
-					}
2062
-					//-- handle regular expressions
2063
-					if (preg_match("/([A-Z:]+)\s*([^\s]+)\s*(.+)/", $condition, $match)) {
2064
-						$tag  = trim($match[1]);
2065
-						$expr = trim($match[2]);
2066
-						$val  = trim($match[3]);
2067
-						if (preg_match("/\\$(\w+)/", $val, $match)) {
2068
-							$val = $this->vars[$match[1]]['id'];
2069
-							$val = trim($val);
2070
-						}
2071
-						if ($val) {
2072
-							$searchstr = "";
2073
-							$tags      = explode(":", $tag);
2074
-							//-- only limit to a level number if we are specifically looking at a level
2075
-							if (count($tags) > 1) {
2076
-								$level = 1;
2077
-								foreach ($tags as $t) {
2078
-									if (!empty($searchstr)) {
2079
-										$searchstr .= "[^\n]*(\n[2-9][^\n]*)*\n";
2080
-									}
2081
-									//-- search for both EMAIL and _EMAIL... silly double gedcom standard
2082
-									if ($t == "EMAIL" || $t == "_EMAIL") {
2083
-										$t = "_?EMAIL";
2084
-									}
2085
-									$searchstr .= $level . " " . $t;
2086
-									$level++;
2087
-								}
2088
-							} else {
2089
-								if ($tag == "EMAIL" || $tag == "_EMAIL") {
2090
-									$tag = "_?EMAIL";
2091
-								}
2092
-								$t         = $tag;
2093
-								$searchstr = "1 " . $tag;
2094
-							}
2095
-							switch ($expr) {
2096
-							case "CONTAINS":
2097
-								if ($t == "PLAC") {
2098
-									$searchstr .= "[^\n]*[, ]*" . $val;
2099
-								} else {
2100
-									$searchstr .= "[^\n]*" . $val;
2101
-								}
2102
-								$filters[] = $searchstr;
2103
-								break;
2104
-							default:
2105
-								$filters2[] = array("tag" => $tag, "expr" => $expr, "val" => $val);
2106
-								break;
2107
-							}
2108
-						}
2109
-					}
2110
-				}
2111
-			}
2112
-		}
2113
-		//-- apply other filters to the list that could not be added to the search string
2114
-		if ($filters) {
2115
-			foreach ($this->list as $key => $record) {
2116
-				foreach ($filters as $filter) {
2117
-					if (!preg_match("/" . $filter . "/i", $record->privatizeGedcom(Auth::accessLevel($WT_TREE)))) {
2118
-						unset($this->list[$key]);
2119
-						break;
2120
-					}
2121
-				}
2122
-			}
2123
-		}
2124
-		if ($filters2) {
2125
-			$mylist = array();
2126
-			foreach ($this->list as $indi) {
2127
-				$key  = $indi->getXref();
2128
-				$grec = $indi->privatizeGedcom(Auth::accessLevel($WT_TREE));
2129
-				$keep = true;
2130
-				foreach ($filters2 as $filter) {
2131
-					if ($keep) {
2132
-						$tag  = $filter['tag'];
2133
-						$expr = $filter['expr'];
2134
-						$val  = $filter['val'];
2135
-						if ($val == "''") {
2136
-							$val = "";
2137
-						}
2138
-						$tags = explode(":", $tag);
2139
-						$t    = end($tags);
2140
-						$v    = $this->getGedcomValue($tag, 1, $grec);
2141
-						//-- check for EMAIL and _EMAIL (silly double gedcom standard :P)
2142
-						if ($t == "EMAIL" && empty($v)) {
2143
-							$tag  = str_replace("EMAIL", "_EMAIL", $tag);
2144
-							$tags = explode(":", $tag);
2145
-							$t    = end($tags);
2146
-							$v    = Functions::getSubRecord(1, $tag, $grec);
2147
-						}
2148
-
2149
-						switch ($expr) {
2150
-						case "GTE":
2151
-							if ($t == "DATE") {
2152
-								$date1 = new Date($v);
2153
-								$date2 = new Date($val);
2154
-								$keep  = (Date::compare($date1, $date2) >= 0);
2155
-							} elseif ($val >= $v) {
2156
-								$keep = true;
2157
-							}
2158
-							break;
2159
-						case "LTE":
2160
-							if ($t == "DATE") {
2161
-								$date1 = new Date($v);
2162
-								$date2 = new Date($val);
2163
-								$keep  = (Date::compare($date1, $date2) <= 0);
2164
-							} elseif ($val >= $v) {
2165
-								$keep = true;
2166
-							}
2167
-							break;
2168
-						default:
2169
-							if ($v == $val) {
2170
-								$keep = true;
2171
-							} else {
2172
-								$keep = false;
2173
-							}
2174
-							break;
2175
-						}
2176
-					}
2177
-				}
2178
-				if ($keep) {
2179
-					$mylist[$key] = $indi;
2180
-				}
2181
-			}
2182
-			$this->list = $mylist;
2183
-		}
2184
-
2185
-		switch ($sortby) {
2186
-		case 'NAME':
2187
-			uasort($this->list, '\Fisharebest\Webtrees\GedcomRecord::compare');
2188
-			break;
2189
-		case 'CHAN':
2190
-			uasort($this->list, function (GedcomRecord $x, GedcomRecord $y) {
2191
-				return $y->lastChangeTimestamp(true) - $x->lastChangeTimestamp(true);
2192
-			});
2193
-			break;
2194
-		case 'BIRT:DATE':
2195
-			uasort($this->list, '\Fisharebest\Webtrees\Individual::compareBirthDate');
2196
-			break;
2197
-		case 'DEAT:DATE':
2198
-			uasort($this->list, '\Fisharebest\Webtrees\Individual::compareDeathDate');
2199
-			break;
2200
-		case 'MARR:DATE':
2201
-			uasort($this->list, '\Fisharebest\Webtrees\Family::compareMarrDate');
2202
-			break;
2203
-		default:
2204
-			// unsorted or already sorted by SQL
2205
-			break;
2206
-		}
2207
-
2208
-		array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
2209
-		$this->repeat_bytes = xml_get_current_line_number($this->parser) + 1;
2210
-	}
2211
-
2212
-	/**
2213
-	 * XML <List>
2214
-	 */
2215
-	private function listEndHandler() {
2216
-		global $report;
2217
-
2218
-		$this->process_repeats--;
2219
-		if ($this->process_repeats > 0) {
2220
-			return;
2221
-		}
2222
-
2223
-		// Check if there is any list
2224
-		if (count($this->list) > 0) {
2225
-			$lineoffset = 0;
2226
-			foreach ($this->repeats_stack as $rep) {
2227
-				$lineoffset += $rep[1];
2228
-			}
2229
-			//-- read the xml from the file
2230
-			$lines = file($report);
2231
-			while ((strpos($lines[$lineoffset + $this->repeat_bytes], "<List") === false) && (($lineoffset + $this->repeat_bytes) > 0)) {
2232
-				$lineoffset--;
2233
-			}
2234
-			$lineoffset++;
2235
-			$reportxml = "<tempdoc>\n";
2236
-			$line_nr   = $lineoffset + $this->repeat_bytes;
2237
-			// List Level counter
2238
-			$count = 1;
2239
-			while (0 < $count) {
2240
-				if (strpos($lines[$line_nr], "<List") !== false) {
2241
-					$count++;
2242
-				} elseif (strpos($lines[$line_nr], "</List") !== false) {
2243
-					$count--;
2244
-				}
2245
-				if (0 < $count) {
2246
-					$reportxml .= $lines[$line_nr];
2247
-				}
2248
-				$line_nr++;
2249
-			}
2250
-			// No need to drag this
2251
-			unset($lines);
2252
-			$reportxml .= "</tempdoc>";
2253
-			// Save original values
2254
-			array_push($this->parser_stack, $this->parser);
2255
-			$oldgedrec = $this->gedrec;
2256
-
2257
-			$this->list_total   = count($this->list);
2258
-			$this->list_private = 0;
2259
-			foreach ($this->list as $record) {
2260
-				if ($record->canShow()) {
2261
-					$this->gedrec = $record->privatizeGedcom(Auth::accessLevel($record->getTree()));
2262
-					//-- start the sax parser
2263
-					$repeat_parser = xml_parser_create();
2264
-					$this->parser  = $repeat_parser;
2265
-					xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
2266
-					xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
2267
-					xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
2268
-					if (!xml_parse($repeat_parser, $reportxml, true)) {
2269
-						throw new \DomainException(sprintf(
2270
-							'ListEHandler XML error: %s at line %d',
2271
-							xml_error_string(xml_get_error_code($repeat_parser)),
2272
-							xml_get_current_line_number($repeat_parser)
2273
-						));
2274
-					}
2275
-					xml_parser_free($repeat_parser);
2276
-				} else {
2277
-					$this->list_private++;
2278
-				}
2279
-			}
2280
-			$this->list   = array();
2281
-			$this->parser = array_pop($this->parser_stack);
2282
-			$this->gedrec = $oldgedrec;
2283
-		}
2284
-		list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
2285
-	}
2286
-
2287
-	/**
2288
-	 * XML <ListTotal> element handler
2289
-	 *
2290
-	 * Prints the total number of records in a list
2291
-	 * The total number is collected from
2292
-	 * List and Relatives
2293
-	 */
2294
-	private function listTotalStartHandler() {
2295
-		if ($this->list_private == 0) {
2296
-			$this->current_element->addText($this->list_total);
2297
-		} else {
2298
-			$this->current_element->addText(($this->list_total - $this->list_private) . " / " . $this->list_total);
2299
-		}
2300
-	}
2301
-
2302
-	/**
2303
-	 * XML <Relatives>
2304
-	 *
2305
-	 * @param array $attrs an array of key value pairs for the attributes
2306
-	 */
2307
-	private function relativesStartHandler($attrs) {
2308
-		global $WT_TREE;
2309
-
2310
-		$this->process_repeats++;
2311
-		if ($this->process_repeats > 1) {
2312
-			return;
2313
-		}
2314
-
2315
-		$sortby = "NAME";
2316
-		if (isset($attrs['sortby'])) {
2317
-			$sortby = $attrs['sortby'];
2318
-		}
2319
-		$match = array();
2320
-		if (preg_match("/\\$(\w+)/", $sortby, $match)) {
2321
-			$sortby = $this->vars[$match[1]]['id'];
2322
-			$sortby = trim($sortby);
2323
-		}
2324
-
2325
-		$maxgen = -1;
2326
-		if (isset($attrs['maxgen'])) {
2327
-			$maxgen = $attrs['maxgen'];
2328
-		}
2329
-		if ($maxgen == "*") {
2330
-			$maxgen = -1;
2331
-		}
2332
-
2333
-		$group = "child-family";
2334
-		if (isset($attrs['group'])) {
2335
-			$group = $attrs['group'];
2336
-		}
2337
-		if (preg_match("/\\$(\w+)/", $group, $match)) {
2338
-			$group = $this->vars[$match[1]]['id'];
2339
-			$group = trim($group);
2340
-		}
2341
-
2342
-		$id = "";
2343
-		if (isset($attrs['id'])) {
2344
-			$id = $attrs['id'];
2345
-		}
2346
-		if (preg_match("/\\$(\w+)/", $id, $match)) {
2347
-			$id = $this->vars[$match[1]]['id'];
2348
-			$id = trim($id);
2349
-		}
2350
-
2351
-		$this->list = array();
2352
-		$person     = Individual::getInstance($id, $WT_TREE);
2353
-		if (!empty($person)) {
2354
-			$this->list[$id] = $person;
2355
-			switch ($group) {
2356
-			case "child-family":
2357
-				foreach ($person->getChildFamilies() as $family) {
2358
-					$husband = $family->getHusband();
2359
-					$wife    = $family->getWife();
2360
-					if (!empty($husband)) {
2361
-						$this->list[$husband->getXref()] = $husband;
2362
-					}
2363
-					if (!empty($wife)) {
2364
-						$this->list[$wife->getXref()] = $wife;
2365
-					}
2366
-					$children = $family->getChildren();
2367
-					foreach ($children as $child) {
2368
-						if (!empty($child)) {
2369
-							$this->list[$child->getXref()] = $child;
2370
-						}
2371
-					}
2372
-				}
2373
-				break;
2374
-			case "spouse-family":
2375
-				foreach ($person->getSpouseFamilies() as $family) {
2376
-					$husband = $family->getHusband();
2377
-					$wife    = $family->getWife();
2378
-					if (!empty($husband)) {
2379
-						$this->list[$husband->getXref()] = $husband;
2380
-					}
2381
-					if (!empty($wife)) {
2382
-						$this->list[$wife->getXref()] = $wife;
2383
-					}
2384
-					$children = $family->getChildren();
2385
-					foreach ($children as $child) {
2386
-						if (!empty($child)) {
2387
-							$this->list[$child->getXref()] = $child;
2388
-						}
2389
-					}
2390
-				}
2391
-				break;
2392
-			case "direct-ancestors":
2393
-				$this->addAncestors($this->list, $id, false, $maxgen);
2394
-				break;
2395
-			case "ancestors":
2396
-				$this->addAncestors($this->list, $id, true, $maxgen);
2397
-				break;
2398
-			case "descendants":
2399
-				$this->list[$id]->generation = 1;
2400
-				$this->addDescendancy($this->list, $id, false, $maxgen);
2401
-				break;
2402
-			case "all":
2403
-				$this->addAncestors($this->list, $id, true, $maxgen);
2404
-				$this->addDescendancy($this->list, $id, true, $maxgen);
2405
-				break;
2406
-			}
2407
-		}
2408
-
2409
-		switch ($sortby) {
2410
-		case 'NAME':
2411
-			uasort($this->list, '\Fisharebest\Webtrees\GedcomRecord::compare');
2412
-			break;
2413
-		case 'BIRT:DATE':
2414
-			uasort($this->list, '\Fisharebest\Webtrees\Individual::compareBirthDate');
2415
-			break;
2416
-		case 'DEAT:DATE':
2417
-			uasort($this->list, '\Fisharebest\Webtrees\Individual::compareDeathDate');
2418
-			break;
2419
-		case 'generation':
2420
-			$newarray = array();
2421
-			reset($this->list);
2422
-			$genCounter = 1;
2423
-			while (count($newarray) < count($this->list)) {
2424
-				foreach ($this->list as $key => $value) {
2425
-					$this->generation = $value->generation;
2426
-					if ($this->generation == $genCounter) {
2427
-						$newarray[$key]             = new \stdClass;
2428
-						$newarray[$key]->generation = $this->generation;
2429
-					}
2430
-				}
2431
-				$genCounter++;
2432
-			}
2433
-			$this->list = $newarray;
2434
-			break;
2435
-		default:
2436
-			// unsorted
2437
-			break;
2438
-		}
2439
-		array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
2440
-		$this->repeat_bytes = xml_get_current_line_number($this->parser) + 1;
2441
-	}
2442
-
2443
-	/**
2444
-	 * XML </ Relatives>
2445
-	 */
2446
-	private function relativesEndHandler() {
2447
-		global $report, $WT_TREE;
2448
-
2449
-		$this->process_repeats--;
2450
-		if ($this->process_repeats > 0) {
2451
-			return;
2452
-		}
2453
-
2454
-		// Check if there is any relatives
2455
-		if (count($this->list) > 0) {
2456
-			$lineoffset = 0;
2457
-			foreach ($this->repeats_stack as $rep) {
2458
-				$lineoffset += $rep[1];
2459
-			}
2460
-			//-- read the xml from the file
2461
-			$lines = file($report);
2462
-			while ((strpos($lines[$lineoffset + $this->repeat_bytes], "<Relatives") === false) && (($lineoffset + $this->repeat_bytes) > 0)) {
2463
-				$lineoffset--;
2464
-			}
2465
-			$lineoffset++;
2466
-			$reportxml = "<tempdoc>\n";
2467
-			$line_nr   = $lineoffset + $this->repeat_bytes;
2468
-			// Relatives Level counter
2469
-			$count = 1;
2470
-			while (0 < $count) {
2471
-				if (strpos($lines[$line_nr], "<Relatives") !== false) {
2472
-					$count++;
2473
-				} elseif (strpos($lines[$line_nr], "</Relatives") !== false) {
2474
-					$count--;
2475
-				}
2476
-				if (0 < $count) {
2477
-					$reportxml .= $lines[$line_nr];
2478
-				}
2479
-				$line_nr++;
2480
-			}
2481
-			// No need to drag this
2482
-			unset($lines);
2483
-			$reportxml .= "</tempdoc>\n";
2484
-			// Save original values
2485
-			array_push($this->parser_stack, $this->parser);
2486
-			$oldgedrec = $this->gedrec;
2487
-
2488
-			$this->list_total   = count($this->list);
2489
-			$this->list_private = 0;
2490
-			foreach ($this->list as $key => $value) {
2491
-				if (isset($value->generation)) {
2492
-					$this->generation = $value->generation;
2493
-				}
2494
-				$tmp          = GedcomRecord::getInstance($key, $WT_TREE);
2495
-				$this->gedrec = $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE));
2496
-
2497
-				$repeat_parser = xml_parser_create();
2498
-				$this->parser  = $repeat_parser;
2499
-				xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
2500
-				xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
2501
-				xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
2502
-
2503
-				if (!xml_parse($repeat_parser, $reportxml, true)) {
2504
-					throw new \DomainException(sprintf("RelativesEHandler XML error: %s at line %d", xml_error_string(xml_get_error_code($repeat_parser)), xml_get_current_line_number($repeat_parser)));
2505
-				}
2506
-				xml_parser_free($repeat_parser);
2507
-			}
2508
-			// Clean up the list array
2509
-			$this->list   = array();
2510
-			$this->parser = array_pop($this->parser_stack);
2511
-			$this->gedrec = $oldgedrec;
2512
-		}
2513
-		list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
2514
-	}
2515
-
2516
-	/**
2517
-	 * XML <Generation /> element handler
2518
-	 *
2519
-	 * Prints the number of generations
2520
-	 */
2521
-	private function generationStartHandler() {
2522
-		$this->current_element->addText($this->generation);
2523
-	}
2524
-
2525
-	/**
2526
-	 * XML <NewPage /> element handler
2527
-	 *
2528
-	 * Has to be placed in an element (header, pageheader, body or footer)
2529
-	 */
2530
-	private function newPageStartHandler() {
2531
-		$temp = "addpage";
2532
-		$this->wt_report->addElement($temp);
2533
-	}
2534
-
2535
-	/**
2536
-	 * XML <html>
2537
-	 *
2538
-	 * @param string  $tag   HTML tag name
2539
-	 * @param array[] $attrs an array of key value pairs for the attributes
2540
-	 */
2541
-	private function htmlStartHandler($tag, $attrs) {
2542
-		if ($tag === "tempdoc") {
2543
-			return;
2544
-		}
2545
-		array_push($this->wt_report_stack, $this->wt_report);
2546
-		$this->wt_report       = $this->report_root->createHTML($tag, $attrs);
2547
-		$this->current_element = $this->wt_report;
2548
-
2549
-		array_push($this->print_data_stack, $this->print_data);
2550
-		$this->print_data = true;
2551
-	}
2552
-
2553
-	/**
2554
-	 * XML </html>
2555
-	 *
2556
-	 * @param string $tag
2557
-	 */
2558
-	private function htmlEndHandler($tag) {
2559
-		if ($tag === "tempdoc") {
2560
-			return;
2561
-		}
2562
-
2563
-		$this->print_data      = array_pop($this->print_data_stack);
2564
-		$this->current_element = $this->wt_report;
2565
-		$this->wt_report       = array_pop($this->wt_report_stack);
2566
-		if (!is_null($this->wt_report)) {
2567
-			$this->wt_report->addElement($this->current_element);
2568
-		} else {
2569
-			$this->wt_report = $this->current_element;
2570
-		}
2571
-	}
2572
-
2573
-	/**
2574
-	 * Handle <Input>
2575
-	 */
2576
-	private function inputStartHandler() {
2577
-		// Dummy function, to prevent the default HtmlStartHandler() being called
2578
-	}
2579
-
2580
-	/**
2581
-	 * Handle </Input>
2582
-	 */
2583
-	private function inputEndHandler() {
2584
-		// Dummy function, to prevent the default HtmlEndHandler() being called
2585
-	}
2586
-
2587
-	/**
2588
-	 * Handle <Report>
2589
-	 */
2590
-	private function reportStartHandler() {
2591
-		// Dummy function, to prevent the default HtmlStartHandler() being called
2592
-	}
2593
-
2594
-	/**
2595
-	 * Handle </Report>
2596
-	 */
2597
-	private function reportEndHandler() {
2598
-		// Dummy function, to prevent the default HtmlEndHandler() being called
2599
-	}
2600
-
2601
-	/**
2602
-	 * XML </titleEndHandler>
2603
-	 */
2604
-	private function titleEndHandler() {
2605
-		$this->report_root->addTitle($this->text);
2606
-	}
2607
-
2608
-	/**
2609
-	 * XML </descriptionEndHandler>
2610
-	 */
2611
-	private function descriptionEndHandler() {
2612
-		$this->report_root->addDescription($this->text);
2613
-	}
2614
-
2615
-	/**
2616
-	 * Create a list of all descendants.
2617
-	 *
2618
-	 * @param string[] $list
2619
-	 * @param string   $pid
2620
-	 * @param bool  $parents
2621
-	 * @param int  $generations
2622
-	 */
2623
-	private function addDescendancy(&$list, $pid, $parents = false, $generations = -1) {
2624
-		global $WT_TREE;
2625
-
2626
-		$person = Individual::getInstance($pid, $WT_TREE);
2627
-		if ($person === null) {
2628
-			return;
2629
-		}
2630
-		if (!isset($list[$pid])) {
2631
-			$list[$pid] = $person;
2632
-		}
2633
-		if (!isset($list[$pid]->generation)) {
2634
-			$list[$pid]->generation = 0;
2635
-		}
2636
-		foreach ($person->getSpouseFamilies() as $family) {
2637
-			if ($parents) {
2638
-				$husband = $family->getHusband();
2639
-				$wife    = $family->getWife();
2640
-				if ($husband) {
2641
-					$list[$husband->getXref()] = $husband;
2642
-					if (isset($list[$pid]->generation)) {
2643
-						$list[$husband->getXref()]->generation = $list[$pid]->generation - 1;
2644
-					} else {
2645
-						$list[$husband->getXref()]->generation = 1;
2646
-					}
2647
-				}
2648
-				if ($wife) {
2649
-					$list[$wife->getXref()] = $wife;
2650
-					if (isset($list[$pid]->generation)) {
2651
-						$list[$wife->getXref()]->generation = $list[$pid]->generation - 1;
2652
-					} else {
2653
-						$list[$wife->getXref()]->generation = 1;
2654
-					}
2655
-				}
2656
-			}
2657
-			$children = $family->getChildren();
2658
-			foreach ($children as $child) {
2659
-				if ($child) {
2660
-					$list[$child->getXref()] = $child;
2661
-					if (isset($list[$pid]->generation)) {
2662
-						$list[$child->getXref()]->generation = $list[$pid]->generation + 1;
2663
-					} else {
2664
-						$list[$child->getXref()]->generation = 2;
2665
-					}
2666
-				}
2667
-			}
2668
-			if ($generations == -1 || $list[$pid]->generation + 1 < $generations) {
2669
-				foreach ($children as $child) {
2670
-					$this->addDescendancy($list, $child->getXref(), $parents, $generations); // recurse on the childs family
2671
-				}
2672
-			}
2673
-		}
2674
-	}
2675
-
2676
-	/**
2677
-	 * Create a list of all ancestors.
2678
-	 *
2679
-	 * @param string[] $list
2680
-	 * @param string   $pid
2681
-	 * @param bool  $children
2682
-	 * @param int  $generations
2683
-	 */
2684
-	private function addAncestors(&$list, $pid, $children = false, $generations = -1) {
2685
-		global $WT_TREE;
2686
-
2687
-		$genlist                = array($pid);
2688
-		$list[$pid]->generation = 1;
2689
-		while (count($genlist) > 0) {
2690
-			$id = array_shift($genlist);
2691
-			if (strpos($id, 'empty') === 0) {
2692
-				continue; // id can be something like “empty7”
2693
-			}
2694
-			$person = Individual::getInstance($id, $WT_TREE);
2695
-			foreach ($person->getChildFamilies() as $family) {
2696
-				$husband = $family->getHusband();
2697
-				$wife    = $family->getWife();
2698
-				if ($husband) {
2699
-					$list[$husband->getXref()]             = $husband;
2700
-					$list[$husband->getXref()]->generation = $list[$id]->generation + 1;
2701
-				}
2702
-				if ($wife) {
2703
-					$list[$wife->getXref()]             = $wife;
2704
-					$list[$wife->getXref()]->generation = $list[$id]->generation + 1;
2705
-				}
2706
-				if ($generations == -1 || $list[$id]->generation + 1 < $generations) {
2707
-					if ($husband) {
2708
-						array_push($genlist, $husband->getXref());
2709
-					}
2710
-					if ($wife) {
2711
-						array_push($genlist, $wife->getXref());
2712
-					}
2713
-				}
2714
-				if ($children) {
2715
-					foreach ($family->getChildren() as $child) {
2716
-						$list[$child->getXref()] = $child;
2717
-						if (isset($list[$id]->generation)) {
2718
-							$list[$child->getXref()]->generation = $list[$id]->generation;
2719
-						} else {
2720
-							$list[$child->getXref()]->generation = 1;
2721
-						}
2722
-					}
2723
-				}
2724
-			}
2725
-		}
2726
-	}
2727
-
2728
-	/**
2729
-	 * get gedcom tag value
2730
-	 *
2731
-	 * @param string  $tag    The tag to find, use : to delineate subtags
2732
-	 * @param int $level  The gedcom line level of the first tag to find, setting level to 0 will cause it to use 1+ the level of the incoming record
2733
-	 * @param string  $gedrec The gedcom record to get the value from
2734
-	 *
2735
-	 * @return string the value of a gedcom tag from the given gedcom record
2736
-	 */
2737
-	private function getGedcomValue($tag, $level, $gedrec) {
2738
-		global $WT_TREE;
2739
-
2740
-		if (empty($gedrec)) {
2741
-			return '';
2742
-		}
2743
-		$tags      = explode(':', $tag);
2744
-		$origlevel = $level;
2745
-		if ($level == 0) {
2746
-			$level = $gedrec{0} + 1;
2747
-		}
2748
-
2749
-		$subrec = $gedrec;
2750
-		foreach ($tags as $t) {
2751
-			$lastsubrec = $subrec;
2752
-			$subrec     = Functions::getSubRecord($level, "$level $t", $subrec);
2753
-			if (empty($subrec) && $origlevel == 0) {
2754
-				$level--;
2755
-				$subrec = Functions::getSubRecord($level, "$level $t", $lastsubrec);
2756
-			}
2757
-			if (empty($subrec)) {
2758
-				if ($t == "TITL") {
2759
-					$subrec = Functions::getSubRecord($level, "$level ABBR", $lastsubrec);
2760
-					if (!empty($subrec)) {
2761
-						$t = "ABBR";
2762
-					}
2763
-				}
2764
-				if (empty($subrec)) {
2765
-					if ($level > 0) {
2766
-						$level--;
2767
-					}
2768
-					$subrec = Functions::getSubRecord($level, "@ $t", $gedrec);
2769
-					if (empty($subrec)) {
2770
-						return '';
2771
-					}
2772
-				}
2773
-			}
2774
-			$level++;
2775
-		}
2776
-		$level--;
2777
-		$ct = preg_match("/$level $t(.*)/", $subrec, $match);
2778
-		if ($ct == 0) {
2779
-			$ct = preg_match("/$level @.+@ (.+)/", $subrec, $match);
2780
-		}
2781
-		if ($ct == 0) {
2782
-			$ct = preg_match("/@ $t (.+)/", $subrec, $match);
2783
-		}
2784
-		if ($ct > 0) {
2785
-			$value = trim($match[1]);
2786
-			if ($t == 'NOTE' && preg_match('/^@(.+)@$/', $value, $match)) {
2787
-				$note = Note::getInstance($match[1], $WT_TREE);
2788
-				if ($note) {
2789
-					$value = $note->getNote();
2790
-				} else {
2791
-					//-- set the value to the id without the @
2792
-					$value = $match[1];
2793
-				}
2794
-			}
2795
-			if ($level != 0 || $t != "NOTE") {
2796
-				$value .= Functions::getCont($level + 1, $subrec);
2797
-			}
2798
-
2799
-			return $value;
2800
-		}
2801
-
2802
-		return "";
2803
-	}
2804
-
2805
-	/**
2806
-	 * Replace variable identifiers with their values.
2807
-	 *
2808
-	 * @param string $expression An expression such as "$foo == 123"
2809
-	 * @param bool   $quote      Whether to add quotation marks
2810
-	 *
2811
-	 * @return string
2812
-	 */
2813
-	private function substituteVars($expression, $quote) {
2814
-		$that = $this; // PHP5.3 cannot access $this inside a closure
2815
-		return preg_replace_callback(
2816
-			'/\$(\w+)/',
2817
-			function ($matches) use ($that, $quote) {
2818
-				if (isset($that->vars[$matches[1]]['id'])) {
2819
-					if ($quote) {
2820
-						return "'" . addcslashes($that->vars[$matches[1]]['id'], "'") . "'";
2821
-					} else {
2822
-						return $that->vars[$matches[1]]['id'];
2823
-					}
2824
-				} else {
2825
-					Log::addErrorLog(sprintf('Undefined variable $%s in report', $matches[1]));
2826
-
2827
-					return '$' . $matches[1];
2828
-				}
2829
-			},
2830
-			$expression
2831
-		);
2832
-	}
53
+    /** @var int Are we processing repeats*/
54
+    private $process_repeats = 0;
55
+
56
+    /** @var int Quantity of data to repeat during loops */
57
+    private $repeat_bytes = 0;
58
+
59
+    /** @var array[] Repeated data when iterating over loops */
60
+    private $repeats = array();
61
+
62
+    /** @var array[] Nested repeating data */
63
+    private $repeats_stack = array();
64
+
65
+    /** @var ReportBase[] Nested repeating data */
66
+    private $wt_report_stack = array();
67
+
68
+    /** @var resource Nested repeating data */
69
+    private $parser;
70
+
71
+    /** @var resource[] Nested repeating data */
72
+    private $parser_stack = array();
73
+
74
+    /** @var string The current GEDCOM record */
75
+    private $gedrec = '';
76
+
77
+    /** @var string[] Nested GEDCOM records */
78
+    private $gedrec_stack = array();
79
+
80
+    /** @var ReportBaseElement The currently processed element */
81
+    private $current_element;
82
+
83
+    /** @var ReportBaseElement The currently processed element */
84
+    private $footnote_element;
85
+
86
+    /** @var string The GEDCOM fact currently being processed */
87
+    private $fact = '';
88
+
89
+    /** @var string The GEDCOM value currently being processed */
90
+    private $desc = '';
91
+
92
+    /** @var string The GEDCOM type currently being processed */
93
+    private $type = '';
94
+
95
+    /** @var int The current generational level */
96
+    private $generation = 1;
97
+
98
+    /** @var array Source data for processing lists */
99
+    private $list = array();
100
+
101
+    /** @var int Number of items in lists */
102
+    private $list_total = 0;
103
+
104
+    /** @var int Number of items filtered from lists */
105
+    private $list_private = 0;
106
+
107
+    /** @var ReportBase A factory for creating report elements */
108
+    private $report_root;
109
+
110
+    /** @var ReportBase Nested report elements */
111
+    private $wt_report;
112
+
113
+    /** @todo This attribute is public to support the PHP5.3 closure workaround. */
114
+    /** @var string[][] Variables defined in the report at run-time */
115
+    public $vars;
116
+
117
+    /**
118
+     * Create a parser for a report
119
+     *
120
+     * @param string     $report     The XML filename
121
+     * @param ReportBase $report_root
122
+     * @param string[][] $vars
123
+     */
124
+    public function __construct($report, ReportBase $report_root = null, array $vars = array()) {
125
+        $this->report_root     = $report_root;
126
+        $this->wt_report       = $report_root;
127
+        $this->current_element = new ReportBaseElement;
128
+        $this->vars            = $vars;
129
+        parent::__construct($report);
130
+    }
131
+
132
+    /**
133
+     * XML start element handler
134
+     *
135
+     * This function is called whenever a starting element is reached
136
+     * The element handler will be called if found, otherwise it must be HTML
137
+     *
138
+     * @param resource $parser the resource handler for the XML parser
139
+     * @param string   $name   the name of the XML element parsed
140
+     * @param array    $attrs  an array of key value pairs for the attributes
141
+     */
142
+    protected function startElement($parser, $name, $attrs) {
143
+        $newattrs = array();
144
+
145
+        foreach ($attrs as $key => $value) {
146
+            if (preg_match("/^\\$(\w+)$/", $value, $match)) {
147
+                if ((isset($this->vars[$match[1]]['id'])) && (!isset($this->vars[$match[1]]['gedcom']))) {
148
+                    $value = $this->vars[$match[1]]['id'];
149
+                }
150
+            }
151
+            $newattrs[$key] = $value;
152
+        }
153
+        $attrs = $newattrs;
154
+        if ($this->process_footnote && ($this->process_ifs === 0 || $name === "if") && ($this->process_gedcoms === 0 || $name === "Gedcom") && ($this->process_repeats === 0 || $name === "Facts" || $name === "RepeatTag")) {
155
+            $start_method = $name . 'StartHandler';
156
+            $end_method   = $name . 'EndHandler';
157
+            if (method_exists($this, $start_method)) {
158
+                $this->$start_method($attrs);
159
+            } elseif (!method_exists($this, $end_method)) {
160
+                $this->htmlStartHandler($name, $attrs);
161
+            }
162
+        }
163
+    }
164
+
165
+    /**
166
+     * XML end element handler
167
+     *
168
+     * This function is called whenever an ending element is reached
169
+     * The element handler will be called if found, otherwise it must be HTML
170
+     *
171
+     * @param resource $parser the resource handler for the XML parser
172
+     * @param string   $name   the name of the XML element parsed
173
+     */
174
+    protected function endElement($parser, $name) {
175
+        if (($this->process_footnote || $name === "Footnote") && ($this->process_ifs === 0 || $name === "if") && ($this->process_gedcoms === 0 || $name === "Gedcom") && ($this->process_repeats === 0 || $name === "Facts" || $name === "RepeatTag" || $name === "List" || $name === "Relatives")) {
176
+            $start_method = $name . 'StartHandler';
177
+            $end_method   = $name . 'EndHandler';
178
+            if (method_exists($this, $end_method)) {
179
+                $this->$end_method();
180
+            } elseif (!method_exists($this, $start_method)) {
181
+                $this->htmlEndHandler($name);
182
+            }
183
+        }
184
+    }
185
+
186
+    /**
187
+     * XML character data handler
188
+     *
189
+     * @param resource $parser the resource handler for the XML parser
190
+     * @param string   $data   the name of the XML element parsed
191
+     */
192
+    protected function characterData($parser, $data) {
193
+        if ($this->print_data && $this->process_gedcoms === 0 && $this->process_ifs === 0 && $this->process_repeats === 0) {
194
+            $this->current_element->addText($data);
195
+        }
196
+    }
197
+
198
+    /**
199
+     * XML <style>
200
+     *
201
+     * @param array $attrs an array of key value pairs for the attributes
202
+     */
203
+    private function styleStartHandler($attrs) {
204
+        if (empty($attrs['name'])) {
205
+            throw new \DomainException('REPORT ERROR Style: The "name" of the style is missing or not set in the XML file.');
206
+        }
207
+
208
+        // array Style that will be passed on
209
+        $s = array();
210
+
211
+        // string Name af the style
212
+        $s['name'] = $attrs['name'];
213
+
214
+        // string Name of the DEFAULT font
215
+        $s['font'] = $this->wt_report->defaultFont;
216
+        if (!empty($attrs['font'])) {
217
+            $s['font'] = $attrs['font'];
218
+        }
219
+
220
+        // int The size of the font in points
221
+        $s['size'] = $this->wt_report->defaultFontSize;
222
+        if (!empty($attrs['size'])) {
223
+            $s['size'] = (int) $attrs['size'];
224
+        } // Get it as int to ignore all decimal points or text (if any text then int(0))
225
+
226
+        // string B: bold, I: italic, U: underline, D: line trough, The default value is regular.
227
+        $s['style'] = "";
228
+        if (!empty($attrs['style'])) {
229
+            $s['style'] = $attrs['style'];
230
+        }
231
+
232
+        $this->wt_report->addStyle($s);
233
+    }
234
+
235
+    /**
236
+     * XML <Doc>
237
+     *
238
+     * Sets up the basics of the document proparties
239
+     *
240
+     * @param array $attrs an array of key value pairs for the attributes
241
+     */
242
+    private function docStartHandler($attrs) {
243
+        $this->parser = $this->xml_parser;
244
+
245
+        // Custom page width
246
+        if (!empty($attrs['customwidth'])) {
247
+            $this->wt_report->pagew = (int) $attrs['customwidth'];
248
+        } // Get it as int to ignore all decimal points or text (if any text then int(0))
249
+        // Custom Page height
250
+        if (!empty($attrs['customheight'])) {
251
+            $this->wt_report->pageh = (int) $attrs['customheight'];
252
+        } // Get it as int to ignore all decimal points or text (if any text then int(0))
253
+
254
+        // Left Margin
255
+        if (isset($attrs['leftmargin'])) {
256
+            if ($attrs['leftmargin'] === "0") {
257
+                $this->wt_report->leftmargin = 0;
258
+            } elseif (!empty($attrs['leftmargin'])) {
259
+                $this->wt_report->leftmargin = (int) $attrs['leftmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
260
+            }
261
+        }
262
+        // Right Margin
263
+        if (isset($attrs['rightmargin'])) {
264
+            if ($attrs['rightmargin'] === "0") {
265
+                $this->wt_report->rightmargin = 0;
266
+            } elseif (!empty($attrs['rightmargin'])) {
267
+                $this->wt_report->rightmargin = (int) $attrs['rightmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
268
+            }
269
+        }
270
+        // Top Margin
271
+        if (isset($attrs['topmargin'])) {
272
+            if ($attrs['topmargin'] === "0") {
273
+                $this->wt_report->topmargin = 0;
274
+            } elseif (!empty($attrs['topmargin'])) {
275
+                $this->wt_report->topmargin = (int) $attrs['topmargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
276
+            }
277
+        }
278
+        // Bottom Margin
279
+        if (isset($attrs['bottommargin'])) {
280
+            if ($attrs['bottommargin'] === "0") {
281
+                $this->wt_report->bottommargin = 0;
282
+            } elseif (!empty($attrs['bottommargin'])) {
283
+                $this->wt_report->bottommargin = (int) $attrs['bottommargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
284
+            }
285
+        }
286
+        // Header Margin
287
+        if (isset($attrs['headermargin'])) {
288
+            if ($attrs['headermargin'] === "0") {
289
+                $this->wt_report->headermargin = 0;
290
+            } elseif (!empty($attrs['headermargin'])) {
291
+                $this->wt_report->headermargin = (int) $attrs['headermargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
292
+            }
293
+        }
294
+        // Footer Margin
295
+        if (isset($attrs['footermargin'])) {
296
+            if ($attrs['footermargin'] === "0") {
297
+                $this->wt_report->footermargin = 0;
298
+            } elseif (!empty($attrs['footermargin'])) {
299
+                $this->wt_report->footermargin = (int) $attrs['footermargin']; // Get it as int to ignore all decimal points or text (if any text then int(0))
300
+            }
301
+        }
302
+
303
+        // Page Orientation
304
+        if (!empty($attrs['orientation'])) {
305
+            if ($attrs['orientation'] == "landscape") {
306
+                $this->wt_report->orientation = "landscape";
307
+            } elseif ($attrs['orientation'] == "portrait") {
308
+                $this->wt_report->orientation = "portrait";
309
+            }
310
+        }
311
+        // Page Size
312
+        if (!empty($attrs['pageSize'])) {
313
+            $this->wt_report->pageFormat = strtoupper($attrs['pageSize']);
314
+        }
315
+
316
+        // Show Generated By...
317
+        if (isset($attrs['showGeneratedBy'])) {
318
+            if ($attrs['showGeneratedBy'] === "0") {
319
+                $this->wt_report->showGenText = false;
320
+            } elseif ($attrs['showGeneratedBy'] === "1") {
321
+                $this->wt_report->showGenText = true;
322
+            }
323
+        }
324
+
325
+        $this->wt_report->setup();
326
+    }
327
+
328
+    /**
329
+     * XML </Doc>
330
+     */
331
+    private function docEndHandler() {
332
+        $this->wt_report->run();
333
+    }
334
+
335
+    /**
336
+     * XML <Header>
337
+     */
338
+    private function headerStartHandler() {
339
+        // Clear the Header before any new elements are added
340
+        $this->wt_report->clearHeader();
341
+        $this->wt_report->setProcessing("H");
342
+    }
343
+
344
+    /**
345
+     * XML <PageHeader>
346
+     */
347
+    private function pageHeaderStartHandler() {
348
+        array_push($this->print_data_stack, $this->print_data);
349
+        $this->print_data = false;
350
+        array_push($this->wt_report_stack, $this->wt_report);
351
+        $this->wt_report = $this->report_root->createPageHeader();
352
+    }
353
+
354
+    /**
355
+     * XML <pageHeaderEndHandler>
356
+     */
357
+    private function pageHeaderEndHandler() {
358
+        $this->print_data        = array_pop($this->print_data_stack);
359
+        $this->current_element   = $this->wt_report;
360
+        $this->wt_report         = array_pop($this->wt_report_stack);
361
+        $this->wt_report->addElement($this->current_element);
362
+    }
363
+
364
+    /**
365
+     * XML <bodyStartHandler>
366
+     */
367
+    private function bodyStartHandler() {
368
+        $this->wt_report->setProcessing("B");
369
+    }
370
+
371
+    /**
372
+     * XML <footerStartHandler>
373
+     */
374
+    private function footerStartHandler() {
375
+        $this->wt_report->setProcessing("F");
376
+    }
377
+
378
+    /**
379
+     * XML <Cell>
380
+     *
381
+     * @param array $attrs an array of key value pairs for the attributes
382
+     */
383
+    private function cellStartHandler($attrs) {
384
+        // string The text alignment of the text in this box.
385
+        $align = "";
386
+        if (!empty($attrs['align'])) {
387
+            $align = $attrs['align'];
388
+            // RTL supported left/right alignment
389
+            if ($align == "rightrtl") {
390
+                if ($this->wt_report->rtl) {
391
+                    $align = "left";
392
+                } else {
393
+                    $align = "right";
394
+                }
395
+            } elseif ($align == "leftrtl") {
396
+                if ($this->wt_report->rtl) {
397
+                    $align = "right";
398
+                } else {
399
+                    $align = "left";
400
+                }
401
+            }
402
+        }
403
+
404
+        // string The color to fill the background of this cell
405
+        $bgcolor = "";
406
+        if (!empty($attrs['bgcolor'])) {
407
+            $bgcolor = $attrs['bgcolor'];
408
+        }
409
+
410
+        // int Whether or not the background should be painted
411
+        $fill = 1;
412
+        if (isset($attrs['fill'])) {
413
+            if ($attrs['fill'] === "0") {
414
+                $fill = 0;
415
+            } elseif ($attrs['fill'] === "1") {
416
+                $fill = 1;
417
+            }
418
+        }
419
+
420
+        $reseth = true;
421
+        // boolean   if true reset the last cell height (default true)
422
+        if (isset($attrs['reseth'])) {
423
+            if ($attrs['reseth'] === "0") {
424
+                $reseth = false;
425
+            } elseif ($attrs['reseth'] === "1") {
426
+                $reseth = true;
427
+            }
428
+        }
429
+
430
+        // mixed Whether or not a border should be printed around this box
431
+        $border = 0;
432
+        if (!empty($attrs['border'])) {
433
+            $border = $attrs['border'];
434
+        }
435
+        // string Border color in HTML code
436
+        $bocolor = "";
437
+        if (!empty($attrs['bocolor'])) {
438
+            $bocolor = $attrs['bocolor'];
439
+        }
440
+
441
+        // int Cell height (expressed in points) The starting height of this cell. If the text wraps the height will automatically be adjusted.
442
+        $height = 0;
443
+        if (!empty($attrs['height'])) {
444
+            $height = (int) $attrs['height'];
445
+        }
446
+        // int Cell width (expressed in points) Setting the width to 0 will make it the width from the current location to the right margin.
447
+        $width = 0;
448
+        if (!empty($attrs['width'])) {
449
+            $width = (int) $attrs['width'];
450
+        }
451
+
452
+        // int Stretch carachter mode
453
+        $stretch = 0;
454
+        if (!empty($attrs['stretch'])) {
455
+            $stretch = (int) $attrs['stretch'];
456
+        }
457
+
458
+        // mixed Position the left corner of this box on the page. The default is the current position.
459
+        $left = ".";
460
+        if (isset($attrs['left'])) {
461
+            if ($attrs['left'] === ".") {
462
+                $left = ".";
463
+            } elseif (!empty($attrs['left'])) {
464
+                $left = (int) $attrs['left'];
465
+            } elseif ($attrs['left'] === "0") {
466
+                $left = 0;
467
+            }
468
+        }
469
+        // mixed Position the top corner of this box on the page. the default is the current position
470
+        $top = ".";
471
+        if (isset($attrs['top'])) {
472
+            if ($attrs['top'] === ".") {
473
+                $top = ".";
474
+            } elseif (!empty($attrs['top'])) {
475
+                $top = (int) $attrs['top'];
476
+            } elseif ($attrs['top'] === "0") {
477
+                $top = 0;
478
+            }
479
+        }
480
+
481
+        // string The name of the Style that should be used to render the text.
482
+        $style = "";
483
+        if (!empty($attrs['style'])) {
484
+            $style = $attrs['style'];
485
+        }
486
+
487
+        // string Text color in html code
488
+        $tcolor = "";
489
+        if (!empty($attrs['tcolor'])) {
490
+            $tcolor = $attrs['tcolor'];
491
+        }
492
+
493
+        // int Indicates where the current position should go after the call.
494
+        $ln = 0;
495
+        if (isset($attrs['newline'])) {
496
+            if (!empty($attrs['newline'])) {
497
+                $ln = (int) $attrs['newline'];
498
+            } elseif ($attrs['newline'] === "0") {
499
+                $ln = 0;
500
+            }
501
+        }
502
+
503
+        if ($align == "left") {
504
+            $align = "L";
505
+        } elseif ($align == "right") {
506
+            $align = "R";
507
+        } elseif ($align == "center") {
508
+            $align = "C";
509
+        } elseif ($align == "justify") {
510
+            $align = "J";
511
+        }
512
+
513
+        array_push($this->print_data_stack, $this->print_data);
514
+        $this->print_data = true;
515
+
516
+        $this->current_element = $this->report_root->createCell(
517
+            $width,
518
+            $height,
519
+            $border,
520
+            $align,
521
+            $bgcolor,
522
+            $style,
523
+            $ln,
524
+            $top,
525
+            $left,
526
+            $fill,
527
+            $stretch,
528
+            $bocolor,
529
+            $tcolor,
530
+            $reseth
531
+        );
532
+    }
533
+
534
+    /**
535
+     * XML </Cell>
536
+     */
537
+    private function cellEndHandler() {
538
+        $this->print_data = array_pop($this->print_data_stack);
539
+        $this->wt_report->addElement($this->current_element);
540
+    }
541
+
542
+    /**
543
+     * XML <Now /> element handler
544
+     */
545
+    private function nowStartHandler() {
546
+        $g = FunctionsDate::timestampToGedcomDate(WT_TIMESTAMP + WT_TIMESTAMP_OFFSET);
547
+        $this->current_element->addText($g->display());
548
+    }
549
+
550
+    /**
551
+     * XML <PageNum /> element handler
552
+     */
553
+    private function pageNumStartHandler() {
554
+        $this->current_element->addText("#PAGENUM#");
555
+    }
556
+
557
+    /**
558
+     * XML <TotalPages /> element handler
559
+     */
560
+    private function totalPagesStartHandler() {
561
+        $this->current_element->addText("{{:ptp:}}");
562
+    }
563
+
564
+    /**
565
+     * Called at the start of an element.
566
+     *
567
+     * @param array $attrs an array of key value pairs for the attributes
568
+     */
569
+    private function gedcomStartHandler($attrs) {
570
+        global $WT_TREE;
571
+
572
+        if ($this->process_gedcoms > 0) {
573
+            $this->process_gedcoms++;
574
+
575
+            return;
576
+        }
577
+
578
+        $tag       = $attrs['id'];
579
+        $tag       = str_replace("@fact", $this->fact, $tag);
580
+        $tags      = explode(":", $tag);
581
+        $newgedrec = '';
582
+        if (count($tags) < 2) {
583
+            $tmp       = GedcomRecord::getInstance($attrs['id'], $WT_TREE);
584
+            $newgedrec = $tmp ? $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE)) : '';
585
+        }
586
+        if (empty($newgedrec)) {
587
+            $tgedrec   = $this->gedrec;
588
+            $newgedrec = '';
589
+            foreach ($tags as $tag) {
590
+                if (preg_match("/\\$(.+)/", $tag, $match)) {
591
+                    if (isset($this->vars[$match[1]]['gedcom'])) {
592
+                        $newgedrec = $this->vars[$match[1]]['gedcom'];
593
+                    } else {
594
+                        $tmp       = GedcomRecord::getInstance($match[1], $WT_TREE);
595
+                        $newgedrec = $tmp ? $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE)) : '';
596
+                    }
597
+                } else {
598
+                    if (preg_match("/@(.+)/", $tag, $match)) {
599
+                        $gmatch = array();
600
+                        if (preg_match("/\d $match[1] @([^@]+)@/", $tgedrec, $gmatch)) {
601
+                            $tmp       = GedcomRecord::getInstance($gmatch[1], $WT_TREE);
602
+                            $newgedrec = $tmp ? $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE)) : '';
603
+                            $tgedrec   = $newgedrec;
604
+                        } else {
605
+                            $newgedrec = '';
606
+                            break;
607
+                        }
608
+                    } else {
609
+                        $temp      = explode(" ", trim($tgedrec));
610
+                        $level     = $temp[0] + 1;
611
+                        $newgedrec = Functions::getSubRecord($level, "$level $tag", $tgedrec);
612
+                        $tgedrec   = $newgedrec;
613
+                    }
614
+                }
615
+            }
616
+        }
617
+        if (!empty($newgedrec)) {
618
+            array_push($this->gedrec_stack, array($this->gedrec, $this->fact, $this->desc));
619
+            $this->gedrec = $newgedrec;
620
+            if (preg_match("/(\d+) (_?[A-Z0-9]+) (.*)/", $this->gedrec, $match)) {
621
+                $this->fact = $match[2];
622
+                $this->desc = trim($match[3]);
623
+            }
624
+        } else {
625
+            $this->process_gedcoms++;
626
+        }
627
+    }
628
+
629
+    /**
630
+     * Called at the end of an element.
631
+     */
632
+    private function gedcomEndHandler() {
633
+        if ($this->process_gedcoms > 0) {
634
+            $this->process_gedcoms--;
635
+        } else {
636
+            list($this->gedrec, $this->fact, $this->desc) = array_pop($this->gedrec_stack);
637
+        }
638
+    }
639
+
640
+    /**
641
+     * XML <textBoxStartHandler>
642
+     *
643
+     * @param array $attrs an array of key value pairs for the attributes
644
+     */
645
+    private function textBoxStartHandler($attrs) {
646
+        // string Background color code
647
+        $bgcolor = "";
648
+        if (!empty($attrs['bgcolor'])) {
649
+            $bgcolor = $attrs['bgcolor'];
650
+        }
651
+
652
+        // boolean Wether or not fill the background color
653
+        $fill = true;
654
+        if (isset($attrs['fill'])) {
655
+            if ($attrs['fill'] === "0") {
656
+                $fill = false;
657
+            } elseif ($attrs['fill'] === "1") {
658
+                $fill = true;
659
+            }
660
+        }
661
+
662
+        // var boolean Whether or not a border should be printed around this box. 0 = no border, 1 = border. Default is 0
663
+        $border = false;
664
+        if (isset($attrs['border'])) {
665
+            if ($attrs['border'] === "1") {
666
+                $border = true;
667
+            } elseif ($attrs['border'] === "0") {
668
+                $border = false;
669
+            }
670
+        }
671
+
672
+        // int The starting height of this cell. If the text wraps the height will automatically be adjusted
673
+        $height = 0;
674
+        if (!empty($attrs['height'])) {
675
+            $height = (int) $attrs['height'];
676
+        }
677
+        // int Setting the width to 0 will make it the width from the current location to the margin
678
+        $width = 0;
679
+        if (!empty($attrs['width'])) {
680
+            $width = (int) $attrs['width'];
681
+        }
682
+
683
+        // mixed Position the left corner of this box on the page. The default is the current position.
684
+        $left = ".";
685
+        if (isset($attrs['left'])) {
686
+            if ($attrs['left'] === ".") {
687
+                $left = ".";
688
+            } elseif (!empty($attrs['left'])) {
689
+                $left = (int) $attrs['left'];
690
+            } elseif ($attrs['left'] === "0") {
691
+                $left = 0;
692
+            }
693
+        }
694
+        // mixed Position the top corner of this box on the page. the default is the current position
695
+        $top = ".";
696
+        if (isset($attrs['top'])) {
697
+            if ($attrs['top'] === ".") {
698
+                $top = ".";
699
+            } elseif (!empty($attrs['top'])) {
700
+                $top = (int) $attrs['top'];
701
+            } elseif ($attrs['top'] === "0") {
702
+                $top = 0;
703
+            }
704
+        }
705
+        // boolean After this box is finished rendering, should the next section of text start immediately after the this box or should it start on a new line under this box. 0 = no new line, 1 = force new line. Default is 0
706
+        $newline = false;
707
+        if (isset($attrs['newline'])) {
708
+            if ($attrs['newline'] === "1") {
709
+                $newline = true;
710
+            } elseif ($attrs['newline'] === "0") {
711
+                $newline = false;
712
+            }
713
+        }
714
+        // boolean
715
+        $pagecheck = true;
716
+        if (isset($attrs['pagecheck'])) {
717
+            if ($attrs['pagecheck'] === "0") {
718
+                $pagecheck = false;
719
+            } elseif ($attrs['pagecheck'] === "1") {
720
+                $pagecheck = true;
721
+            }
722
+        }
723
+        // boolean Cell padding
724
+        $padding = true;
725
+        if (isset($attrs['padding'])) {
726
+            if ($attrs['padding'] === "0") {
727
+                $padding = false;
728
+            } elseif ($attrs['padding'] === "1") {
729
+                $padding = true;
730
+            }
731
+        }
732
+        // boolean Reset this box Height
733
+        $reseth = false;
734
+        if (isset($attrs['reseth'])) {
735
+            if ($attrs['reseth'] === "1") {
736
+                $reseth = true;
737
+            } elseif ($attrs['reseth'] === "0") {
738
+                $reseth = false;
739
+            }
740
+        }
741
+
742
+        // string Style of rendering
743
+        $style = "";
744
+
745
+        array_push($this->print_data_stack, $this->print_data);
746
+        $this->print_data = false;
747
+
748
+        array_push($this->wt_report_stack, $this->wt_report);
749
+        $this->wt_report = $this->report_root->createTextBox(
750
+            $width,
751
+            $height,
752
+            $border,
753
+            $bgcolor,
754
+            $newline,
755
+            $left,
756
+            $top,
757
+            $pagecheck,
758
+            $style,
759
+            $fill,
760
+            $padding,
761
+            $reseth
762
+        );
763
+    }
764
+
765
+    /**
766
+     * XML <textBoxEndHandler>
767
+     */
768
+    private function textBoxEndHandler() {
769
+        $this->print_data      = array_pop($this->print_data_stack);
770
+        $this->current_element = $this->wt_report;
771
+        $this->wt_report       = array_pop($this->wt_report_stack);
772
+        $this->wt_report->addElement($this->current_element);
773
+    }
774
+
775
+    /**
776
+     * XLM <Text>.
777
+     *
778
+     * @param array $attrs an array of key value pairs for the attributes
779
+     */
780
+    private function textStartHandler($attrs) {
781
+        array_push($this->print_data_stack, $this->print_data);
782
+        $this->print_data = true;
783
+
784
+        // string The name of the Style that should be used to render the text.
785
+        $style = "";
786
+        if (!empty($attrs['style'])) {
787
+            $style = $attrs['style'];
788
+        }
789
+
790
+        // string  The color of the text - Keep the black color as default
791
+        $color = "";
792
+        if (!empty($attrs['color'])) {
793
+            $color = $attrs['color'];
794
+        }
795
+
796
+        $this->current_element = $this->report_root->createText($style, $color);
797
+    }
798
+
799
+    /**
800
+     * XML </Text>
801
+     */
802
+    private function textEndHandler() {
803
+        $this->print_data = array_pop($this->print_data_stack);
804
+        $this->wt_report->addElement($this->current_element);
805
+    }
806
+
807
+    /**
808
+     * XML <GetPersonName/>
809
+     *
810
+     * Get the name
811
+     * 1. id is empty - current GEDCOM record
812
+     * 2. id is set with a record id
813
+     *
814
+     * @param array $attrs an array of key value pairs for the attributes
815
+     */
816
+    private function getPersonNameStartHandler($attrs) {
817
+        global $WT_TREE;
818
+
819
+        $id    = "";
820
+        $match = array();
821
+        if (empty($attrs['id'])) {
822
+            if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
823
+                $id = $match[1];
824
+            }
825
+        } else {
826
+            if (preg_match("/\\$(.+)/", $attrs['id'], $match)) {
827
+                if (isset($this->vars[$match[1]]['id'])) {
828
+                    $id = $this->vars[$match[1]]['id'];
829
+                }
830
+            } else {
831
+                if (preg_match("/@(.+)/", $attrs['id'], $match)) {
832
+                    $gmatch = array();
833
+                    if (preg_match("/\d $match[1] @([^@]+)@/", $this->gedrec, $gmatch)) {
834
+                        $id = $gmatch[1];
835
+                    }
836
+                } else {
837
+                    $id = $attrs['id'];
838
+                }
839
+            }
840
+        }
841
+        if (!empty($id)) {
842
+            $record = GedcomRecord::getInstance($id, $WT_TREE);
843
+            if (is_null($record)) {
844
+                return;
845
+            }
846
+            if (!$record->canShowName()) {
847
+                $this->current_element->addText(I18N::translate('Private'));
848
+            } else {
849
+                $name = $record->getFullName();
850
+                $name = preg_replace(
851
+                    array('/<span class="starredname">/', '/<\/span><\/span>/', '/<\/span>/'),
852
+                    array('«', '', '»'),
853
+                    $name
854
+                );
855
+                $name = strip_tags($name);
856
+                if (!empty($attrs['truncate'])) {
857
+                    if (mb_strlen($name) > $attrs['truncate']) {
858
+                        $name  = preg_replace("/\(.*\) ?/", '', $name); //removes () and text inbetween - what about ", [ and { etc?
859
+                        $words = preg_split('/[, -]+/', $name); // names separated with space, comma or hyphen - any others?
860
+                        $name  = $words[count($words) - 1];
861
+                        for ($i = count($words) - 2; $i >= 0; $i--) {
862
+                            $len = mb_strlen($name);
863
+                            for ($j = count($words) - 3; $j >= 0; $j--) {
864
+                                $len += mb_strlen($words[$j]);
865
+                            }
866
+                            if ($len > $attrs['truncate']) {
867
+                                $first_letter = mb_substr($words[$i], 0, 1);
868
+                                // Do not show " of nick-names
869
+                                if ($first_letter != "\"") {
870
+                                    $name = mb_substr($words[$i], 0, 1) . '. ' . $name;
871
+                                }
872
+                            } else {
873
+                                $name = $words[$i] . ' ' . $name;
874
+                            }
875
+                        }
876
+                    }
877
+                } else {
878
+                    $addname = $record->getAddName();
879
+                    $addname = preg_replace(
880
+                        array('/<span class="starredname">/', '/<\/span><\/span>/', '/<\/span>/'),
881
+                        array('«', '', '»'),
882
+                        $addname
883
+                    );
884
+                    $addname = strip_tags($addname);
885
+                    if (!empty($addname)) {
886
+                        $name .= " " . $addname;
887
+                    }
888
+                }
889
+                $this->current_element->addText(trim($name));
890
+            }
891
+        }
892
+    }
893
+
894
+    /**
895
+     * XML <GedcomValue/>
896
+     *
897
+     * @param array $attrs an array of key value pairs for the attributes
898
+     */
899
+    private function gedcomValueStartHandler($attrs) {
900
+        global $WT_TREE;
901
+
902
+        $id    = "";
903
+        $match = array();
904
+        if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
905
+            $id = $match[1];
906
+        }
907
+
908
+        if (isset($attrs['newline']) && $attrs['newline'] == "1") {
909
+            $useBreak = "1";
910
+        } else {
911
+            $useBreak = "0";
912
+        }
913
+
914
+        $tag = $attrs['tag'];
915
+        if (!empty($tag)) {
916
+            if ($tag == "@desc") {
917
+                $value = $this->desc;
918
+                $value = trim($value);
919
+                $this->current_element->addText($value);
920
+            }
921
+            if ($tag == "@id") {
922
+                $this->current_element->addText($id);
923
+            } else {
924
+                $tag = str_replace("@fact", $this->fact, $tag);
925
+                if (empty($attrs['level'])) {
926
+                    $temp  = explode(" ", trim($this->gedrec));
927
+                    $level = $temp[0];
928
+                    if ($level == 0) {
929
+                        $level++;
930
+                    }
931
+                } else {
932
+                    $level = $attrs['level'];
933
+                }
934
+                $tags  = preg_split('/[: ]/', $tag);
935
+                $value = $this->getGedcomValue($tag, $level, $this->gedrec);
936
+                switch (end($tags)) {
937
+                case 'DATE':
938
+                    $tmp   = new Date($value);
939
+                    $value = $tmp->display();
940
+                    break;
941
+                case 'PLAC':
942
+                    $tmp   = new Place($value, $WT_TREE);
943
+                    $value = $tmp->getShortName();
944
+                    break;
945
+                }
946
+                if ($useBreak == "1") {
947
+                    // Insert <br> when multiple dates exist.
948
+                    // This works around a TCPDF bug that incorrectly wraps RTL dates on LTR pages
949
+                    $value = str_replace('(', '<br>(', $value);
950
+                    $value = str_replace('<span dir="ltr"><br>', '<br><span dir="ltr">', $value);
951
+                    $value = str_replace('<span dir="rtl"><br>', '<br><span dir="rtl">', $value);
952
+                    if (substr($value, 0, 6) == '<br>') {
953
+                        $value = substr($value, 6);
954
+                    }
955
+                }
956
+                $tmp = explode(':', $tag);
957
+                if (in_array(end($tmp), array('NOTE', 'TEXT'))) {
958
+                    $value = Filter::formatText($value, $WT_TREE); // We'll strip HTML in addText()
959
+                }
960
+                $this->current_element->addText($value);
961
+            }
962
+        }
963
+    }
964
+
965
+    /**
966
+     * XML <RepeatTag>
967
+     *
968
+     * @param array $attrs an array of key value pairs for the attributes
969
+     */
970
+    private function repeatTagStartHandler($attrs) {
971
+        global $WT_TREE;
972
+
973
+        $this->process_repeats++;
974
+        if ($this->process_repeats > 1) {
975
+            return;
976
+        }
977
+
978
+        array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
979
+        $this->repeats      = array();
980
+        $this->repeat_bytes = xml_get_current_line_number($this->parser);
981
+
982
+        $tag = "";
983
+        if (isset($attrs['tag'])) {
984
+            $tag = $attrs['tag'];
985
+        }
986
+        if (!empty($tag)) {
987
+            if ($tag == "@desc") {
988
+                $value = $this->desc;
989
+                $value = trim($value);
990
+                $this->current_element->addText($value);
991
+            } else {
992
+                $tag   = str_replace("@fact", $this->fact, $tag);
993
+                $tags  = explode(":", $tag);
994
+                $temp  = explode(" ", trim($this->gedrec));
995
+                $level = $temp[0];
996
+                if ($level == 0) {
997
+                    $level++;
998
+                }
999
+                $subrec = $this->gedrec;
1000
+                $t      = $tag;
1001
+                $count  = count($tags);
1002
+                $i      = 0;
1003
+                while ($i < $count) {
1004
+                    $t = $tags[$i];
1005
+                    if (!empty($t)) {
1006
+                        if ($i < ($count - 1)) {
1007
+                            $subrec = Functions::getSubRecord($level, "$level $t", $subrec);
1008
+                            if (empty($subrec)) {
1009
+                                $level--;
1010
+                                $subrec = Functions::getSubRecord($level, "@ $t", $this->gedrec);
1011
+                                if (empty($subrec)) {
1012
+                                    return;
1013
+                                }
1014
+                            }
1015
+                        }
1016
+                        $level++;
1017
+                    }
1018
+                    $i++;
1019
+                }
1020
+                $level--;
1021
+                $count = preg_match_all("/$level $t(.*)/", $subrec, $match, PREG_SET_ORDER);
1022
+                $i     = 0;
1023
+                while ($i < $count) {
1024
+                    $i++;
1025
+                    // Privacy check - is this a link, and are we allowed to view the linked object?
1026
+                    $subrecord = Functions::getSubRecord($level, "$level $t", $subrec, $i);
1027
+                    if (preg_match('/^\d ' . WT_REGEX_TAG . ' @(' . WT_REGEX_XREF . ')@/', $subrecord, $xref_match)) {
1028
+                        $linked_object = GedcomRecord::getInstance($xref_match[1], $WT_TREE);
1029
+                        if ($linked_object && !$linked_object->canShow()) {
1030
+                            continue;
1031
+                        }
1032
+                    }
1033
+                    $this->repeats[] = $subrecord;
1034
+                }
1035
+            }
1036
+        }
1037
+    }
1038
+
1039
+    /**
1040
+     * XML </ RepeatTag>
1041
+     */
1042
+    private function repeatTagEndHandler() {
1043
+        global $report;
1044
+
1045
+        $this->process_repeats--;
1046
+        if ($this->process_repeats > 0) {
1047
+            return;
1048
+        }
1049
+
1050
+        // Check if there is anything to repeat
1051
+        if (count($this->repeats) > 0) {
1052
+            // No need to load them if not used...
1053
+
1054
+            $lineoffset = 0;
1055
+            foreach ($this->repeats_stack as $rep) {
1056
+                $lineoffset += $rep[1];
1057
+            }
1058
+            //-- read the xml from the file
1059
+            $lines = file($report);
1060
+            while (strpos($lines[$lineoffset + $this->repeat_bytes], "<RepeatTag") === false) {
1061
+                $lineoffset--;
1062
+            }
1063
+            $lineoffset++;
1064
+            $reportxml = "<tempdoc>\n";
1065
+            $line_nr   = $lineoffset + $this->repeat_bytes;
1066
+            // RepeatTag Level counter
1067
+            $count = 1;
1068
+            while (0 < $count) {
1069
+                if (strstr($lines[$line_nr], "<RepeatTag") !== false) {
1070
+                    $count++;
1071
+                } elseif (strstr($lines[$line_nr], "</RepeatTag") !== false) {
1072
+                    $count--;
1073
+                }
1074
+                if (0 < $count) {
1075
+                    $reportxml .= $lines[$line_nr];
1076
+                }
1077
+                $line_nr++;
1078
+            }
1079
+            // No need to drag this
1080
+            unset($lines);
1081
+            $reportxml .= "</tempdoc>\n";
1082
+            // Save original values
1083
+            array_push($this->parser_stack, $this->parser);
1084
+            $oldgedrec = $this->gedrec;
1085
+            foreach ($this->repeats as $gedrec) {
1086
+                $this->gedrec  = $gedrec;
1087
+                $repeat_parser = xml_parser_create();
1088
+                $this->parser  = $repeat_parser;
1089
+                xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
1090
+                xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
1091
+                xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
1092
+                if (!xml_parse($repeat_parser, $reportxml, true)) {
1093
+                    throw new \DomainException(sprintf(
1094
+                        'RepeatTagEHandler XML error: %s at line %d',
1095
+                        xml_error_string(xml_get_error_code($repeat_parser)),
1096
+                        xml_get_current_line_number($repeat_parser)
1097
+                    ));
1098
+                }
1099
+                xml_parser_free($repeat_parser);
1100
+            }
1101
+            // Restore original values
1102
+            $this->gedrec = $oldgedrec;
1103
+            $this->parser = array_pop($this->parser_stack);
1104
+        }
1105
+        list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
1106
+    }
1107
+
1108
+    /**
1109
+     * Variable lookup
1110
+     *
1111
+     * Retrieve predefined variables :
1112
+     *
1113
+     * @ desc GEDCOM fact description, example:
1114
+     *        1 EVEN This is a description
1115
+     * @ fact GEDCOM fact tag, such as BIRT, DEAT etc.
1116
+     * $ I18N::translate('....')
1117
+     * $ language_settings[]
1118
+     *
1119
+     * @param array $attrs an array of key value pairs for the attributes
1120
+     */
1121
+    private function varStartHandler($attrs) {
1122
+        if (empty($attrs['var'])) {
1123
+            throw new \DomainException('REPORT ERROR var: The attribute "var=" is missing or not set in the XML file on line: ' . xml_get_current_line_number($this->parser));
1124
+        }
1125
+
1126
+        $var = $attrs['var'];
1127
+        // SetVar element preset variables
1128
+        if (!empty($this->vars[$var]['id'])) {
1129
+            $var = $this->vars[$var]['id'];
1130
+        } else {
1131
+            $tfact = $this->fact;
1132
+            if (($this->fact === "EVEN" || $this->fact === "FACT") && $this->type !== " ") {
1133
+                // Use :
1134
+                // n TYPE This text if string
1135
+                $tfact = $this->type;
1136
+            }
1137
+            $var = str_replace(array("@fact", "@desc"), array(GedcomTag::getLabel($tfact), $this->desc), $var);
1138
+            if (preg_match('/^I18N::number\((.+)\)$/', $var, $match)) {
1139
+                $var = I18N::number($match[1]);
1140
+            } elseif (preg_match('/^I18N::translate\(\'(.+)\'\)$/', $var, $match)) {
1141
+                $var = I18N::translate($match[1]);
1142
+            } elseif (preg_match('/^I18N::translateContext\(\'(.+)\', *\'(.+)\'\)$/', $var, $match)) {
1143
+                $var = I18N::translateContext($match[1], $match[2]);
1144
+            }
1145
+        }
1146
+        // Check if variable is set as a date and reformat the date
1147
+        if (isset($attrs['date'])) {
1148
+            if ($attrs['date'] === "1") {
1149
+                $g   = new Date($var);
1150
+                $var = $g->display();
1151
+            }
1152
+        }
1153
+        $this->current_element->addText($var);
1154
+        $this->text = $var; // Used for title/descriptio
1155
+    }
1156
+
1157
+    /**
1158
+     * XML <Facts>
1159
+     *
1160
+     * @param array $attrs an array of key value pairs for the attributes
1161
+     */
1162
+    private function factsStartHandler($attrs) {
1163
+        global $WT_TREE;
1164
+
1165
+        $this->process_repeats++;
1166
+        if ($this->process_repeats > 1) {
1167
+            return;
1168
+        }
1169
+
1170
+        array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
1171
+        $this->repeats      = array();
1172
+        $this->repeat_bytes = xml_get_current_line_number($this->parser);
1173
+
1174
+        $id    = "";
1175
+        $match = array();
1176
+        if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1177
+            $id = $match[1];
1178
+        }
1179
+        $tag = "";
1180
+        if (isset($attrs['ignore'])) {
1181
+            $tag .= $attrs['ignore'];
1182
+        }
1183
+        if (preg_match("/\\$(.+)/", $tag, $match)) {
1184
+            $tag = $this->vars[$match[1]]['id'];
1185
+        }
1186
+
1187
+        $record = GedcomRecord::getInstance($id, $WT_TREE);
1188
+        if (empty($attrs['diff']) && !empty($id)) {
1189
+            $facts = $record->getFacts();
1190
+            Functions::sortFacts($facts);
1191
+            $this->repeats  = array();
1192
+            $nonfacts       = explode(',', $tag);
1193
+            foreach ($facts as $event) {
1194
+                if (!in_array($event->getTag(), $nonfacts)) {
1195
+                    $this->repeats[] = $event->getGedcom();
1196
+                }
1197
+            }
1198
+        } else {
1199
+            foreach ($record->getFacts() as $fact) {
1200
+                if ($fact->isPendingAddition() && $fact->getTag() !== 'CHAN') {
1201
+                    $this->repeats[] = $fact->getGedcom();
1202
+                }
1203
+            }
1204
+        }
1205
+    }
1206
+
1207
+    /**
1208
+     * XML </Facts>
1209
+     */
1210
+    private function factsEndHandler() {
1211
+        global $report;
1212
+
1213
+        $this->process_repeats--;
1214
+        if ($this->process_repeats > 0) {
1215
+            return;
1216
+        }
1217
+
1218
+        // Check if there is anything to repeat
1219
+        if (count($this->repeats) > 0) {
1220
+
1221
+            $line       = xml_get_current_line_number($this->parser) - 1;
1222
+            $lineoffset = 0;
1223
+            foreach ($this->repeats_stack as $rep) {
1224
+                $lineoffset += $rep[1];
1225
+            }
1226
+
1227
+            //-- read the xml from the file
1228
+            $lines = file($report);
1229
+            while ($lineoffset + $this->repeat_bytes > 0 && strpos($lines[$lineoffset + $this->repeat_bytes], '<Facts ') === false) {
1230
+                $lineoffset--;
1231
+            }
1232
+            $lineoffset++;
1233
+            $reportxml = "<tempdoc>\n";
1234
+            $i         = $line + $lineoffset;
1235
+            $line_nr   = $this->repeat_bytes + $lineoffset;
1236
+            while ($line_nr < $i) {
1237
+                $reportxml .= $lines[$line_nr];
1238
+                $line_nr++;
1239
+            }
1240
+            // No need to drag this
1241
+            unset($lines);
1242
+            $reportxml .= "</tempdoc>\n";
1243
+            // Save original values
1244
+            array_push($this->parser_stack, $this->parser);
1245
+            $oldgedrec = $this->gedrec;
1246
+            $count     = count($this->repeats);
1247
+            $i         = 0;
1248
+            while ($i < $count) {
1249
+                $this->gedrec = $this->repeats[$i];
1250
+                $this->fact   = '';
1251
+                $this->desc   = '';
1252
+                if (preg_match('/1 (\w+)(.*)/', $this->gedrec, $match)) {
1253
+                    $this->fact = $match[1];
1254
+                    if ($this->fact === 'EVEN' || $this->fact === 'FACT') {
1255
+                        $tmatch = array();
1256
+                        if (preg_match('/2 TYPE (.+)/', $this->gedrec, $tmatch)) {
1257
+                            $this->type = trim($tmatch[1]);
1258
+                        } else {
1259
+                            $this->type = ' ';
1260
+                        }
1261
+                    }
1262
+                    $this->desc = trim($match[2]);
1263
+                    $this->desc .= Functions::getCont(2, $this->gedrec);
1264
+                }
1265
+                $repeat_parser = xml_parser_create();
1266
+                $this->parser  = $repeat_parser;
1267
+                xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
1268
+                xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
1269
+                xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
1270
+                if (!xml_parse($repeat_parser, $reportxml, true)) {
1271
+                    throw new \DomainException(sprintf(
1272
+                        'FactsEHandler XML error: %s at line %d',
1273
+                        xml_error_string(xml_get_error_code($repeat_parser)),
1274
+                        xml_get_current_line_number($repeat_parser)
1275
+                    ));
1276
+                }
1277
+                xml_parser_free($repeat_parser);
1278
+                $i++;
1279
+            }
1280
+            // Restore original values
1281
+            $this->parser = array_pop($this->parser_stack);
1282
+            $this->gedrec = $oldgedrec;
1283
+        }
1284
+        list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
1285
+    }
1286
+
1287
+    /**
1288
+     * Setting upp or changing variables in the XML
1289
+     * The XML variable name and value is stored in $this->vars
1290
+     *
1291
+     * @param array $attrs an array of key value pairs for the attributes
1292
+     */
1293
+    private function setVarStartHandler($attrs) {
1294
+        if (empty($attrs['name'])) {
1295
+            throw new \DomainException('REPORT ERROR var: The attribute "name" is missing or not set in the XML file');
1296
+        }
1297
+
1298
+        $name  = $attrs['name'];
1299
+        $value = $attrs['value'];
1300
+        $match = array();
1301
+        // Current GEDCOM record strings
1302
+        if ($value == "@ID") {
1303
+            if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1304
+                $value = $match[1];
1305
+            }
1306
+        } elseif ($value == "@fact") {
1307
+            $value = $this->fact;
1308
+        } elseif ($value == "@desc") {
1309
+            $value = $this->desc;
1310
+        } elseif ($value == "@generation") {
1311
+            $value = $this->generation;
1312
+        } elseif (preg_match("/@(\w+)/", $value, $match)) {
1313
+            $gmatch = array();
1314
+            if (preg_match("/\d $match[1] (.+)/", $this->gedrec, $gmatch)) {
1315
+                $value = str_replace("@", "", trim($gmatch[1]));
1316
+            }
1317
+        }
1318
+        if (preg_match("/\\$(\w+)/", $name, $match)) {
1319
+            $name = $this->vars["'" . $match[1] . "'"]['id'];
1320
+        }
1321
+        $count = preg_match_all("/\\$(\w+)/", $value, $match, PREG_SET_ORDER);
1322
+        $i     = 0;
1323
+        while ($i < $count) {
1324
+            $t     = $this->vars[$match[$i][1]]['id'];
1325
+            $value = preg_replace("/\\$" . $match[$i][1] . "/", $t, $value, 1);
1326
+            $i++;
1327
+        }
1328
+        if (preg_match('/^I18N::number\((.+)\)$/', $value, $match)) {
1329
+            $value = I18N::number($match[1]);
1330
+        } elseif (preg_match('/^I18N::translate\(\'(.+)\'\)$/', $value, $match)) {
1331
+            $value = I18N::translate($match[1]);
1332
+        } elseif (preg_match('/^I18N::translateContext\(\'(.+)\', *\'(.+)\'\)$/', $value, $match)) {
1333
+            $value = I18N::translateContext($match[1], $match[2]);
1334
+        }
1335
+        // Arithmetic functions
1336
+        if (preg_match("/(\d+)\s*([\-\+\*\/])\s*(\d+)/", $value, $match)) {
1337
+            switch ($match[2]) {
1338
+            case "+":
1339
+                $t     = $match[1] + $match[3];
1340
+                $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1341
+                break;
1342
+            case "-":
1343
+                $t     = $match[1] - $match[3];
1344
+                $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1345
+                break;
1346
+            case "*":
1347
+                $t     = $match[1] * $match[3];
1348
+                $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1349
+                break;
1350
+            case "/":
1351
+                $t     = $match[1] / $match[3];
1352
+                $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1353
+                break;
1354
+            }
1355
+        }
1356
+        if (strpos($value, "@") !== false) {
1357
+            $value = "";
1358
+        }
1359
+        $this->vars[$name]['id'] = $value;
1360
+    }
1361
+
1362
+    /**
1363
+     * XML <if > start element
1364
+     *
1365
+     * @param array $attrs an array of key value pairs for the attributes
1366
+     */
1367
+    private function ifStartHandler($attrs) {
1368
+        if ($this->process_ifs > 0) {
1369
+            $this->process_ifs++;
1370
+
1371
+            return;
1372
+        }
1373
+
1374
+        $condition = $attrs['condition'];
1375
+        $condition = $this->substituteVars($condition, true);
1376
+        $condition = str_replace(array(" LT ", " GT "), array("<", ">"), $condition);
1377
+        // Replace the first accurance only once of @fact:DATE or in any other combinations to the current fact, such as BIRT
1378
+        $condition = str_replace("@fact:", $this->fact . ':', $condition);
1379
+        $match     = array();
1380
+        $count     = preg_match_all("/@([\w:\.]+)/", $condition, $match, PREG_SET_ORDER);
1381
+        $i         = 0;
1382
+        while ($i < $count) {
1383
+            $id    = $match[$i][1];
1384
+            $value = '""';
1385
+            if ($id == "ID") {
1386
+                if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1387
+                    $value = "'" . $match[1] . "'";
1388
+                }
1389
+            } elseif ($id === "fact") {
1390
+                $value = '"' . $this->fact . '"';
1391
+            } elseif ($id === "desc") {
1392
+                $value = '"' . addslashes($this->desc) . '"';
1393
+            } elseif ($id === "generation") {
1394
+                $value = '"' . $this->generation . '"';
1395
+            } else {
1396
+
1397
+                $temp  = explode(" ", trim($this->gedrec));
1398
+                $level = $temp[0];
1399
+                if ($level == 0) {
1400
+                    $level++;
1401
+                }
1402
+                $value = $this->getGedcomValue($id, $level, $this->gedrec);
1403
+                if (empty($value)) {
1404
+                    $level++;
1405
+                    $value = $this->getGedcomValue($id, $level, $this->gedrec);
1406
+                }
1407
+                $value = preg_replace('/^@(' . WT_REGEX_XREF . ')@$/', '$1', $value);
1408
+                $value = '"' . addslashes($value) . '"';
1409
+            }
1410
+            $condition = str_replace("@$id", $value, $condition);
1411
+            $i++;
1412
+        }
1413
+        $ret = eval("return (bool) ($condition);");
1414
+        if (!$ret) {
1415
+            $this->process_ifs++;
1416
+        }
1417
+    }
1418
+
1419
+    /**
1420
+     * XML <if /> end element
1421
+     */
1422
+    private function ifEndHandler() {
1423
+        if ($this->process_ifs > 0) {
1424
+            $this->process_ifs--;
1425
+        }
1426
+    }
1427
+
1428
+    /**
1429
+     * XML <Footnote > start element
1430
+     * Collect the Footnote links
1431
+     * GEDCOM Records that are protected by Privacy setting will be ignore
1432
+     *
1433
+     * @param array $attrs an array of key value pairs for the attributes
1434
+     */
1435
+    private function footnoteStartHandler($attrs) {
1436
+        global $WT_TREE;
1437
+
1438
+        $id = "";
1439
+        if (preg_match("/[0-9] (.+) @(.+)@/", $this->gedrec, $match)) {
1440
+            $id = $match[2];
1441
+        }
1442
+        $record = GedcomRecord::getInstance($id, $WT_TREE);
1443
+        if ($record && $record->canShow()) {
1444
+            array_push($this->print_data_stack, $this->print_data);
1445
+            $this->print_data = true;
1446
+            $style            = "";
1447
+            if (!empty($attrs['style'])) {
1448
+                $style = $attrs['style'];
1449
+            }
1450
+            $this->footnote_element = $this->current_element;
1451
+            $this->current_element  = $this->report_root->createFootnote($style);
1452
+        } else {
1453
+            $this->print_data       = false;
1454
+            $this->process_footnote = false;
1455
+        }
1456
+    }
1457
+
1458
+    /**
1459
+     * XML <Footnote /> end element
1460
+     * Print the collected Footnote data
1461
+     */
1462
+    private function footnoteEndHandler() {
1463
+        if ($this->process_footnote) {
1464
+            $this->print_data = array_pop($this->print_data_stack);
1465
+            $temp             = trim($this->current_element->getValue());
1466
+            if (strlen($temp) > 3) {
1467
+                $this->wt_report->addElement($this->current_element);
1468
+            }
1469
+            $this->current_element = $this->footnote_element;
1470
+        } else {
1471
+            $this->process_footnote = true;
1472
+        }
1473
+    }
1474
+
1475
+    /**
1476
+     * XML <FootnoteTexts /> element
1477
+     */
1478
+    private function footnoteTextsStartHandler() {
1479
+        $temp = "footnotetexts";
1480
+        $this->wt_report->addElement($temp);
1481
+    }
1482
+
1483
+    /**
1484
+     * XML <AgeAtDeath /> element handler
1485
+     */
1486
+    private function ageAtDeathStartHandler() {
1487
+        // This duplicates functionality in FunctionsPrint::format_fact_date()
1488
+        global $factrec, $WT_TREE;
1489
+
1490
+        $match = array();
1491
+        if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1492
+            $person = Individual::getInstance($match[1], $WT_TREE);
1493
+            // Recorded age
1494
+            if (preg_match('/\n2 AGE (.+)/', $factrec, $match)) {
1495
+                $fact_age = $match[1];
1496
+            } else {
1497
+                $fact_age = '';
1498
+            }
1499
+            if (preg_match('/\n2 HUSB\n3 AGE (.+)/', $factrec, $match)) {
1500
+                $husb_age = $match[1];
1501
+            } else {
1502
+                $husb_age = '';
1503
+            }
1504
+            if (preg_match('/\n2 WIFE\n3 AGE (.+)/', $factrec, $match)) {
1505
+                $wife_age = $match[1];
1506
+            } else {
1507
+                $wife_age = '';
1508
+            }
1509
+
1510
+            // Calculated age
1511
+            $birth_date = $person->getBirthDate();
1512
+            // Can't use getDeathDate(), as this also gives BURI/CREM events, which
1513
+            // wouldn't give the correct "days after death" result for people with
1514
+            // no DEAT.
1515
+            $death_event = $person->getFirstFact('DEAT');
1516
+            if ($death_event) {
1517
+                $death_date = $death_event->getDate();
1518
+            } else {
1519
+                $death_date = new Date('');
1520
+            }
1521
+            $value = '';
1522
+            if (Date::compare($birth_date, $death_date) <= 0 || !$person->isDead()) {
1523
+                $age = Date::getAgeGedcom($birth_date, $death_date);
1524
+                // Only show calculated age if it differs from recorded age
1525
+                if ($age != '' && $age != "0d") {
1526
+                    if ($fact_age != '' && $fact_age != $age || $fact_age == '' && $husb_age == '' && $wife_age == '' || $husb_age != '' && $person->getSex() == 'M' && $husb_age != $age || $wife_age != '' && $person->getSex() == 'F' && $wife_age != $age
1527
+                    ) {
1528
+                        $value  = FunctionsDate::getAgeAtEvent($age);
1529
+                        $abbrev = substr($value, 0, strpos($value, ' ') + 5);
1530
+                        if ($value !== $abbrev) {
1531
+                            $value = $abbrev . '.';
1532
+                        }
1533
+                    }
1534
+                }
1535
+            }
1536
+            $this->current_element->addText($value);
1537
+        }
1538
+    }
1539
+
1540
+    /**
1541
+     * XML element Forced line break handler - HTML code
1542
+     */
1543
+    private function brStartHandler() {
1544
+        if ($this->print_data && $this->process_gedcoms === 0) {
1545
+            $this->current_element->addText('<br>');
1546
+        }
1547
+    }
1548
+
1549
+    /**
1550
+     * XML <sp />element Forced space handler
1551
+     */
1552
+    private function spStartHandler() {
1553
+        if ($this->print_data && $this->process_gedcoms === 0) {
1554
+            $this->current_element->addText(' ');
1555
+        }
1556
+    }
1557
+
1558
+    /**
1559
+     * XML <HighlightedImage/>
1560
+     *
1561
+     * @param array $attrs an array of key value pairs for the attributes
1562
+     */
1563
+    private function highlightedImageStartHandler($attrs) {
1564
+        global $WT_TREE;
1565
+
1566
+        $id    = '';
1567
+        $match = array();
1568
+        if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
1569
+            $id = $match[1];
1570
+        }
1571
+
1572
+        // mixed Position the top corner of this box on the page. the default is the current position
1573
+        $top = '.';
1574
+        if (isset($attrs['top'])) {
1575
+            if ($attrs['top'] === '0') {
1576
+                $top = 0;
1577
+            } elseif ($attrs['top'] === '.') {
1578
+                $top = '.';
1579
+            } elseif (!empty($attrs['top'])) {
1580
+                $top = (int) $attrs['top'];
1581
+            }
1582
+        }
1583
+
1584
+        // mixed Position the left corner of this box on the page. the default is the current position
1585
+        $left = '.';
1586
+        if (isset($attrs['left'])) {
1587
+            if ($attrs['left'] === '0') {
1588
+                $left = 0;
1589
+            } elseif ($attrs['left'] === '.') {
1590
+                $left = '.';
1591
+            } elseif (!empty($attrs['left'])) {
1592
+                $left = (int) $attrs['left'];
1593
+            }
1594
+        }
1595
+
1596
+        // string Align the image in left, center, right
1597
+        $align = '';
1598
+        if (!empty($attrs['align'])) {
1599
+            $align = $attrs['align'];
1600
+        }
1601
+
1602
+        // string Next Line should be T:next to the image, N:next line
1603
+        $ln = '';
1604
+        if (!empty($attrs['ln'])) {
1605
+            $ln = $attrs['ln'];
1606
+        }
1607
+
1608
+        $width  = 0;
1609
+        $height = 0;
1610
+        if (!empty($attrs['width'])) {
1611
+            $width = (int) $attrs['width'];
1612
+        }
1613
+        if (!empty($attrs['height'])) {
1614
+            $height = (int) $attrs['height'];
1615
+        }
1616
+
1617
+        $person      = Individual::getInstance($id, $WT_TREE);
1618
+        $mediaobject = $person->findHighlightedMedia();
1619
+        if ($mediaobject) {
1620
+            $attributes = $mediaobject->getImageAttributes('thumb');
1621
+            if (in_array(
1622
+                    $attributes['ext'],
1623
+                    array(
1624
+                        'GIF',
1625
+                        'JPG',
1626
+                        'PNG',
1627
+                        'SWF',
1628
+                        'PSD',
1629
+                        'BMP',
1630
+                        'TIFF',
1631
+                        'TIFF',
1632
+                        'JPC',
1633
+                        'JP2',
1634
+                        'JPX',
1635
+                        'JB2',
1636
+                        'SWC',
1637
+                        'IFF',
1638
+                        'WBMP',
1639
+                        'XBM',
1640
+                    )
1641
+                ) && $mediaobject->canShow() && $mediaobject->fileExists('thumb')
1642
+            ) {
1643
+                if ($width > 0 && $height == 0) {
1644
+                    $perc   = $width / $attributes['adjW'];
1645
+                    $height = round($attributes['adjH'] * $perc);
1646
+                } elseif ($height > 0 && $width == 0) {
1647
+                    $perc  = $height / $attributes['adjH'];
1648
+                    $width = round($attributes['adjW'] * $perc);
1649
+                } else {
1650
+                    $width  = $attributes['adjW'];
1651
+                    $height = $attributes['adjH'];
1652
+                }
1653
+                $image = $this->report_root->createImageFromObject($mediaobject, $left, $top, $width, $height, $align, $ln);
1654
+                $this->wt_report->addElement($image);
1655
+            }
1656
+        }
1657
+    }
1658
+
1659
+    /**
1660
+     * XML <Image/>
1661
+     *
1662
+     * @param array $attrs an array of key value pairs for the attributes
1663
+     */
1664
+    private function imageStartHandler($attrs) {
1665
+        global $WT_TREE;
1666
+
1667
+        // mixed Position the top corner of this box on the page. the default is the current position
1668
+        $top = '.';
1669
+        if (isset($attrs['top'])) {
1670
+            if ($attrs['top'] === "0") {
1671
+                $top = 0;
1672
+            } elseif ($attrs['top'] === '.') {
1673
+                $top = '.';
1674
+            } elseif (!empty($attrs['top'])) {
1675
+                $top = (int) $attrs['top'];
1676
+            }
1677
+        }
1678
+
1679
+        // mixed Position the left corner of this box on the page. the default is the current position
1680
+        $left = '.';
1681
+        if (isset($attrs['left'])) {
1682
+            if ($attrs['left'] === '0') {
1683
+                $left = 0;
1684
+            } elseif ($attrs['left'] === '.') {
1685
+                $left = '.';
1686
+            } elseif (!empty($attrs['left'])) {
1687
+                $left = (int) $attrs['left'];
1688
+            }
1689
+        }
1690
+
1691
+        // string Align the image in left, center, right
1692
+        $align = '';
1693
+        if (!empty($attrs['align'])) {
1694
+            $align = $attrs['align'];
1695
+        }
1696
+
1697
+        // string Next Line should be T:next to the image, N:next line
1698
+        $ln = 'T';
1699
+        if (!empty($attrs['ln'])) {
1700
+            $ln = $attrs['ln'];
1701
+        }
1702
+
1703
+        $width  = 0;
1704
+        $height = 0;
1705
+        if (!empty($attrs['width'])) {
1706
+            $width = (int) $attrs['width'];
1707
+        }
1708
+        if (!empty($attrs['height'])) {
1709
+            $height = (int) $attrs['height'];
1710
+        }
1711
+
1712
+        $file = '';
1713
+        if (!empty($attrs['file'])) {
1714
+            $file = $attrs['file'];
1715
+        }
1716
+        if ($file == "@FILE") {
1717
+            $match = array();
1718
+            if (preg_match("/\d OBJE @(.+)@/", $this->gedrec, $match)) {
1719
+                $mediaobject = Media::getInstance($match[1], $WT_TREE);
1720
+                $attributes  = $mediaobject->getImageAttributes('thumb');
1721
+                if (in_array(
1722
+                        $attributes['ext'],
1723
+                        array(
1724
+                            'GIF',
1725
+                            'JPG',
1726
+                            'PNG',
1727
+                            'SWF',
1728
+                            'PSD',
1729
+                            'BMP',
1730
+                            'TIFF',
1731
+                            'TIFF',
1732
+                            'JPC',
1733
+                            'JP2',
1734
+                            'JPX',
1735
+                            'JB2',
1736
+                            'SWC',
1737
+                            'IFF',
1738
+                            'WBMP',
1739
+                            'XBM',
1740
+                        )
1741
+                    ) && $mediaobject->canShow() && $mediaobject->fileExists('thumb')
1742
+                ) {
1743
+                    if ($width > 0 && $height == 0) {
1744
+                        $perc   = $width / $attributes['adjW'];
1745
+                        $height = round($attributes['adjH'] * $perc);
1746
+                    } elseif ($height > 0 && $width == 0) {
1747
+                        $perc  = $height / $attributes['adjH'];
1748
+                        $width = round($attributes['adjW'] * $perc);
1749
+                    } else {
1750
+                        $width  = $attributes['adjW'];
1751
+                        $height = $attributes['adjH'];
1752
+                    }
1753
+                    $image = $this->report_root->createImageFromObject($mediaobject, $left, $top, $width, $height, $align, $ln);
1754
+                    $this->wt_report->addElement($image);
1755
+                }
1756
+            }
1757
+        } else {
1758
+            if (file_exists($file) && preg_match("/(jpg|jpeg|png|gif)$/i", $file)) {
1759
+                $size = getimagesize($file);
1760
+                if ($width > 0 && $height == 0) {
1761
+                    $perc   = $width / $size[0];
1762
+                    $height = round($size[1] * $perc);
1763
+                } elseif ($height > 0 && $width == 0) {
1764
+                    $perc  = $height / $size[1];
1765
+                    $width = round($size[0] * $perc);
1766
+                } else {
1767
+                    $width  = $size[0];
1768
+                    $height = $size[1];
1769
+                }
1770
+                $image = $this->report_root->createImage($file, $left, $top, $width, $height, $align, $ln);
1771
+                $this->wt_report->addElement($image);
1772
+            }
1773
+        }
1774
+    }
1775
+
1776
+    /**
1777
+     * XML <Line> element handler
1778
+     *
1779
+     * @param array $attrs an array of key value pairs for the attributes
1780
+     */
1781
+    private function lineStartHandler($attrs) {
1782
+        // Start horizontal position, current position (default)
1783
+        $x1 = ".";
1784
+        if (isset($attrs['x1'])) {
1785
+            if ($attrs['x1'] === "0") {
1786
+                $x1 = 0;
1787
+            } elseif ($attrs['x1'] === ".") {
1788
+                $x1 = ".";
1789
+            } elseif (!empty($attrs['x1'])) {
1790
+                $x1 = (int) $attrs['x1'];
1791
+            }
1792
+        }
1793
+        // Start vertical position, current position (default)
1794
+        $y1 = ".";
1795
+        if (isset($attrs['y1'])) {
1796
+            if ($attrs['y1'] === "0") {
1797
+                $y1 = 0;
1798
+            } elseif ($attrs['y1'] === ".") {
1799
+                $y1 = ".";
1800
+            } elseif (!empty($attrs['y1'])) {
1801
+                $y1 = (int) $attrs['y1'];
1802
+            }
1803
+        }
1804
+        // End horizontal position, maximum width (default)
1805
+        $x2 = ".";
1806
+        if (isset($attrs['x2'])) {
1807
+            if ($attrs['x2'] === "0") {
1808
+                $x2 = 0;
1809
+            } elseif ($attrs['x2'] === ".") {
1810
+                $x2 = ".";
1811
+            } elseif (!empty($attrs['x2'])) {
1812
+                $x2 = (int) $attrs['x2'];
1813
+            }
1814
+        }
1815
+        // End vertical position
1816
+        $y2 = ".";
1817
+        if (isset($attrs['y2'])) {
1818
+            if ($attrs['y2'] === "0") {
1819
+                $y2 = 0;
1820
+            } elseif ($attrs['y2'] === ".") {
1821
+                $y2 = ".";
1822
+            } elseif (!empty($attrs['y2'])) {
1823
+                $y2 = (int) $attrs['y2'];
1824
+            }
1825
+        }
1826
+
1827
+        $line = $this->report_root->createLine($x1, $y1, $x2, $y2);
1828
+        $this->wt_report->addElement($line);
1829
+    }
1830
+
1831
+    /**
1832
+     * XML <List>
1833
+     *
1834
+     * @param array $attrs an array of key value pairs for the attributes
1835
+     */
1836
+    private function listStartHandler($attrs) {
1837
+        global $WT_TREE;
1838
+
1839
+        $this->process_repeats++;
1840
+        if ($this->process_repeats > 1) {
1841
+            return;
1842
+        }
1843
+
1844
+        $match = array();
1845
+        if (isset($attrs['sortby'])) {
1846
+            $sortby = $attrs['sortby'];
1847
+            if (preg_match("/\\$(\w+)/", $sortby, $match)) {
1848
+                $sortby = $this->vars[$match[1]]['id'];
1849
+                $sortby = trim($sortby);
1850
+            }
1851
+        } else {
1852
+            $sortby = "NAME";
1853
+        }
1854
+
1855
+        if (isset($attrs['list'])) {
1856
+            $listname = $attrs['list'];
1857
+        } else {
1858
+            $listname = "individual";
1859
+        }
1860
+        // Some filters/sorts can be applied using SQL, while others require PHP
1861
+        switch ($listname) {
1862
+        case "pending":
1863
+            $rows = Database::prepare(
1864
+                "SELECT xref, CASE new_gedcom WHEN '' THEN old_gedcom ELSE new_gedcom END AS gedcom" .
1865
+                " FROM `##change`" . " WHERE (xref, change_id) IN (" .
1866
+                "  SELECT xref, MAX(change_id)" .
1867
+                "  FROM `##change`" .
1868
+                "  WHERE status = 'pending' AND gedcom_id = :tree_id" .
1869
+                "  GROUP BY xref" .
1870
+                " )"
1871
+            )->execute(array(
1872
+                'tree_id' => $WT_TREE->getTreeId(),
1873
+            ))->fetchAll();
1874
+            $this->list = array();
1875
+            foreach ($rows as $row) {
1876
+                $this->list[] = GedcomRecord::getInstance($row->xref, $WT_TREE, $row->gedcom);
1877
+            }
1878
+            break;
1879
+        case 'individual':
1880
+            $sql_select   = "SELECT i_id AS xref, i_gedcom AS gedcom FROM `##individuals` ";
1881
+            $sql_join     = "";
1882
+            $sql_where    = " WHERE i_file = :tree_id";
1883
+            $sql_order_by = "";
1884
+            $sql_params   = array('tree_id' => $WT_TREE->getTreeId());
1885
+            foreach ($attrs as $attr => $value) {
1886
+                if (strpos($attr, 'filter') === 0 && $value) {
1887
+                    $value = $this->substituteVars($value, false);
1888
+                    // Convert the various filters into SQL
1889
+                    if (preg_match('/^(\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
1890
+                        $sql_join .= " JOIN `##dates` AS {$attr} ON ({$attr}.d_file=i_file AND {$attr}.d_gid=i_id)";
1891
+                        $sql_where .= " AND {$attr}.d_fact = :{$attr}fact";
1892
+                        $sql_params[$attr . 'fact'] = $match[1];
1893
+                        $date                       = new Date($match[3]);
1894
+                        if ($match[2] == "LTE") {
1895
+                            $sql_where .= " AND {$attr}.d_julianday2 <= :{$attr}date";
1896
+                            $sql_params[$attr . 'date'] = $date->maximumJulianDay();
1897
+                        } else {
1898
+                            $sql_where .= " AND {$attr}.d_julianday1 >= :{$attr}date";
1899
+                            $sql_params[$attr . 'date'] = $date->minimumJulianDay();
1900
+                        }
1901
+                        if ($sortby == $match[1]) {
1902
+                            $sortby = "";
1903
+                            $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
1904
+                        }
1905
+                        unset($attrs[$attr]); // This filter has been fully processed
1906
+                    } elseif (preg_match('/^NAME CONTAINS (.*)$/', $value, $match)) {
1907
+                        // Do nothing, unless you have to
1908
+                        if ($match[1] != '' || $sortby == 'NAME') {
1909
+                            $sql_join .= " JOIN `##name` AS {$attr} ON (n_file=i_file AND n_id=i_id)";
1910
+                            // Search the DB only if there is any name supplied
1911
+                            if ($match[1] != "") {
1912
+                                $names = explode(" ", $match[1]);
1913
+                                foreach ($names as $n => $name) {
1914
+                                    $sql_where .= " AND {$attr}.n_full LIKE CONCAT('%', :{$attr}name{$n}, '%')";
1915
+                                    $sql_params[$attr . 'name' . $n] = $name;
1916
+                                }
1917
+                            }
1918
+                            // Let the DB do the name sorting even when no name was entered
1919
+                            if ($sortby == "NAME") {
1920
+                                $sortby = "";
1921
+                                $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.n_sort";
1922
+                            }
1923
+                        }
1924
+                        unset($attrs[$attr]); // This filter has been fully processed
1925
+                    } elseif (preg_match('/^REGEXP \/(.+)\//', $value, $match)) {
1926
+                        $sql_where .= " AND i_gedcom REGEXP :{$attr}gedcom";
1927
+                        // PDO helpfully escapes backslashes for us, preventing us from matching "\n1 FACT"
1928
+                        $sql_params[$attr . 'gedcom'] = str_replace('\n', "\n", $match[1]);
1929
+                        unset($attrs[$attr]); // This filter has been fully processed
1930
+                    } elseif (preg_match('/^(?:\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
1931
+                        $sql_join .= " JOIN `##places` AS {$attr}a ON ({$attr}a.p_file = i_file)";
1932
+                        $sql_join .= " JOIN `##placelinks` AS {$attr}b ON ({$attr}a.p_file = {$attr}b.pl_file AND {$attr}b.pl_p_id = {$attr}a.p_id AND {$attr}b.pl_gid = i_id)";
1933
+                        $sql_where .= " AND {$attr}a.p_place LIKE CONCAT('%', :{$attr}place, '%')";
1934
+                        $sql_params[$attr . 'place'] = $match[1];
1935
+                        // Don't unset this filter. This is just initial filtering
1936
+                    } elseif (preg_match('/^(\w*):*(\w*) CONTAINS (.+)$/', $value, $match)) {
1937
+                        $sql_where .= " AND i_gedcom LIKE CONCAT('%', :{$attr}contains1, '%', :{$attr}contains2, '%', :{$attr}contains3, '%')";
1938
+                        $sql_params[$attr . 'contains1'] = $match[1];
1939
+                        $sql_params[$attr . 'contains2'] = $match[2];
1940
+                        $sql_params[$attr . 'contains3'] = $match[3];
1941
+                        // Don't unset this filter. This is just initial filtering
1942
+                    }
1943
+                }
1944
+            }
1945
+
1946
+            $this->list = array();
1947
+            $rows       = Database::prepare(
1948
+                $sql_select . $sql_join . $sql_where . $sql_order_by
1949
+            )->execute($sql_params)->fetchAll();
1950
+
1951
+            foreach ($rows as $row) {
1952
+                $this->list[$row->xref] = Individual::getInstance($row->xref, $WT_TREE, $row->gedcom);
1953
+            }
1954
+            break;
1955
+
1956
+        case 'family':
1957
+            $sql_select   = "SELECT f_id AS xref, f_gedcom AS gedcom FROM `##families`";
1958
+            $sql_join     = "";
1959
+            $sql_where    = " WHERE f_file = :tree_id";
1960
+            $sql_order_by = "";
1961
+            $sql_params   = array('tree_id' => $WT_TREE->getTreeId());
1962
+            foreach ($attrs as $attr => $value) {
1963
+                if (strpos($attr, 'filter') === 0 && $value) {
1964
+                    $value = $this->substituteVars($value, false);
1965
+                    // Convert the various filters into SQL
1966
+                    if (preg_match('/^(\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
1967
+                        $sql_join .= " JOIN `##dates` AS {$attr} ON ({$attr}.d_file=f_file AND {$attr}.d_gid=f_id)";
1968
+                        $sql_where .= " AND {$attr}.d_fact = :{$attr}fact";
1969
+                        $sql_params[$attr . 'fact'] = $match[1];
1970
+                        $date                       = new Date($match[3]);
1971
+                        if ($match[2] == "LTE") {
1972
+                            $sql_where .= " AND {$attr}.d_julianday2 <= :{$attr}date";
1973
+                            $sql_params[$attr . 'date'] = $date->maximumJulianDay();
1974
+                        } else {
1975
+                            $sql_where .= " AND {$attr}.d_julianday1 >= :{$attr}date";
1976
+                            $sql_params[$attr . 'date'] = $date->minimumJulianDay();
1977
+                        }
1978
+                        if ($sortby == $match[1]) {
1979
+                            $sortby = "";
1980
+                            $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
1981
+                        }
1982
+                        unset($attrs[$attr]); // This filter has been fully processed
1983
+                    } elseif (preg_match('/^REGEXP \/(.+)\//', $value, $match)) {
1984
+                        $sql_where .= " AND f_gedcom REGEXP :{$attr}gedcom";
1985
+                        // PDO helpfully escapes backslashes for us, preventing us from matching "\n1 FACT"
1986
+                        $sql_params[$attr . 'gedcom'] = str_replace('\n', "\n", $match[1]);
1987
+                        unset($attrs[$attr]); // This filter has been fully processed
1988
+                    } elseif (preg_match('/^NAME CONTAINS (.+)$/', $value, $match)) {
1989
+                        // Do nothing, unless you have to
1990
+                        if ($match[1] != '' || $sortby == 'NAME') {
1991
+                            $sql_join .= " JOIN `##name` AS {$attr} ON n_file = f_file AND n_id IN (f_husb, f_wife)";
1992
+                            // Search the DB only if there is any name supplied
1993
+                            if ($match[1] != "") {
1994
+                                $names = explode(" ", $match[1]);
1995
+                                foreach ($names as $n => $name) {
1996
+                                    $sql_where .= " AND {$attr}.n_full LIKE CONCAT('%', :{$attr}name{$n}, '%')";
1997
+                                    $sql_params[$attr . 'name' . $n] = $name;
1998
+                                }
1999
+                            }
2000
+                            // Let the DB do the name sorting even when no name was entered
2001
+                            if ($sortby == "NAME") {
2002
+                                $sortby = "";
2003
+                                $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.n_sort";
2004
+                            }
2005
+                        }
2006
+                        unset($attrs[$attr]); // This filter has been fully processed
2007
+
2008
+                    } elseif (preg_match('/^(?:\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
2009
+                        $sql_join .= " JOIN `##places` AS {$attr}a ON ({$attr}a.p_file=f_file)";
2010
+                        $sql_join .= " JOIN `##placelinks` AS {$attr}b ON ({$attr}a.p_file={$attr}b.pl_file AND {$attr}b.pl_p_id={$attr}a.p_id AND {$attr}b.pl_gid=f_id)";
2011
+                        $sql_where .= " AND {$attr}a.p_place LIKE CONCAT('%', :{$attr}place, '%')";
2012
+                        $sql_params[$attr . 'place'] = $match[1];
2013
+                        // Don't unset this filter. This is just initial filtering
2014
+                    } elseif (preg_match('/^(\w*):*(\w*) CONTAINS (.+)$/', $value, $match)) {
2015
+                        $sql_where .= " AND f_gedcom LIKE CONCAT('%', :{$attr}contains1, '%', :{$attr}contains2, '%', :{$attr}contains3, '%')";
2016
+                        $sql_params[$attr . 'contains1'] = $match[1];
2017
+                        $sql_params[$attr . 'contains2'] = $match[2];
2018
+                        $sql_params[$attr . 'contains3'] = $match[3];
2019
+                        // Don't unset this filter. This is just initial filtering
2020
+                    }
2021
+                }
2022
+            }
2023
+
2024
+            $this->list = array();
2025
+            $rows       = Database::prepare(
2026
+                $sql_select . $sql_join . $sql_where . $sql_order_by
2027
+            )->execute($sql_params)->fetchAll();
2028
+
2029
+            foreach ($rows as $row) {
2030
+                $this->list[$row->xref] = Family::getInstance($row->xref, $WT_TREE, $row->gedcom);
2031
+            }
2032
+            break;
2033
+
2034
+        default:
2035
+            throw new \DomainException('Invalid list name: ' . $listname);
2036
+        }
2037
+
2038
+        $filters  = array();
2039
+        $filters2 = array();
2040
+        if (isset($attrs['filter1']) && count($this->list) > 0) {
2041
+            foreach ($attrs as $key => $value) {
2042
+                if (preg_match("/filter(\d)/", $key)) {
2043
+                    $condition = $value;
2044
+                    if (preg_match("/@(\w+)/", $condition, $match)) {
2045
+                        $id    = $match[1];
2046
+                        $value = "''";
2047
+                        if ($id == "ID") {
2048
+                            if (preg_match("/0 @(.+)@/", $this->gedrec, $match)) {
2049
+                                $value = "'" . $match[1] . "'";
2050
+                            }
2051
+                        } elseif ($id == "fact") {
2052
+                            $value = "'" . $this->fact . "'";
2053
+                        } elseif ($id == "desc") {
2054
+                            $value = "'" . $this->desc . "'";
2055
+                        } else {
2056
+                            if (preg_match("/\d $id (.+)/", $this->gedrec, $match)) {
2057
+                                $value = "'" . str_replace("@", "", trim($match[1])) . "'";
2058
+                            }
2059
+                        }
2060
+                        $condition = preg_replace("/@$id/", $value, $condition);
2061
+                    }
2062
+                    //-- handle regular expressions
2063
+                    if (preg_match("/([A-Z:]+)\s*([^\s]+)\s*(.+)/", $condition, $match)) {
2064
+                        $tag  = trim($match[1]);
2065
+                        $expr = trim($match[2]);
2066
+                        $val  = trim($match[3]);
2067
+                        if (preg_match("/\\$(\w+)/", $val, $match)) {
2068
+                            $val = $this->vars[$match[1]]['id'];
2069
+                            $val = trim($val);
2070
+                        }
2071
+                        if ($val) {
2072
+                            $searchstr = "";
2073
+                            $tags      = explode(":", $tag);
2074
+                            //-- only limit to a level number if we are specifically looking at a level
2075
+                            if (count($tags) > 1) {
2076
+                                $level = 1;
2077
+                                foreach ($tags as $t) {
2078
+                                    if (!empty($searchstr)) {
2079
+                                        $searchstr .= "[^\n]*(\n[2-9][^\n]*)*\n";
2080
+                                    }
2081
+                                    //-- search for both EMAIL and _EMAIL... silly double gedcom standard
2082
+                                    if ($t == "EMAIL" || $t == "_EMAIL") {
2083
+                                        $t = "_?EMAIL";
2084
+                                    }
2085
+                                    $searchstr .= $level . " " . $t;
2086
+                                    $level++;
2087
+                                }
2088
+                            } else {
2089
+                                if ($tag == "EMAIL" || $tag == "_EMAIL") {
2090
+                                    $tag = "_?EMAIL";
2091
+                                }
2092
+                                $t         = $tag;
2093
+                                $searchstr = "1 " . $tag;
2094
+                            }
2095
+                            switch ($expr) {
2096
+                            case "CONTAINS":
2097
+                                if ($t == "PLAC") {
2098
+                                    $searchstr .= "[^\n]*[, ]*" . $val;
2099
+                                } else {
2100
+                                    $searchstr .= "[^\n]*" . $val;
2101
+                                }
2102
+                                $filters[] = $searchstr;
2103
+                                break;
2104
+                            default:
2105
+                                $filters2[] = array("tag" => $tag, "expr" => $expr, "val" => $val);
2106
+                                break;
2107
+                            }
2108
+                        }
2109
+                    }
2110
+                }
2111
+            }
2112
+        }
2113
+        //-- apply other filters to the list that could not be added to the search string
2114
+        if ($filters) {
2115
+            foreach ($this->list as $key => $record) {
2116
+                foreach ($filters as $filter) {
2117
+                    if (!preg_match("/" . $filter . "/i", $record->privatizeGedcom(Auth::accessLevel($WT_TREE)))) {
2118
+                        unset($this->list[$key]);
2119
+                        break;
2120
+                    }
2121
+                }
2122
+            }
2123
+        }
2124
+        if ($filters2) {
2125
+            $mylist = array();
2126
+            foreach ($this->list as $indi) {
2127
+                $key  = $indi->getXref();
2128
+                $grec = $indi->privatizeGedcom(Auth::accessLevel($WT_TREE));
2129
+                $keep = true;
2130
+                foreach ($filters2 as $filter) {
2131
+                    if ($keep) {
2132
+                        $tag  = $filter['tag'];
2133
+                        $expr = $filter['expr'];
2134
+                        $val  = $filter['val'];
2135
+                        if ($val == "''") {
2136
+                            $val = "";
2137
+                        }
2138
+                        $tags = explode(":", $tag);
2139
+                        $t    = end($tags);
2140
+                        $v    = $this->getGedcomValue($tag, 1, $grec);
2141
+                        //-- check for EMAIL and _EMAIL (silly double gedcom standard :P)
2142
+                        if ($t == "EMAIL" && empty($v)) {
2143
+                            $tag  = str_replace("EMAIL", "_EMAIL", $tag);
2144
+                            $tags = explode(":", $tag);
2145
+                            $t    = end($tags);
2146
+                            $v    = Functions::getSubRecord(1, $tag, $grec);
2147
+                        }
2148
+
2149
+                        switch ($expr) {
2150
+                        case "GTE":
2151
+                            if ($t == "DATE") {
2152
+                                $date1 = new Date($v);
2153
+                                $date2 = new Date($val);
2154
+                                $keep  = (Date::compare($date1, $date2) >= 0);
2155
+                            } elseif ($val >= $v) {
2156
+                                $keep = true;
2157
+                            }
2158
+                            break;
2159
+                        case "LTE":
2160
+                            if ($t == "DATE") {
2161
+                                $date1 = new Date($v);
2162
+                                $date2 = new Date($val);
2163
+                                $keep  = (Date::compare($date1, $date2) <= 0);
2164
+                            } elseif ($val >= $v) {
2165
+                                $keep = true;
2166
+                            }
2167
+                            break;
2168
+                        default:
2169
+                            if ($v == $val) {
2170
+                                $keep = true;
2171
+                            } else {
2172
+                                $keep = false;
2173
+                            }
2174
+                            break;
2175
+                        }
2176
+                    }
2177
+                }
2178
+                if ($keep) {
2179
+                    $mylist[$key] = $indi;
2180
+                }
2181
+            }
2182
+            $this->list = $mylist;
2183
+        }
2184
+
2185
+        switch ($sortby) {
2186
+        case 'NAME':
2187
+            uasort($this->list, '\Fisharebest\Webtrees\GedcomRecord::compare');
2188
+            break;
2189
+        case 'CHAN':
2190
+            uasort($this->list, function (GedcomRecord $x, GedcomRecord $y) {
2191
+                return $y->lastChangeTimestamp(true) - $x->lastChangeTimestamp(true);
2192
+            });
2193
+            break;
2194
+        case 'BIRT:DATE':
2195
+            uasort($this->list, '\Fisharebest\Webtrees\Individual::compareBirthDate');
2196
+            break;
2197
+        case 'DEAT:DATE':
2198
+            uasort($this->list, '\Fisharebest\Webtrees\Individual::compareDeathDate');
2199
+            break;
2200
+        case 'MARR:DATE':
2201
+            uasort($this->list, '\Fisharebest\Webtrees\Family::compareMarrDate');
2202
+            break;
2203
+        default:
2204
+            // unsorted or already sorted by SQL
2205
+            break;
2206
+        }
2207
+
2208
+        array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
2209
+        $this->repeat_bytes = xml_get_current_line_number($this->parser) + 1;
2210
+    }
2211
+
2212
+    /**
2213
+     * XML <List>
2214
+     */
2215
+    private function listEndHandler() {
2216
+        global $report;
2217
+
2218
+        $this->process_repeats--;
2219
+        if ($this->process_repeats > 0) {
2220
+            return;
2221
+        }
2222
+
2223
+        // Check if there is any list
2224
+        if (count($this->list) > 0) {
2225
+            $lineoffset = 0;
2226
+            foreach ($this->repeats_stack as $rep) {
2227
+                $lineoffset += $rep[1];
2228
+            }
2229
+            //-- read the xml from the file
2230
+            $lines = file($report);
2231
+            while ((strpos($lines[$lineoffset + $this->repeat_bytes], "<List") === false) && (($lineoffset + $this->repeat_bytes) > 0)) {
2232
+                $lineoffset--;
2233
+            }
2234
+            $lineoffset++;
2235
+            $reportxml = "<tempdoc>\n";
2236
+            $line_nr   = $lineoffset + $this->repeat_bytes;
2237
+            // List Level counter
2238
+            $count = 1;
2239
+            while (0 < $count) {
2240
+                if (strpos($lines[$line_nr], "<List") !== false) {
2241
+                    $count++;
2242
+                } elseif (strpos($lines[$line_nr], "</List") !== false) {
2243
+                    $count--;
2244
+                }
2245
+                if (0 < $count) {
2246
+                    $reportxml .= $lines[$line_nr];
2247
+                }
2248
+                $line_nr++;
2249
+            }
2250
+            // No need to drag this
2251
+            unset($lines);
2252
+            $reportxml .= "</tempdoc>";
2253
+            // Save original values
2254
+            array_push($this->parser_stack, $this->parser);
2255
+            $oldgedrec = $this->gedrec;
2256
+
2257
+            $this->list_total   = count($this->list);
2258
+            $this->list_private = 0;
2259
+            foreach ($this->list as $record) {
2260
+                if ($record->canShow()) {
2261
+                    $this->gedrec = $record->privatizeGedcom(Auth::accessLevel($record->getTree()));
2262
+                    //-- start the sax parser
2263
+                    $repeat_parser = xml_parser_create();
2264
+                    $this->parser  = $repeat_parser;
2265
+                    xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
2266
+                    xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
2267
+                    xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
2268
+                    if (!xml_parse($repeat_parser, $reportxml, true)) {
2269
+                        throw new \DomainException(sprintf(
2270
+                            'ListEHandler XML error: %s at line %d',
2271
+                            xml_error_string(xml_get_error_code($repeat_parser)),
2272
+                            xml_get_current_line_number($repeat_parser)
2273
+                        ));
2274
+                    }
2275
+                    xml_parser_free($repeat_parser);
2276
+                } else {
2277
+                    $this->list_private++;
2278
+                }
2279
+            }
2280
+            $this->list   = array();
2281
+            $this->parser = array_pop($this->parser_stack);
2282
+            $this->gedrec = $oldgedrec;
2283
+        }
2284
+        list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
2285
+    }
2286
+
2287
+    /**
2288
+     * XML <ListTotal> element handler
2289
+     *
2290
+     * Prints the total number of records in a list
2291
+     * The total number is collected from
2292
+     * List and Relatives
2293
+     */
2294
+    private function listTotalStartHandler() {
2295
+        if ($this->list_private == 0) {
2296
+            $this->current_element->addText($this->list_total);
2297
+        } else {
2298
+            $this->current_element->addText(($this->list_total - $this->list_private) . " / " . $this->list_total);
2299
+        }
2300
+    }
2301
+
2302
+    /**
2303
+     * XML <Relatives>
2304
+     *
2305
+     * @param array $attrs an array of key value pairs for the attributes
2306
+     */
2307
+    private function relativesStartHandler($attrs) {
2308
+        global $WT_TREE;
2309
+
2310
+        $this->process_repeats++;
2311
+        if ($this->process_repeats > 1) {
2312
+            return;
2313
+        }
2314
+
2315
+        $sortby = "NAME";
2316
+        if (isset($attrs['sortby'])) {
2317
+            $sortby = $attrs['sortby'];
2318
+        }
2319
+        $match = array();
2320
+        if (preg_match("/\\$(\w+)/", $sortby, $match)) {
2321
+            $sortby = $this->vars[$match[1]]['id'];
2322
+            $sortby = trim($sortby);
2323
+        }
2324
+
2325
+        $maxgen = -1;
2326
+        if (isset($attrs['maxgen'])) {
2327
+            $maxgen = $attrs['maxgen'];
2328
+        }
2329
+        if ($maxgen == "*") {
2330
+            $maxgen = -1;
2331
+        }
2332
+
2333
+        $group = "child-family";
2334
+        if (isset($attrs['group'])) {
2335
+            $group = $attrs['group'];
2336
+        }
2337
+        if (preg_match("/\\$(\w+)/", $group, $match)) {
2338
+            $group = $this->vars[$match[1]]['id'];
2339
+            $group = trim($group);
2340
+        }
2341
+
2342
+        $id = "";
2343
+        if (isset($attrs['id'])) {
2344
+            $id = $attrs['id'];
2345
+        }
2346
+        if (preg_match("/\\$(\w+)/", $id, $match)) {
2347
+            $id = $this->vars[$match[1]]['id'];
2348
+            $id = trim($id);
2349
+        }
2350
+
2351
+        $this->list = array();
2352
+        $person     = Individual::getInstance($id, $WT_TREE);
2353
+        if (!empty($person)) {
2354
+            $this->list[$id] = $person;
2355
+            switch ($group) {
2356
+            case "child-family":
2357
+                foreach ($person->getChildFamilies() as $family) {
2358
+                    $husband = $family->getHusband();
2359
+                    $wife    = $family->getWife();
2360
+                    if (!empty($husband)) {
2361
+                        $this->list[$husband->getXref()] = $husband;
2362
+                    }
2363
+                    if (!empty($wife)) {
2364
+                        $this->list[$wife->getXref()] = $wife;
2365
+                    }
2366
+                    $children = $family->getChildren();
2367
+                    foreach ($children as $child) {
2368
+                        if (!empty($child)) {
2369
+                            $this->list[$child->getXref()] = $child;
2370
+                        }
2371
+                    }
2372
+                }
2373
+                break;
2374
+            case "spouse-family":
2375
+                foreach ($person->getSpouseFamilies() as $family) {
2376
+                    $husband = $family->getHusband();
2377
+                    $wife    = $family->getWife();
2378
+                    if (!empty($husband)) {
2379
+                        $this->list[$husband->getXref()] = $husband;
2380
+                    }
2381
+                    if (!empty($wife)) {
2382
+                        $this->list[$wife->getXref()] = $wife;
2383
+                    }
2384
+                    $children = $family->getChildren();
2385
+                    foreach ($children as $child) {
2386
+                        if (!empty($child)) {
2387
+                            $this->list[$child->getXref()] = $child;
2388
+                        }
2389
+                    }
2390
+                }
2391
+                break;
2392
+            case "direct-ancestors":
2393
+                $this->addAncestors($this->list, $id, false, $maxgen);
2394
+                break;
2395
+            case "ancestors":
2396
+                $this->addAncestors($this->list, $id, true, $maxgen);
2397
+                break;
2398
+            case "descendants":
2399
+                $this->list[$id]->generation = 1;
2400
+                $this->addDescendancy($this->list, $id, false, $maxgen);
2401
+                break;
2402
+            case "all":
2403
+                $this->addAncestors($this->list, $id, true, $maxgen);
2404
+                $this->addDescendancy($this->list, $id, true, $maxgen);
2405
+                break;
2406
+            }
2407
+        }
2408
+
2409
+        switch ($sortby) {
2410
+        case 'NAME':
2411
+            uasort($this->list, '\Fisharebest\Webtrees\GedcomRecord::compare');
2412
+            break;
2413
+        case 'BIRT:DATE':
2414
+            uasort($this->list, '\Fisharebest\Webtrees\Individual::compareBirthDate');
2415
+            break;
2416
+        case 'DEAT:DATE':
2417
+            uasort($this->list, '\Fisharebest\Webtrees\Individual::compareDeathDate');
2418
+            break;
2419
+        case 'generation':
2420
+            $newarray = array();
2421
+            reset($this->list);
2422
+            $genCounter = 1;
2423
+            while (count($newarray) < count($this->list)) {
2424
+                foreach ($this->list as $key => $value) {
2425
+                    $this->generation = $value->generation;
2426
+                    if ($this->generation == $genCounter) {
2427
+                        $newarray[$key]             = new \stdClass;
2428
+                        $newarray[$key]->generation = $this->generation;
2429
+                    }
2430
+                }
2431
+                $genCounter++;
2432
+            }
2433
+            $this->list = $newarray;
2434
+            break;
2435
+        default:
2436
+            // unsorted
2437
+            break;
2438
+        }
2439
+        array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
2440
+        $this->repeat_bytes = xml_get_current_line_number($this->parser) + 1;
2441
+    }
2442
+
2443
+    /**
2444
+     * XML </ Relatives>
2445
+     */
2446
+    private function relativesEndHandler() {
2447
+        global $report, $WT_TREE;
2448
+
2449
+        $this->process_repeats--;
2450
+        if ($this->process_repeats > 0) {
2451
+            return;
2452
+        }
2453
+
2454
+        // Check if there is any relatives
2455
+        if (count($this->list) > 0) {
2456
+            $lineoffset = 0;
2457
+            foreach ($this->repeats_stack as $rep) {
2458
+                $lineoffset += $rep[1];
2459
+            }
2460
+            //-- read the xml from the file
2461
+            $lines = file($report);
2462
+            while ((strpos($lines[$lineoffset + $this->repeat_bytes], "<Relatives") === false) && (($lineoffset + $this->repeat_bytes) > 0)) {
2463
+                $lineoffset--;
2464
+            }
2465
+            $lineoffset++;
2466
+            $reportxml = "<tempdoc>\n";
2467
+            $line_nr   = $lineoffset + $this->repeat_bytes;
2468
+            // Relatives Level counter
2469
+            $count = 1;
2470
+            while (0 < $count) {
2471
+                if (strpos($lines[$line_nr], "<Relatives") !== false) {
2472
+                    $count++;
2473
+                } elseif (strpos($lines[$line_nr], "</Relatives") !== false) {
2474
+                    $count--;
2475
+                }
2476
+                if (0 < $count) {
2477
+                    $reportxml .= $lines[$line_nr];
2478
+                }
2479
+                $line_nr++;
2480
+            }
2481
+            // No need to drag this
2482
+            unset($lines);
2483
+            $reportxml .= "</tempdoc>\n";
2484
+            // Save original values
2485
+            array_push($this->parser_stack, $this->parser);
2486
+            $oldgedrec = $this->gedrec;
2487
+
2488
+            $this->list_total   = count($this->list);
2489
+            $this->list_private = 0;
2490
+            foreach ($this->list as $key => $value) {
2491
+                if (isset($value->generation)) {
2492
+                    $this->generation = $value->generation;
2493
+                }
2494
+                $tmp          = GedcomRecord::getInstance($key, $WT_TREE);
2495
+                $this->gedrec = $tmp->privatizeGedcom(Auth::accessLevel($WT_TREE));
2496
+
2497
+                $repeat_parser = xml_parser_create();
2498
+                $this->parser  = $repeat_parser;
2499
+                xml_parser_set_option($repeat_parser, XML_OPTION_CASE_FOLDING, false);
2500
+                xml_set_element_handler($repeat_parser, array($this, 'startElement'), array($this, 'endElement'));
2501
+                xml_set_character_data_handler($repeat_parser, array($this, 'characterData'));
2502
+
2503
+                if (!xml_parse($repeat_parser, $reportxml, true)) {
2504
+                    throw new \DomainException(sprintf("RelativesEHandler XML error: %s at line %d", xml_error_string(xml_get_error_code($repeat_parser)), xml_get_current_line_number($repeat_parser)));
2505
+                }
2506
+                xml_parser_free($repeat_parser);
2507
+            }
2508
+            // Clean up the list array
2509
+            $this->list   = array();
2510
+            $this->parser = array_pop($this->parser_stack);
2511
+            $this->gedrec = $oldgedrec;
2512
+        }
2513
+        list($this->repeats, $this->repeat_bytes) = array_pop($this->repeats_stack);
2514
+    }
2515
+
2516
+    /**
2517
+     * XML <Generation /> element handler
2518
+     *
2519
+     * Prints the number of generations
2520
+     */
2521
+    private function generationStartHandler() {
2522
+        $this->current_element->addText($this->generation);
2523
+    }
2524
+
2525
+    /**
2526
+     * XML <NewPage /> element handler
2527
+     *
2528
+     * Has to be placed in an element (header, pageheader, body or footer)
2529
+     */
2530
+    private function newPageStartHandler() {
2531
+        $temp = "addpage";
2532
+        $this->wt_report->addElement($temp);
2533
+    }
2534
+
2535
+    /**
2536
+     * XML <html>
2537
+     *
2538
+     * @param string  $tag   HTML tag name
2539
+     * @param array[] $attrs an array of key value pairs for the attributes
2540
+     */
2541
+    private function htmlStartHandler($tag, $attrs) {
2542
+        if ($tag === "tempdoc") {
2543
+            return;
2544
+        }
2545
+        array_push($this->wt_report_stack, $this->wt_report);
2546
+        $this->wt_report       = $this->report_root->createHTML($tag, $attrs);
2547
+        $this->current_element = $this->wt_report;
2548
+
2549
+        array_push($this->print_data_stack, $this->print_data);
2550
+        $this->print_data = true;
2551
+    }
2552
+
2553
+    /**
2554
+     * XML </html>
2555
+     *
2556
+     * @param string $tag
2557
+     */
2558
+    private function htmlEndHandler($tag) {
2559
+        if ($tag === "tempdoc") {
2560
+            return;
2561
+        }
2562
+
2563
+        $this->print_data      = array_pop($this->print_data_stack);
2564
+        $this->current_element = $this->wt_report;
2565
+        $this->wt_report       = array_pop($this->wt_report_stack);
2566
+        if (!is_null($this->wt_report)) {
2567
+            $this->wt_report->addElement($this->current_element);
2568
+        } else {
2569
+            $this->wt_report = $this->current_element;
2570
+        }
2571
+    }
2572
+
2573
+    /**
2574
+     * Handle <Input>
2575
+     */
2576
+    private function inputStartHandler() {
2577
+        // Dummy function, to prevent the default HtmlStartHandler() being called
2578
+    }
2579
+
2580
+    /**
2581
+     * Handle </Input>
2582
+     */
2583
+    private function inputEndHandler() {
2584
+        // Dummy function, to prevent the default HtmlEndHandler() being called
2585
+    }
2586
+
2587
+    /**
2588
+     * Handle <Report>
2589
+     */
2590
+    private function reportStartHandler() {
2591
+        // Dummy function, to prevent the default HtmlStartHandler() being called
2592
+    }
2593
+
2594
+    /**
2595
+     * Handle </Report>
2596
+     */
2597
+    private function reportEndHandler() {
2598
+        // Dummy function, to prevent the default HtmlEndHandler() being called
2599
+    }
2600
+
2601
+    /**
2602
+     * XML </titleEndHandler>
2603
+     */
2604
+    private function titleEndHandler() {
2605
+        $this->report_root->addTitle($this->text);
2606
+    }
2607
+
2608
+    /**
2609
+     * XML </descriptionEndHandler>
2610
+     */
2611
+    private function descriptionEndHandler() {
2612
+        $this->report_root->addDescription($this->text);
2613
+    }
2614
+
2615
+    /**
2616
+     * Create a list of all descendants.
2617
+     *
2618
+     * @param string[] $list
2619
+     * @param string   $pid
2620
+     * @param bool  $parents
2621
+     * @param int  $generations
2622
+     */
2623
+    private function addDescendancy(&$list, $pid, $parents = false, $generations = -1) {
2624
+        global $WT_TREE;
2625
+
2626
+        $person = Individual::getInstance($pid, $WT_TREE);
2627
+        if ($person === null) {
2628
+            return;
2629
+        }
2630
+        if (!isset($list[$pid])) {
2631
+            $list[$pid] = $person;
2632
+        }
2633
+        if (!isset($list[$pid]->generation)) {
2634
+            $list[$pid]->generation = 0;
2635
+        }
2636
+        foreach ($person->getSpouseFamilies() as $family) {
2637
+            if ($parents) {
2638
+                $husband = $family->getHusband();
2639
+                $wife    = $family->getWife();
2640
+                if ($husband) {
2641
+                    $list[$husband->getXref()] = $husband;
2642
+                    if (isset($list[$pid]->generation)) {
2643
+                        $list[$husband->getXref()]->generation = $list[$pid]->generation - 1;
2644
+                    } else {
2645
+                        $list[$husband->getXref()]->generation = 1;
2646
+                    }
2647
+                }
2648
+                if ($wife) {
2649
+                    $list[$wife->getXref()] = $wife;
2650
+                    if (isset($list[$pid]->generation)) {
2651
+                        $list[$wife->getXref()]->generation = $list[$pid]->generation - 1;
2652
+                    } else {
2653
+                        $list[$wife->getXref()]->generation = 1;
2654
+                    }
2655
+                }
2656
+            }
2657
+            $children = $family->getChildren();
2658
+            foreach ($children as $child) {
2659
+                if ($child) {
2660
+                    $list[$child->getXref()] = $child;
2661
+                    if (isset($list[$pid]->generation)) {
2662
+                        $list[$child->getXref()]->generation = $list[$pid]->generation + 1;
2663
+                    } else {
2664
+                        $list[$child->getXref()]->generation = 2;
2665
+                    }
2666
+                }
2667
+            }
2668
+            if ($generations == -1 || $list[$pid]->generation + 1 < $generations) {
2669
+                foreach ($children as $child) {
2670
+                    $this->addDescendancy($list, $child->getXref(), $parents, $generations); // recurse on the childs family
2671
+                }
2672
+            }
2673
+        }
2674
+    }
2675
+
2676
+    /**
2677
+     * Create a list of all ancestors.
2678
+     *
2679
+     * @param string[] $list
2680
+     * @param string   $pid
2681
+     * @param bool  $children
2682
+     * @param int  $generations
2683
+     */
2684
+    private function addAncestors(&$list, $pid, $children = false, $generations = -1) {
2685
+        global $WT_TREE;
2686
+
2687
+        $genlist                = array($pid);
2688
+        $list[$pid]->generation = 1;
2689
+        while (count($genlist) > 0) {
2690
+            $id = array_shift($genlist);
2691
+            if (strpos($id, 'empty') === 0) {
2692
+                continue; // id can be something like “empty7”
2693
+            }
2694
+            $person = Individual::getInstance($id, $WT_TREE);
2695
+            foreach ($person->getChildFamilies() as $family) {
2696
+                $husband = $family->getHusband();
2697
+                $wife    = $family->getWife();
2698
+                if ($husband) {
2699
+                    $list[$husband->getXref()]             = $husband;
2700
+                    $list[$husband->getXref()]->generation = $list[$id]->generation + 1;
2701
+                }
2702
+                if ($wife) {
2703
+                    $list[$wife->getXref()]             = $wife;
2704
+                    $list[$wife->getXref()]->generation = $list[$id]->generation + 1;
2705
+                }
2706
+                if ($generations == -1 || $list[$id]->generation + 1 < $generations) {
2707
+                    if ($husband) {
2708
+                        array_push($genlist, $husband->getXref());
2709
+                    }
2710
+                    if ($wife) {
2711
+                        array_push($genlist, $wife->getXref());
2712
+                    }
2713
+                }
2714
+                if ($children) {
2715
+                    foreach ($family->getChildren() as $child) {
2716
+                        $list[$child->getXref()] = $child;
2717
+                        if (isset($list[$id]->generation)) {
2718
+                            $list[$child->getXref()]->generation = $list[$id]->generation;
2719
+                        } else {
2720
+                            $list[$child->getXref()]->generation = 1;
2721
+                        }
2722
+                    }
2723
+                }
2724
+            }
2725
+        }
2726
+    }
2727
+
2728
+    /**
2729
+     * get gedcom tag value
2730
+     *
2731
+     * @param string  $tag    The tag to find, use : to delineate subtags
2732
+     * @param int $level  The gedcom line level of the first tag to find, setting level to 0 will cause it to use 1+ the level of the incoming record
2733
+     * @param string  $gedrec The gedcom record to get the value from
2734
+     *
2735
+     * @return string the value of a gedcom tag from the given gedcom record
2736
+     */
2737
+    private function getGedcomValue($tag, $level, $gedrec) {
2738
+        global $WT_TREE;
2739
+
2740
+        if (empty($gedrec)) {
2741
+            return '';
2742
+        }
2743
+        $tags      = explode(':', $tag);
2744
+        $origlevel = $level;
2745
+        if ($level == 0) {
2746
+            $level = $gedrec{0} + 1;
2747
+        }
2748
+
2749
+        $subrec = $gedrec;
2750
+        foreach ($tags as $t) {
2751
+            $lastsubrec = $subrec;
2752
+            $subrec     = Functions::getSubRecord($level, "$level $t", $subrec);
2753
+            if (empty($subrec) && $origlevel == 0) {
2754
+                $level--;
2755
+                $subrec = Functions::getSubRecord($level, "$level $t", $lastsubrec);
2756
+            }
2757
+            if (empty($subrec)) {
2758
+                if ($t == "TITL") {
2759
+                    $subrec = Functions::getSubRecord($level, "$level ABBR", $lastsubrec);
2760
+                    if (!empty($subrec)) {
2761
+                        $t = "ABBR";
2762
+                    }
2763
+                }
2764
+                if (empty($subrec)) {
2765
+                    if ($level > 0) {
2766
+                        $level--;
2767
+                    }
2768
+                    $subrec = Functions::getSubRecord($level, "@ $t", $gedrec);
2769
+                    if (empty($subrec)) {
2770
+                        return '';
2771
+                    }
2772
+                }
2773
+            }
2774
+            $level++;
2775
+        }
2776
+        $level--;
2777
+        $ct = preg_match("/$level $t(.*)/", $subrec, $match);
2778
+        if ($ct == 0) {
2779
+            $ct = preg_match("/$level @.+@ (.+)/", $subrec, $match);
2780
+        }
2781
+        if ($ct == 0) {
2782
+            $ct = preg_match("/@ $t (.+)/", $subrec, $match);
2783
+        }
2784
+        if ($ct > 0) {
2785
+            $value = trim($match[1]);
2786
+            if ($t == 'NOTE' && preg_match('/^@(.+)@$/', $value, $match)) {
2787
+                $note = Note::getInstance($match[1], $WT_TREE);
2788
+                if ($note) {
2789
+                    $value = $note->getNote();
2790
+                } else {
2791
+                    //-- set the value to the id without the @
2792
+                    $value = $match[1];
2793
+                }
2794
+            }
2795
+            if ($level != 0 || $t != "NOTE") {
2796
+                $value .= Functions::getCont($level + 1, $subrec);
2797
+            }
2798
+
2799
+            return $value;
2800
+        }
2801
+
2802
+        return "";
2803
+    }
2804
+
2805
+    /**
2806
+     * Replace variable identifiers with their values.
2807
+     *
2808
+     * @param string $expression An expression such as "$foo == 123"
2809
+     * @param bool   $quote      Whether to add quotation marks
2810
+     *
2811
+     * @return string
2812
+     */
2813
+    private function substituteVars($expression, $quote) {
2814
+        $that = $this; // PHP5.3 cannot access $this inside a closure
2815
+        return preg_replace_callback(
2816
+            '/\$(\w+)/',
2817
+            function ($matches) use ($that, $quote) {
2818
+                if (isset($that->vars[$matches[1]]['id'])) {
2819
+                    if ($quote) {
2820
+                        return "'" . addcslashes($that->vars[$matches[1]]['id'], "'") . "'";
2821
+                    } else {
2822
+                        return $that->vars[$matches[1]]['id'];
2823
+                    }
2824
+                } else {
2825
+                    Log::addErrorLog(sprintf('Undefined variable $%s in report', $matches[1]));
2826
+
2827
+                    return '$' . $matches[1];
2828
+                }
2829
+            },
2830
+            $expression
2831
+        );
2832
+    }
2833 2833
 }
Please login to merge, or discard this patch.
Switch Indentation   +332 added lines, -332 removed lines patch added patch discarded remove patch
@@ -934,14 +934,14 @@  discard block
 block discarded – undo
934 934
 				$tags  = preg_split('/[: ]/', $tag);
935 935
 				$value = $this->getGedcomValue($tag, $level, $this->gedrec);
936 936
 				switch (end($tags)) {
937
-				case 'DATE':
938
-					$tmp   = new Date($value);
939
-					$value = $tmp->display();
940
-					break;
941
-				case 'PLAC':
942
-					$tmp   = new Place($value, $WT_TREE);
943
-					$value = $tmp->getShortName();
944
-					break;
937
+				    case 'DATE':
938
+					    $tmp   = new Date($value);
939
+					    $value = $tmp->display();
940
+					    break;
941
+				    case 'PLAC':
942
+					    $tmp   = new Place($value, $WT_TREE);
943
+					    $value = $tmp->getShortName();
944
+					    break;
945 945
 				}
946 946
 				if ($useBreak == "1") {
947 947
 					// Insert <br> when multiple dates exist.
@@ -1335,22 +1335,22 @@  discard block
 block discarded – undo
1335 1335
 		// Arithmetic functions
1336 1336
 		if (preg_match("/(\d+)\s*([\-\+\*\/])\s*(\d+)/", $value, $match)) {
1337 1337
 			switch ($match[2]) {
1338
-			case "+":
1339
-				$t     = $match[1] + $match[3];
1340
-				$value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1341
-				break;
1342
-			case "-":
1343
-				$t     = $match[1] - $match[3];
1344
-				$value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1345
-				break;
1346
-			case "*":
1347
-				$t     = $match[1] * $match[3];
1348
-				$value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1349
-				break;
1350
-			case "/":
1351
-				$t     = $match[1] / $match[3];
1352
-				$value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1353
-				break;
1338
+			    case "+":
1339
+				    $t     = $match[1] + $match[3];
1340
+				    $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1341
+				    break;
1342
+			    case "-":
1343
+				    $t     = $match[1] - $match[3];
1344
+				    $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1345
+				    break;
1346
+			    case "*":
1347
+				    $t     = $match[1] * $match[3];
1348
+				    $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1349
+				    break;
1350
+			    case "/":
1351
+				    $t     = $match[1] / $match[3];
1352
+				    $value = preg_replace("/" . $match[1] . "\s*([\-\+\*\/])\s*" . $match[3] . "/", $t, $value);
1353
+				    break;
1354 1354
 			}
1355 1355
 		}
1356 1356
 		if (strpos($value, "@") !== false) {
@@ -1859,180 +1859,180 @@  discard block
 block discarded – undo
1859 1859
 		}
1860 1860
 		// Some filters/sorts can be applied using SQL, while others require PHP
1861 1861
 		switch ($listname) {
1862
-		case "pending":
1863
-			$rows = Database::prepare(
1864
-				"SELECT xref, CASE new_gedcom WHEN '' THEN old_gedcom ELSE new_gedcom END AS gedcom" .
1865
-				" FROM `##change`" . " WHERE (xref, change_id) IN (" .
1866
-				"  SELECT xref, MAX(change_id)" .
1867
-				"  FROM `##change`" .
1868
-				"  WHERE status = 'pending' AND gedcom_id = :tree_id" .
1869
-				"  GROUP BY xref" .
1870
-				" )"
1871
-			)->execute(array(
1872
-				'tree_id' => $WT_TREE->getTreeId(),
1873
-			))->fetchAll();
1874
-			$this->list = array();
1875
-			foreach ($rows as $row) {
1876
-				$this->list[] = GedcomRecord::getInstance($row->xref, $WT_TREE, $row->gedcom);
1877
-			}
1878
-			break;
1879
-		case 'individual':
1880
-			$sql_select   = "SELECT i_id AS xref, i_gedcom AS gedcom FROM `##individuals` ";
1881
-			$sql_join     = "";
1882
-			$sql_where    = " WHERE i_file = :tree_id";
1883
-			$sql_order_by = "";
1884
-			$sql_params   = array('tree_id' => $WT_TREE->getTreeId());
1885
-			foreach ($attrs as $attr => $value) {
1886
-				if (strpos($attr, 'filter') === 0 && $value) {
1887
-					$value = $this->substituteVars($value, false);
1888
-					// Convert the various filters into SQL
1889
-					if (preg_match('/^(\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
1890
-						$sql_join .= " JOIN `##dates` AS {$attr} ON ({$attr}.d_file=i_file AND {$attr}.d_gid=i_id)";
1891
-						$sql_where .= " AND {$attr}.d_fact = :{$attr}fact";
1892
-						$sql_params[$attr . 'fact'] = $match[1];
1893
-						$date                       = new Date($match[3]);
1894
-						if ($match[2] == "LTE") {
1895
-							$sql_where .= " AND {$attr}.d_julianday2 <= :{$attr}date";
1896
-							$sql_params[$attr . 'date'] = $date->maximumJulianDay();
1897
-						} else {
1898
-							$sql_where .= " AND {$attr}.d_julianday1 >= :{$attr}date";
1899
-							$sql_params[$attr . 'date'] = $date->minimumJulianDay();
1900
-						}
1901
-						if ($sortby == $match[1]) {
1902
-							$sortby = "";
1903
-							$sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
1904
-						}
1905
-						unset($attrs[$attr]); // This filter has been fully processed
1906
-					} elseif (preg_match('/^NAME CONTAINS (.*)$/', $value, $match)) {
1907
-						// Do nothing, unless you have to
1908
-						if ($match[1] != '' || $sortby == 'NAME') {
1909
-							$sql_join .= " JOIN `##name` AS {$attr} ON (n_file=i_file AND n_id=i_id)";
1910
-							// Search the DB only if there is any name supplied
1911
-							if ($match[1] != "") {
1912
-								$names = explode(" ", $match[1]);
1913
-								foreach ($names as $n => $name) {
1914
-									$sql_where .= " AND {$attr}.n_full LIKE CONCAT('%', :{$attr}name{$n}, '%')";
1915
-									$sql_params[$attr . 'name' . $n] = $name;
1916
-								}
1917
-							}
1918
-							// Let the DB do the name sorting even when no name was entered
1919
-							if ($sortby == "NAME") {
1920
-								$sortby = "";
1921
-								$sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.n_sort";
1922
-							}
1923
-						}
1924
-						unset($attrs[$attr]); // This filter has been fully processed
1925
-					} elseif (preg_match('/^REGEXP \/(.+)\//', $value, $match)) {
1926
-						$sql_where .= " AND i_gedcom REGEXP :{$attr}gedcom";
1927
-						// PDO helpfully escapes backslashes for us, preventing us from matching "\n1 FACT"
1928
-						$sql_params[$attr . 'gedcom'] = str_replace('\n', "\n", $match[1]);
1929
-						unset($attrs[$attr]); // This filter has been fully processed
1930
-					} elseif (preg_match('/^(?:\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
1931
-						$sql_join .= " JOIN `##places` AS {$attr}a ON ({$attr}a.p_file = i_file)";
1932
-						$sql_join .= " JOIN `##placelinks` AS {$attr}b ON ({$attr}a.p_file = {$attr}b.pl_file AND {$attr}b.pl_p_id = {$attr}a.p_id AND {$attr}b.pl_gid = i_id)";
1933
-						$sql_where .= " AND {$attr}a.p_place LIKE CONCAT('%', :{$attr}place, '%')";
1934
-						$sql_params[$attr . 'place'] = $match[1];
1935
-						// Don't unset this filter. This is just initial filtering
1936
-					} elseif (preg_match('/^(\w*):*(\w*) CONTAINS (.+)$/', $value, $match)) {
1937
-						$sql_where .= " AND i_gedcom LIKE CONCAT('%', :{$attr}contains1, '%', :{$attr}contains2, '%', :{$attr}contains3, '%')";
1938
-						$sql_params[$attr . 'contains1'] = $match[1];
1939
-						$sql_params[$attr . 'contains2'] = $match[2];
1940
-						$sql_params[$attr . 'contains3'] = $match[3];
1941
-						// Don't unset this filter. This is just initial filtering
1942
-					}
1943
-				}
1944
-			}
1945
-
1946
-			$this->list = array();
1947
-			$rows       = Database::prepare(
1948
-				$sql_select . $sql_join . $sql_where . $sql_order_by
1949
-			)->execute($sql_params)->fetchAll();
1950
-
1951
-			foreach ($rows as $row) {
1952
-				$this->list[$row->xref] = Individual::getInstance($row->xref, $WT_TREE, $row->gedcom);
1953
-			}
1954
-			break;
1955
-
1956
-		case 'family':
1957
-			$sql_select   = "SELECT f_id AS xref, f_gedcom AS gedcom FROM `##families`";
1958
-			$sql_join     = "";
1959
-			$sql_where    = " WHERE f_file = :tree_id";
1960
-			$sql_order_by = "";
1961
-			$sql_params   = array('tree_id' => $WT_TREE->getTreeId());
1962
-			foreach ($attrs as $attr => $value) {
1963
-				if (strpos($attr, 'filter') === 0 && $value) {
1964
-					$value = $this->substituteVars($value, false);
1965
-					// Convert the various filters into SQL
1966
-					if (preg_match('/^(\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
1967
-						$sql_join .= " JOIN `##dates` AS {$attr} ON ({$attr}.d_file=f_file AND {$attr}.d_gid=f_id)";
1968
-						$sql_where .= " AND {$attr}.d_fact = :{$attr}fact";
1969
-						$sql_params[$attr . 'fact'] = $match[1];
1970
-						$date                       = new Date($match[3]);
1971
-						if ($match[2] == "LTE") {
1972
-							$sql_where .= " AND {$attr}.d_julianday2 <= :{$attr}date";
1973
-							$sql_params[$attr . 'date'] = $date->maximumJulianDay();
1974
-						} else {
1975
-							$sql_where .= " AND {$attr}.d_julianday1 >= :{$attr}date";
1976
-							$sql_params[$attr . 'date'] = $date->minimumJulianDay();
1977
-						}
1978
-						if ($sortby == $match[1]) {
1979
-							$sortby = "";
1980
-							$sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
1981
-						}
1982
-						unset($attrs[$attr]); // This filter has been fully processed
1983
-					} elseif (preg_match('/^REGEXP \/(.+)\//', $value, $match)) {
1984
-						$sql_where .= " AND f_gedcom REGEXP :{$attr}gedcom";
1985
-						// PDO helpfully escapes backslashes for us, preventing us from matching "\n1 FACT"
1986
-						$sql_params[$attr . 'gedcom'] = str_replace('\n', "\n", $match[1]);
1987
-						unset($attrs[$attr]); // This filter has been fully processed
1988
-					} elseif (preg_match('/^NAME CONTAINS (.+)$/', $value, $match)) {
1989
-						// Do nothing, unless you have to
1990
-						if ($match[1] != '' || $sortby == 'NAME') {
1991
-							$sql_join .= " JOIN `##name` AS {$attr} ON n_file = f_file AND n_id IN (f_husb, f_wife)";
1992
-							// Search the DB only if there is any name supplied
1993
-							if ($match[1] != "") {
1994
-								$names = explode(" ", $match[1]);
1995
-								foreach ($names as $n => $name) {
1996
-									$sql_where .= " AND {$attr}.n_full LIKE CONCAT('%', :{$attr}name{$n}, '%')";
1997
-									$sql_params[$attr . 'name' . $n] = $name;
1998
-								}
1999
-							}
2000
-							// Let the DB do the name sorting even when no name was entered
2001
-							if ($sortby == "NAME") {
2002
-								$sortby = "";
2003
-								$sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.n_sort";
2004
-							}
2005
-						}
2006
-						unset($attrs[$attr]); // This filter has been fully processed
2007
-
2008
-					} elseif (preg_match('/^(?:\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
2009
-						$sql_join .= " JOIN `##places` AS {$attr}a ON ({$attr}a.p_file=f_file)";
2010
-						$sql_join .= " JOIN `##placelinks` AS {$attr}b ON ({$attr}a.p_file={$attr}b.pl_file AND {$attr}b.pl_p_id={$attr}a.p_id AND {$attr}b.pl_gid=f_id)";
2011
-						$sql_where .= " AND {$attr}a.p_place LIKE CONCAT('%', :{$attr}place, '%')";
2012
-						$sql_params[$attr . 'place'] = $match[1];
2013
-						// Don't unset this filter. This is just initial filtering
2014
-					} elseif (preg_match('/^(\w*):*(\w*) CONTAINS (.+)$/', $value, $match)) {
2015
-						$sql_where .= " AND f_gedcom LIKE CONCAT('%', :{$attr}contains1, '%', :{$attr}contains2, '%', :{$attr}contains3, '%')";
2016
-						$sql_params[$attr . 'contains1'] = $match[1];
2017
-						$sql_params[$attr . 'contains2'] = $match[2];
2018
-						$sql_params[$attr . 'contains3'] = $match[3];
2019
-						// Don't unset this filter. This is just initial filtering
2020
-					}
2021
-				}
2022
-			}
2023
-
2024
-			$this->list = array();
2025
-			$rows       = Database::prepare(
2026
-				$sql_select . $sql_join . $sql_where . $sql_order_by
2027
-			)->execute($sql_params)->fetchAll();
2028
-
2029
-			foreach ($rows as $row) {
2030
-				$this->list[$row->xref] = Family::getInstance($row->xref, $WT_TREE, $row->gedcom);
2031
-			}
2032
-			break;
2033
-
2034
-		default:
2035
-			throw new \DomainException('Invalid list name: ' . $listname);
1862
+		    case "pending":
1863
+			    $rows = Database::prepare(
1864
+				    "SELECT xref, CASE new_gedcom WHEN '' THEN old_gedcom ELSE new_gedcom END AS gedcom" .
1865
+				    " FROM `##change`" . " WHERE (xref, change_id) IN (" .
1866
+				    "  SELECT xref, MAX(change_id)" .
1867
+				    "  FROM `##change`" .
1868
+				    "  WHERE status = 'pending' AND gedcom_id = :tree_id" .
1869
+				    "  GROUP BY xref" .
1870
+				    " )"
1871
+			    )->execute(array(
1872
+				    'tree_id' => $WT_TREE->getTreeId(),
1873
+			    ))->fetchAll();
1874
+			    $this->list = array();
1875
+			    foreach ($rows as $row) {
1876
+				    $this->list[] = GedcomRecord::getInstance($row->xref, $WT_TREE, $row->gedcom);
1877
+			    }
1878
+			    break;
1879
+		    case 'individual':
1880
+			    $sql_select   = "SELECT i_id AS xref, i_gedcom AS gedcom FROM `##individuals` ";
1881
+			    $sql_join     = "";
1882
+			    $sql_where    = " WHERE i_file = :tree_id";
1883
+			    $sql_order_by = "";
1884
+			    $sql_params   = array('tree_id' => $WT_TREE->getTreeId());
1885
+			    foreach ($attrs as $attr => $value) {
1886
+				    if (strpos($attr, 'filter') === 0 && $value) {
1887
+					    $value = $this->substituteVars($value, false);
1888
+					    // Convert the various filters into SQL
1889
+					    if (preg_match('/^(\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
1890
+						    $sql_join .= " JOIN `##dates` AS {$attr} ON ({$attr}.d_file=i_file AND {$attr}.d_gid=i_id)";
1891
+						    $sql_where .= " AND {$attr}.d_fact = :{$attr}fact";
1892
+						    $sql_params[$attr . 'fact'] = $match[1];
1893
+						    $date                       = new Date($match[3]);
1894
+						    if ($match[2] == "LTE") {
1895
+							    $sql_where .= " AND {$attr}.d_julianday2 <= :{$attr}date";
1896
+							    $sql_params[$attr . 'date'] = $date->maximumJulianDay();
1897
+						    } else {
1898
+							    $sql_where .= " AND {$attr}.d_julianday1 >= :{$attr}date";
1899
+							    $sql_params[$attr . 'date'] = $date->minimumJulianDay();
1900
+						    }
1901
+						    if ($sortby == $match[1]) {
1902
+							    $sortby = "";
1903
+							    $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
1904
+						    }
1905
+						    unset($attrs[$attr]); // This filter has been fully processed
1906
+					    } elseif (preg_match('/^NAME CONTAINS (.*)$/', $value, $match)) {
1907
+						    // Do nothing, unless you have to
1908
+						    if ($match[1] != '' || $sortby == 'NAME') {
1909
+							    $sql_join .= " JOIN `##name` AS {$attr} ON (n_file=i_file AND n_id=i_id)";
1910
+							    // Search the DB only if there is any name supplied
1911
+							    if ($match[1] != "") {
1912
+								    $names = explode(" ", $match[1]);
1913
+								    foreach ($names as $n => $name) {
1914
+									    $sql_where .= " AND {$attr}.n_full LIKE CONCAT('%', :{$attr}name{$n}, '%')";
1915
+									    $sql_params[$attr . 'name' . $n] = $name;
1916
+								    }
1917
+							    }
1918
+							    // Let the DB do the name sorting even when no name was entered
1919
+							    if ($sortby == "NAME") {
1920
+								    $sortby = "";
1921
+								    $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.n_sort";
1922
+							    }
1923
+						    }
1924
+						    unset($attrs[$attr]); // This filter has been fully processed
1925
+					    } elseif (preg_match('/^REGEXP \/(.+)\//', $value, $match)) {
1926
+						    $sql_where .= " AND i_gedcom REGEXP :{$attr}gedcom";
1927
+						    // PDO helpfully escapes backslashes for us, preventing us from matching "\n1 FACT"
1928
+						    $sql_params[$attr . 'gedcom'] = str_replace('\n', "\n", $match[1]);
1929
+						    unset($attrs[$attr]); // This filter has been fully processed
1930
+					    } elseif (preg_match('/^(?:\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
1931
+						    $sql_join .= " JOIN `##places` AS {$attr}a ON ({$attr}a.p_file = i_file)";
1932
+						    $sql_join .= " JOIN `##placelinks` AS {$attr}b ON ({$attr}a.p_file = {$attr}b.pl_file AND {$attr}b.pl_p_id = {$attr}a.p_id AND {$attr}b.pl_gid = i_id)";
1933
+						    $sql_where .= " AND {$attr}a.p_place LIKE CONCAT('%', :{$attr}place, '%')";
1934
+						    $sql_params[$attr . 'place'] = $match[1];
1935
+						    // Don't unset this filter. This is just initial filtering
1936
+					    } elseif (preg_match('/^(\w*):*(\w*) CONTAINS (.+)$/', $value, $match)) {
1937
+						    $sql_where .= " AND i_gedcom LIKE CONCAT('%', :{$attr}contains1, '%', :{$attr}contains2, '%', :{$attr}contains3, '%')";
1938
+						    $sql_params[$attr . 'contains1'] = $match[1];
1939
+						    $sql_params[$attr . 'contains2'] = $match[2];
1940
+						    $sql_params[$attr . 'contains3'] = $match[3];
1941
+						    // Don't unset this filter. This is just initial filtering
1942
+					    }
1943
+				    }
1944
+			    }
1945
+
1946
+			    $this->list = array();
1947
+			    $rows       = Database::prepare(
1948
+				    $sql_select . $sql_join . $sql_where . $sql_order_by
1949
+			    )->execute($sql_params)->fetchAll();
1950
+
1951
+			    foreach ($rows as $row) {
1952
+				    $this->list[$row->xref] = Individual::getInstance($row->xref, $WT_TREE, $row->gedcom);
1953
+			    }
1954
+			    break;
1955
+
1956
+		    case 'family':
1957
+			    $sql_select   = "SELECT f_id AS xref, f_gedcom AS gedcom FROM `##families`";
1958
+			    $sql_join     = "";
1959
+			    $sql_where    = " WHERE f_file = :tree_id";
1960
+			    $sql_order_by = "";
1961
+			    $sql_params   = array('tree_id' => $WT_TREE->getTreeId());
1962
+			    foreach ($attrs as $attr => $value) {
1963
+				    if (strpos($attr, 'filter') === 0 && $value) {
1964
+					    $value = $this->substituteVars($value, false);
1965
+					    // Convert the various filters into SQL
1966
+					    if (preg_match('/^(\w+):DATE (LTE|GTE) (.+)$/', $value, $match)) {
1967
+						    $sql_join .= " JOIN `##dates` AS {$attr} ON ({$attr}.d_file=f_file AND {$attr}.d_gid=f_id)";
1968
+						    $sql_where .= " AND {$attr}.d_fact = :{$attr}fact";
1969
+						    $sql_params[$attr . 'fact'] = $match[1];
1970
+						    $date                       = new Date($match[3]);
1971
+						    if ($match[2] == "LTE") {
1972
+							    $sql_where .= " AND {$attr}.d_julianday2 <= :{$attr}date";
1973
+							    $sql_params[$attr . 'date'] = $date->maximumJulianDay();
1974
+						    } else {
1975
+							    $sql_where .= " AND {$attr}.d_julianday1 >= :{$attr}date";
1976
+							    $sql_params[$attr . 'date'] = $date->minimumJulianDay();
1977
+						    }
1978
+						    if ($sortby == $match[1]) {
1979
+							    $sortby = "";
1980
+							    $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.d_julianday1";
1981
+						    }
1982
+						    unset($attrs[$attr]); // This filter has been fully processed
1983
+					    } elseif (preg_match('/^REGEXP \/(.+)\//', $value, $match)) {
1984
+						    $sql_where .= " AND f_gedcom REGEXP :{$attr}gedcom";
1985
+						    // PDO helpfully escapes backslashes for us, preventing us from matching "\n1 FACT"
1986
+						    $sql_params[$attr . 'gedcom'] = str_replace('\n', "\n", $match[1]);
1987
+						    unset($attrs[$attr]); // This filter has been fully processed
1988
+					    } elseif (preg_match('/^NAME CONTAINS (.+)$/', $value, $match)) {
1989
+						    // Do nothing, unless you have to
1990
+						    if ($match[1] != '' || $sortby == 'NAME') {
1991
+							    $sql_join .= " JOIN `##name` AS {$attr} ON n_file = f_file AND n_id IN (f_husb, f_wife)";
1992
+							    // Search the DB only if there is any name supplied
1993
+							    if ($match[1] != "") {
1994
+								    $names = explode(" ", $match[1]);
1995
+								    foreach ($names as $n => $name) {
1996
+									    $sql_where .= " AND {$attr}.n_full LIKE CONCAT('%', :{$attr}name{$n}, '%')";
1997
+									    $sql_params[$attr . 'name' . $n] = $name;
1998
+								    }
1999
+							    }
2000
+							    // Let the DB do the name sorting even when no name was entered
2001
+							    if ($sortby == "NAME") {
2002
+								    $sortby = "";
2003
+								    $sql_order_by .= ($sql_order_by ? ", " : " ORDER BY ") . "{$attr}.n_sort";
2004
+							    }
2005
+						    }
2006
+						    unset($attrs[$attr]); // This filter has been fully processed
2007
+
2008
+					    } elseif (preg_match('/^(?:\w+):PLAC CONTAINS (.+)$/', $value, $match)) {
2009
+						    $sql_join .= " JOIN `##places` AS {$attr}a ON ({$attr}a.p_file=f_file)";
2010
+						    $sql_join .= " JOIN `##placelinks` AS {$attr}b ON ({$attr}a.p_file={$attr}b.pl_file AND {$attr}b.pl_p_id={$attr}a.p_id AND {$attr}b.pl_gid=f_id)";
2011
+						    $sql_where .= " AND {$attr}a.p_place LIKE CONCAT('%', :{$attr}place, '%')";
2012
+						    $sql_params[$attr . 'place'] = $match[1];
2013
+						    // Don't unset this filter. This is just initial filtering
2014
+					    } elseif (preg_match('/^(\w*):*(\w*) CONTAINS (.+)$/', $value, $match)) {
2015
+						    $sql_where .= " AND f_gedcom LIKE CONCAT('%', :{$attr}contains1, '%', :{$attr}contains2, '%', :{$attr}contains3, '%')";
2016
+						    $sql_params[$attr . 'contains1'] = $match[1];
2017
+						    $sql_params[$attr . 'contains2'] = $match[2];
2018
+						    $sql_params[$attr . 'contains3'] = $match[3];
2019
+						    // Don't unset this filter. This is just initial filtering
2020
+					    }
2021
+				    }
2022
+			    }
2023
+
2024
+			    $this->list = array();
2025
+			    $rows       = Database::prepare(
2026
+				    $sql_select . $sql_join . $sql_where . $sql_order_by
2027
+			    )->execute($sql_params)->fetchAll();
2028
+
2029
+			    foreach ($rows as $row) {
2030
+				    $this->list[$row->xref] = Family::getInstance($row->xref, $WT_TREE, $row->gedcom);
2031
+			    }
2032
+			    break;
2033
+
2034
+		    default:
2035
+			    throw new \DomainException('Invalid list name: ' . $listname);
2036 2036
 		}
2037 2037
 
2038 2038
 		$filters  = array();
@@ -2093,17 +2093,17 @@  discard block
 block discarded – undo
2093 2093
 								$searchstr = "1 " . $tag;
2094 2094
 							}
2095 2095
 							switch ($expr) {
2096
-							case "CONTAINS":
2097
-								if ($t == "PLAC") {
2098
-									$searchstr .= "[^\n]*[, ]*" . $val;
2099
-								} else {
2100
-									$searchstr .= "[^\n]*" . $val;
2101
-								}
2102
-								$filters[] = $searchstr;
2103
-								break;
2104
-							default:
2105
-								$filters2[] = array("tag" => $tag, "expr" => $expr, "val" => $val);
2106
-								break;
2096
+							    case "CONTAINS":
2097
+								    if ($t == "PLAC") {
2098
+									    $searchstr .= "[^\n]*[, ]*" . $val;
2099
+								    } else {
2100
+									    $searchstr .= "[^\n]*" . $val;
2101
+								    }
2102
+								    $filters[] = $searchstr;
2103
+								    break;
2104
+							    default:
2105
+								    $filters2[] = array("tag" => $tag, "expr" => $expr, "val" => $val);
2106
+								    break;
2107 2107
 							}
2108 2108
 						}
2109 2109
 					}
@@ -2147,31 +2147,31 @@  discard block
 block discarded – undo
2147 2147
 						}
2148 2148
 
2149 2149
 						switch ($expr) {
2150
-						case "GTE":
2151
-							if ($t == "DATE") {
2152
-								$date1 = new Date($v);
2153
-								$date2 = new Date($val);
2154
-								$keep  = (Date::compare($date1, $date2) >= 0);
2155
-							} elseif ($val >= $v) {
2156
-								$keep = true;
2157
-							}
2158
-							break;
2159
-						case "LTE":
2160
-							if ($t == "DATE") {
2161
-								$date1 = new Date($v);
2162
-								$date2 = new Date($val);
2163
-								$keep  = (Date::compare($date1, $date2) <= 0);
2164
-							} elseif ($val >= $v) {
2165
-								$keep = true;
2166
-							}
2167
-							break;
2168
-						default:
2169
-							if ($v == $val) {
2170
-								$keep = true;
2171
-							} else {
2172
-								$keep = false;
2173
-							}
2174
-							break;
2150
+						    case "GTE":
2151
+							    if ($t == "DATE") {
2152
+								    $date1 = new Date($v);
2153
+								    $date2 = new Date($val);
2154
+								    $keep  = (Date::compare($date1, $date2) >= 0);
2155
+							    } elseif ($val >= $v) {
2156
+								    $keep = true;
2157
+							    }
2158
+							    break;
2159
+						    case "LTE":
2160
+							    if ($t == "DATE") {
2161
+								    $date1 = new Date($v);
2162
+								    $date2 = new Date($val);
2163
+								    $keep  = (Date::compare($date1, $date2) <= 0);
2164
+							    } elseif ($val >= $v) {
2165
+								    $keep = true;
2166
+							    }
2167
+							    break;
2168
+						    default:
2169
+							    if ($v == $val) {
2170
+								    $keep = true;
2171
+							    } else {
2172
+								    $keep = false;
2173
+							    }
2174
+							    break;
2175 2175
 						}
2176 2176
 					}
2177 2177
 				}
@@ -2183,26 +2183,26 @@  discard block
 block discarded – undo
2183 2183
 		}
2184 2184
 
2185 2185
 		switch ($sortby) {
2186
-		case 'NAME':
2187
-			uasort($this->list, '\Fisharebest\Webtrees\GedcomRecord::compare');
2188
-			break;
2189
-		case 'CHAN':
2190
-			uasort($this->list, function (GedcomRecord $x, GedcomRecord $y) {
2191
-				return $y->lastChangeTimestamp(true) - $x->lastChangeTimestamp(true);
2192
-			});
2193
-			break;
2194
-		case 'BIRT:DATE':
2195
-			uasort($this->list, '\Fisharebest\Webtrees\Individual::compareBirthDate');
2196
-			break;
2197
-		case 'DEAT:DATE':
2198
-			uasort($this->list, '\Fisharebest\Webtrees\Individual::compareDeathDate');
2199
-			break;
2200
-		case 'MARR:DATE':
2201
-			uasort($this->list, '\Fisharebest\Webtrees\Family::compareMarrDate');
2202
-			break;
2203
-		default:
2204
-			// unsorted or already sorted by SQL
2205
-			break;
2186
+		    case 'NAME':
2187
+			    uasort($this->list, '\Fisharebest\Webtrees\GedcomRecord::compare');
2188
+			    break;
2189
+		    case 'CHAN':
2190
+			    uasort($this->list, function (GedcomRecord $x, GedcomRecord $y) {
2191
+				    return $y->lastChangeTimestamp(true) - $x->lastChangeTimestamp(true);
2192
+			    });
2193
+			    break;
2194
+		    case 'BIRT:DATE':
2195
+			    uasort($this->list, '\Fisharebest\Webtrees\Individual::compareBirthDate');
2196
+			    break;
2197
+		    case 'DEAT:DATE':
2198
+			    uasort($this->list, '\Fisharebest\Webtrees\Individual::compareDeathDate');
2199
+			    break;
2200
+		    case 'MARR:DATE':
2201
+			    uasort($this->list, '\Fisharebest\Webtrees\Family::compareMarrDate');
2202
+			    break;
2203
+		    default:
2204
+			    // unsorted or already sorted by SQL
2205
+			    break;
2206 2206
 		}
2207 2207
 
2208 2208
 		array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
@@ -2353,88 +2353,88 @@  discard block
 block discarded – undo
2353 2353
 		if (!empty($person)) {
2354 2354
 			$this->list[$id] = $person;
2355 2355
 			switch ($group) {
2356
-			case "child-family":
2357
-				foreach ($person->getChildFamilies() as $family) {
2358
-					$husband = $family->getHusband();
2359
-					$wife    = $family->getWife();
2360
-					if (!empty($husband)) {
2361
-						$this->list[$husband->getXref()] = $husband;
2362
-					}
2363
-					if (!empty($wife)) {
2364
-						$this->list[$wife->getXref()] = $wife;
2365
-					}
2366
-					$children = $family->getChildren();
2367
-					foreach ($children as $child) {
2368
-						if (!empty($child)) {
2369
-							$this->list[$child->getXref()] = $child;
2370
-						}
2371
-					}
2372
-				}
2373
-				break;
2374
-			case "spouse-family":
2375
-				foreach ($person->getSpouseFamilies() as $family) {
2376
-					$husband = $family->getHusband();
2377
-					$wife    = $family->getWife();
2378
-					if (!empty($husband)) {
2379
-						$this->list[$husband->getXref()] = $husband;
2380
-					}
2381
-					if (!empty($wife)) {
2382
-						$this->list[$wife->getXref()] = $wife;
2383
-					}
2384
-					$children = $family->getChildren();
2385
-					foreach ($children as $child) {
2386
-						if (!empty($child)) {
2387
-							$this->list[$child->getXref()] = $child;
2388
-						}
2389
-					}
2390
-				}
2391
-				break;
2392
-			case "direct-ancestors":
2393
-				$this->addAncestors($this->list, $id, false, $maxgen);
2394
-				break;
2395
-			case "ancestors":
2396
-				$this->addAncestors($this->list, $id, true, $maxgen);
2397
-				break;
2398
-			case "descendants":
2399
-				$this->list[$id]->generation = 1;
2400
-				$this->addDescendancy($this->list, $id, false, $maxgen);
2401
-				break;
2402
-			case "all":
2403
-				$this->addAncestors($this->list, $id, true, $maxgen);
2404
-				$this->addDescendancy($this->list, $id, true, $maxgen);
2405
-				break;
2356
+			    case "child-family":
2357
+				    foreach ($person->getChildFamilies() as $family) {
2358
+					    $husband = $family->getHusband();
2359
+					    $wife    = $family->getWife();
2360
+					    if (!empty($husband)) {
2361
+						    $this->list[$husband->getXref()] = $husband;
2362
+					    }
2363
+					    if (!empty($wife)) {
2364
+						    $this->list[$wife->getXref()] = $wife;
2365
+					    }
2366
+					    $children = $family->getChildren();
2367
+					    foreach ($children as $child) {
2368
+						    if (!empty($child)) {
2369
+							    $this->list[$child->getXref()] = $child;
2370
+						    }
2371
+					    }
2372
+				    }
2373
+				    break;
2374
+			    case "spouse-family":
2375
+				    foreach ($person->getSpouseFamilies() as $family) {
2376
+					    $husband = $family->getHusband();
2377
+					    $wife    = $family->getWife();
2378
+					    if (!empty($husband)) {
2379
+						    $this->list[$husband->getXref()] = $husband;
2380
+					    }
2381
+					    if (!empty($wife)) {
2382
+						    $this->list[$wife->getXref()] = $wife;
2383
+					    }
2384
+					    $children = $family->getChildren();
2385
+					    foreach ($children as $child) {
2386
+						    if (!empty($child)) {
2387
+							    $this->list[$child->getXref()] = $child;
2388
+						    }
2389
+					    }
2390
+				    }
2391
+				    break;
2392
+			    case "direct-ancestors":
2393
+				    $this->addAncestors($this->list, $id, false, $maxgen);
2394
+				    break;
2395
+			    case "ancestors":
2396
+				    $this->addAncestors($this->list, $id, true, $maxgen);
2397
+				    break;
2398
+			    case "descendants":
2399
+				    $this->list[$id]->generation = 1;
2400
+				    $this->addDescendancy($this->list, $id, false, $maxgen);
2401
+				    break;
2402
+			    case "all":
2403
+				    $this->addAncestors($this->list, $id, true, $maxgen);
2404
+				    $this->addDescendancy($this->list, $id, true, $maxgen);
2405
+				    break;
2406 2406
 			}
2407 2407
 		}
2408 2408
 
2409 2409
 		switch ($sortby) {
2410
-		case 'NAME':
2411
-			uasort($this->list, '\Fisharebest\Webtrees\GedcomRecord::compare');
2412
-			break;
2413
-		case 'BIRT:DATE':
2414
-			uasort($this->list, '\Fisharebest\Webtrees\Individual::compareBirthDate');
2415
-			break;
2416
-		case 'DEAT:DATE':
2417
-			uasort($this->list, '\Fisharebest\Webtrees\Individual::compareDeathDate');
2418
-			break;
2419
-		case 'generation':
2420
-			$newarray = array();
2421
-			reset($this->list);
2422
-			$genCounter = 1;
2423
-			while (count($newarray) < count($this->list)) {
2424
-				foreach ($this->list as $key => $value) {
2425
-					$this->generation = $value->generation;
2426
-					if ($this->generation == $genCounter) {
2427
-						$newarray[$key]             = new \stdClass;
2428
-						$newarray[$key]->generation = $this->generation;
2429
-					}
2430
-				}
2431
-				$genCounter++;
2432
-			}
2433
-			$this->list = $newarray;
2434
-			break;
2435
-		default:
2436
-			// unsorted
2437
-			break;
2410
+		    case 'NAME':
2411
+			    uasort($this->list, '\Fisharebest\Webtrees\GedcomRecord::compare');
2412
+			    break;
2413
+		    case 'BIRT:DATE':
2414
+			    uasort($this->list, '\Fisharebest\Webtrees\Individual::compareBirthDate');
2415
+			    break;
2416
+		    case 'DEAT:DATE':
2417
+			    uasort($this->list, '\Fisharebest\Webtrees\Individual::compareDeathDate');
2418
+			    break;
2419
+		    case 'generation':
2420
+			    $newarray = array();
2421
+			    reset($this->list);
2422
+			    $genCounter = 1;
2423
+			    while (count($newarray) < count($this->list)) {
2424
+				    foreach ($this->list as $key => $value) {
2425
+					    $this->generation = $value->generation;
2426
+					    if ($this->generation == $genCounter) {
2427
+						    $newarray[$key]             = new \stdClass;
2428
+						    $newarray[$key]->generation = $this->generation;
2429
+					    }
2430
+				    }
2431
+				    $genCounter++;
2432
+			    }
2433
+			    $this->list = $newarray;
2434
+			    break;
2435
+		    default:
2436
+			    // unsorted
2437
+			    break;
2438 2438
 		}
2439 2439
 		array_push($this->repeats_stack, array($this->repeats, $this->repeat_bytes));
2440 2440
 		$this->repeat_bytes = xml_get_current_line_number($this->parser) + 1;
Please login to merge, or discard this patch.
Braces   +124 added lines, -62 removed lines patch added patch discarded remove patch
@@ -34,7 +34,8 @@  discard block
 block discarded – undo
34 34
 /**
35 35
  * Class ReportParserGenerate - parse a report.xml file and generate the report.
36 36
  */
37
-class ReportParserGenerate extends ReportParserBase {
37
+class ReportParserGenerate extends ReportParserBase
38
+{
38 39
 	/** @var bool Are we collecting data from <Footnote> elements  */
39 40
 	private $process_footnote = true;
40 41
 
@@ -121,7 +122,8 @@  discard block
 block discarded – undo
121 122
 	 * @param ReportBase $report_root
122 123
 	 * @param string[][] $vars
123 124
 	 */
124
-	public function __construct($report, ReportBase $report_root = null, array $vars = array()) {
125
+	public function __construct($report, ReportBase $report_root = null, array $vars = array())
126
+	{
125 127
 		$this->report_root     = $report_root;
126 128
 		$this->wt_report       = $report_root;
127 129
 		$this->current_element = new ReportBaseElement;
@@ -139,7 +141,8 @@  discard block
 block discarded – undo
139 141
 	 * @param string   $name   the name of the XML element parsed
140 142
 	 * @param array    $attrs  an array of key value pairs for the attributes
141 143
 	 */
142
-	protected function startElement($parser, $name, $attrs) {
144
+	protected function startElement($parser, $name, $attrs)
145
+	{
143 146
 		$newattrs = array();
144 147
 
145 148
 		foreach ($attrs as $key => $value) {
@@ -171,7 +174,8 @@  discard block
 block discarded – undo
171 174
 	 * @param resource $parser the resource handler for the XML parser
172 175
 	 * @param string   $name   the name of the XML element parsed
173 176
 	 */
174
-	protected function endElement($parser, $name) {
177
+	protected function endElement($parser, $name)
178
+	{
175 179
 		if (($this->process_footnote || $name === "Footnote") && ($this->process_ifs === 0 || $name === "if") && ($this->process_gedcoms === 0 || $name === "Gedcom") && ($this->process_repeats === 0 || $name === "Facts" || $name === "RepeatTag" || $name === "List" || $name === "Relatives")) {
176 180
 			$start_method = $name . 'StartHandler';
177 181
 			$end_method   = $name . 'EndHandler';
@@ -189,7 +193,8 @@  discard block
 block discarded – undo
189 193
 	 * @param resource $parser the resource handler for the XML parser
190 194
 	 * @param string   $data   the name of the XML element parsed
191 195
 	 */
192
-	protected function characterData($parser, $data) {
196
+	protected function characterData($parser, $data)
197
+	{
193 198
 		if ($this->print_data && $this->process_gedcoms === 0 && $this->process_ifs === 0 && $this->process_repeats === 0) {
194 199
 			$this->current_element->addText($data);
195 200
 		}
@@ -200,7 +205,8 @@  discard block
 block discarded – undo
200 205
 	 *
201 206
 	 * @param array $attrs an array of key value pairs for the attributes
202 207
 	 */
203
-	private function styleStartHandler($attrs) {
208
+	private function styleStartHandler($attrs)
209
+	{
204 210
 		if (empty($attrs['name'])) {
205 211
 			throw new \DomainException('REPORT ERROR Style: The "name" of the style is missing or not set in the XML file.');
206 212
 		}
@@ -239,7 +245,8 @@  discard block
 block discarded – undo
239 245
 	 *
240 246
 	 * @param array $attrs an array of key value pairs for the attributes
241 247
 	 */
242
-	private function docStartHandler($attrs) {
248
+	private function docStartHandler($attrs)
249
+	{
243 250
 		$this->parser = $this->xml_parser;
244 251
 
245 252
 		// Custom page width
@@ -328,14 +335,16 @@  discard block
 block discarded – undo
328 335
 	/**
329 336
 	 * XML </Doc>
330 337
 	 */
331
-	private function docEndHandler() {
338
+	private function docEndHandler()
339
+	{
332 340
 		$this->wt_report->run();
333 341
 	}
334 342
 
335 343
 	/**
336 344
 	 * XML <Header>
337 345
 	 */
338
-	private function headerStartHandler() {
346
+	private function headerStartHandler()
347
+	{
339 348
 		// Clear the Header before any new elements are added
340 349
 		$this->wt_report->clearHeader();
341 350
 		$this->wt_report->setProcessing("H");
@@ -344,7 +353,8 @@  discard block
 block discarded – undo
344 353
 	/**
345 354
 	 * XML <PageHeader>
346 355
 	 */
347
-	private function pageHeaderStartHandler() {
356
+	private function pageHeaderStartHandler()
357
+	{
348 358
 		array_push($this->print_data_stack, $this->print_data);
349 359
 		$this->print_data = false;
350 360
 		array_push($this->wt_report_stack, $this->wt_report);
@@ -354,7 +364,8 @@  discard block
 block discarded – undo
354 364
 	/**
355 365
 	 * XML <pageHeaderEndHandler>
356 366
 	 */
357
-	private function pageHeaderEndHandler() {
367
+	private function pageHeaderEndHandler()
368
+	{
358 369
 		$this->print_data        = array_pop($this->print_data_stack);
359 370
 		$this->current_element   = $this->wt_report;
360 371
 		$this->wt_report         = array_pop($this->wt_report_stack);
@@ -364,14 +375,16 @@  discard block
 block discarded – undo
364 375
 	/**
365 376
 	 * XML <bodyStartHandler>
366 377
 	 */
367
-	private function bodyStartHandler() {
378
+	private function bodyStartHandler()
379
+	{
368 380
 		$this->wt_report->setProcessing("B");
369 381
 	}
370 382
 
371 383
 	/**
372 384
 	 * XML <footerStartHandler>
373 385
 	 */
374
-	private function footerStartHandler() {
386
+	private function footerStartHandler()
387
+	{
375 388
 		$this->wt_report->setProcessing("F");
376 389
 	}
377 390
 
@@ -380,7 +393,8 @@  discard block
 block discarded – undo
380 393
 	 *
381 394
 	 * @param array $attrs an array of key value pairs for the attributes
382 395
 	 */
383
-	private function cellStartHandler($attrs) {
396
+	private function cellStartHandler($attrs)
397
+	{
384 398
 		// string The text alignment of the text in this box.
385 399
 		$align = "";
386 400
 		if (!empty($attrs['align'])) {
@@ -534,7 +548,8 @@  discard block
 block discarded – undo
534 548
 	/**
535 549
 	 * XML </Cell>
536 550
 	 */
537
-	private function cellEndHandler() {
551
+	private function cellEndHandler()
552
+	{
538 553
 		$this->print_data = array_pop($this->print_data_stack);
539 554
 		$this->wt_report->addElement($this->current_element);
540 555
 	}
@@ -542,7 +557,8 @@  discard block
 block discarded – undo
542 557
 	/**
543 558
 	 * XML <Now /> element handler
544 559
 	 */
545
-	private function nowStartHandler() {
560
+	private function nowStartHandler()
561
+	{
546 562
 		$g = FunctionsDate::timestampToGedcomDate(WT_TIMESTAMP + WT_TIMESTAMP_OFFSET);
547 563
 		$this->current_element->addText($g->display());
548 564
 	}
@@ -550,14 +566,16 @@  discard block
 block discarded – undo
550 566
 	/**
551 567
 	 * XML <PageNum /> element handler
552 568
 	 */
553
-	private function pageNumStartHandler() {
569
+	private function pageNumStartHandler()
570
+	{
554 571
 		$this->current_element->addText("#PAGENUM#");
555 572
 	}
556 573
 
557 574
 	/**
558 575
 	 * XML <TotalPages /> element handler
559 576
 	 */
560
-	private function totalPagesStartHandler() {
577
+	private function totalPagesStartHandler()
578
+	{
561 579
 		$this->current_element->addText("{{:ptp:}}");
562 580
 	}
563 581
 
@@ -566,7 +584,8 @@  discard block
 block discarded – undo
566 584
 	 *
567 585
 	 * @param array $attrs an array of key value pairs for the attributes
568 586
 	 */
569
-	private function gedcomStartHandler($attrs) {
587
+	private function gedcomStartHandler($attrs)
588
+	{
570 589
 		global $WT_TREE;
571 590
 
572 591
 		if ($this->process_gedcoms > 0) {
@@ -629,7 +648,8 @@  discard block
 block discarded – undo
629 648
 	/**
630 649
 	 * Called at the end of an element.
631 650
 	 */
632
-	private function gedcomEndHandler() {
651
+	private function gedcomEndHandler()
652
+	{
633 653
 		if ($this->process_gedcoms > 0) {
634 654
 			$this->process_gedcoms--;
635 655
 		} else {
@@ -642,7 +662,8 @@  discard block
 block discarded – undo
642 662
 	 *
643 663
 	 * @param array $attrs an array of key value pairs for the attributes
644 664
 	 */
645
-	private function textBoxStartHandler($attrs) {
665
+	private function textBoxStartHandler($attrs)
666
+	{
646 667
 		// string Background color code
647 668
 		$bgcolor = "";
648 669
 		if (!empty($attrs['bgcolor'])) {
@@ -765,7 +786,8 @@  discard block
 block discarded – undo
765 786
 	/**
766 787
 	 * XML <textBoxEndHandler>
767 788
 	 */
768
-	private function textBoxEndHandler() {
789
+	private function textBoxEndHandler()
790
+	{
769 791
 		$this->print_data      = array_pop($this->print_data_stack);
770 792
 		$this->current_element = $this->wt_report;
771 793
 		$this->wt_report       = array_pop($this->wt_report_stack);
@@ -777,7 +799,8 @@  discard block
 block discarded – undo
777 799
 	 *
778 800
 	 * @param array $attrs an array of key value pairs for the attributes
779 801
 	 */
780
-	private function textStartHandler($attrs) {
802
+	private function textStartHandler($attrs)
803
+	{
781 804
 		array_push($this->print_data_stack, $this->print_data);
782 805
 		$this->print_data = true;
783 806
 
@@ -799,7 +822,8 @@  discard block
 block discarded – undo
799 822
 	/**
800 823
 	 * XML </Text>
801 824
 	 */
802
-	private function textEndHandler() {
825
+	private function textEndHandler()
826
+	{
803 827
 		$this->print_data = array_pop($this->print_data_stack);
804 828
 		$this->wt_report->addElement($this->current_element);
805 829
 	}
@@ -813,7 +837,8 @@  discard block
 block discarded – undo
813 837
 	 *
814 838
 	 * @param array $attrs an array of key value pairs for the attributes
815 839
 	 */
816
-	private function getPersonNameStartHandler($attrs) {
840
+	private function getPersonNameStartHandler($attrs)
841
+	{
817 842
 		global $WT_TREE;
818 843
 
819 844
 		$id    = "";
@@ -896,7 +921,8 @@  discard block
 block discarded – undo
896 921
 	 *
897 922
 	 * @param array $attrs an array of key value pairs for the attributes
898 923
 	 */
899
-	private function gedcomValueStartHandler($attrs) {
924
+	private function gedcomValueStartHandler($attrs)
925
+	{
900 926
 		global $WT_TREE;
901 927
 
902 928
 		$id    = "";
@@ -967,7 +993,8 @@  discard block
 block discarded – undo
967 993
 	 *
968 994
 	 * @param array $attrs an array of key value pairs for the attributes
969 995
 	 */
970
-	private function repeatTagStartHandler($attrs) {
996
+	private function repeatTagStartHandler($attrs)
997
+	{
971 998
 		global $WT_TREE;
972 999
 
973 1000
 		$this->process_repeats++;
@@ -1039,7 +1066,8 @@  discard block
 block discarded – undo
1039 1066
 	/**
1040 1067
 	 * XML </ RepeatTag>
1041 1068
 	 */
1042
-	private function repeatTagEndHandler() {
1069
+	private function repeatTagEndHandler()
1070
+	{
1043 1071
 		global $report;
1044 1072
 
1045 1073
 		$this->process_repeats--;
@@ -1118,7 +1146,8 @@  discard block
 block discarded – undo
1118 1146
 	 *
1119 1147
 	 * @param array $attrs an array of key value pairs for the attributes
1120 1148
 	 */
1121
-	private function varStartHandler($attrs) {
1149
+	private function varStartHandler($attrs)
1150
+	{
1122 1151
 		if (empty($attrs['var'])) {
1123 1152
 			throw new \DomainException('REPORT ERROR var: The attribute "var=" is missing or not set in the XML file on line: ' . xml_get_current_line_number($this->parser));
1124 1153
 		}
@@ -1159,7 +1188,8 @@  discard block
 block discarded – undo
1159 1188
 	 *
1160 1189
 	 * @param array $attrs an array of key value pairs for the attributes
1161 1190
 	 */
1162
-	private function factsStartHandler($attrs) {
1191
+	private function factsStartHandler($attrs)
1192
+	{
1163 1193
 		global $WT_TREE;
1164 1194
 
1165 1195
 		$this->process_repeats++;
@@ -1207,7 +1237,8 @@  discard block
 block discarded – undo
1207 1237
 	/**
1208 1238
 	 * XML </Facts>
1209 1239
 	 */
1210
-	private function factsEndHandler() {
1240
+	private function factsEndHandler()
1241
+	{
1211 1242
 		global $report;
1212 1243
 
1213 1244
 		$this->process_repeats--;
@@ -1290,7 +1321,8 @@  discard block
 block discarded – undo
1290 1321
 	 *
1291 1322
 	 * @param array $attrs an array of key value pairs for the attributes
1292 1323
 	 */
1293
-	private function setVarStartHandler($attrs) {
1324
+	private function setVarStartHandler($attrs)
1325
+	{
1294 1326
 		if (empty($attrs['name'])) {
1295 1327
 			throw new \DomainException('REPORT ERROR var: The attribute "name" is missing or not set in the XML file');
1296 1328
 		}
@@ -1364,7 +1396,8 @@  discard block
 block discarded – undo
1364 1396
 	 *
1365 1397
 	 * @param array $attrs an array of key value pairs for the attributes
1366 1398
 	 */
1367
-	private function ifStartHandler($attrs) {
1399
+	private function ifStartHandler($attrs)
1400
+	{
1368 1401
 		if ($this->process_ifs > 0) {
1369 1402
 			$this->process_ifs++;
1370 1403
 
@@ -1419,7 +1452,8 @@  discard block
 block discarded – undo
1419 1452
 	/**
1420 1453
 	 * XML <if /> end element
1421 1454
 	 */
1422
-	private function ifEndHandler() {
1455
+	private function ifEndHandler()
1456
+	{
1423 1457
 		if ($this->process_ifs > 0) {
1424 1458
 			$this->process_ifs--;
1425 1459
 		}
@@ -1432,7 +1466,8 @@  discard block
 block discarded – undo
1432 1466
 	 *
1433 1467
 	 * @param array $attrs an array of key value pairs for the attributes
1434 1468
 	 */
1435
-	private function footnoteStartHandler($attrs) {
1469
+	private function footnoteStartHandler($attrs)
1470
+	{
1436 1471
 		global $WT_TREE;
1437 1472
 
1438 1473
 		$id = "";
@@ -1459,7 +1494,8 @@  discard block
 block discarded – undo
1459 1494
 	 * XML <Footnote /> end element
1460 1495
 	 * Print the collected Footnote data
1461 1496
 	 */
1462
-	private function footnoteEndHandler() {
1497
+	private function footnoteEndHandler()
1498
+	{
1463 1499
 		if ($this->process_footnote) {
1464 1500
 			$this->print_data = array_pop($this->print_data_stack);
1465 1501
 			$temp             = trim($this->current_element->getValue());
@@ -1475,7 +1511,8 @@  discard block
 block discarded – undo
1475 1511
 	/**
1476 1512
 	 * XML <FootnoteTexts /> element
1477 1513
 	 */
1478
-	private function footnoteTextsStartHandler() {
1514
+	private function footnoteTextsStartHandler()
1515
+	{
1479 1516
 		$temp = "footnotetexts";
1480 1517
 		$this->wt_report->addElement($temp);
1481 1518
 	}
@@ -1483,7 +1520,8 @@  discard block
 block discarded – undo
1483 1520
 	/**
1484 1521
 	 * XML <AgeAtDeath /> element handler
1485 1522
 	 */
1486
-	private function ageAtDeathStartHandler() {
1523
+	private function ageAtDeathStartHandler()
1524
+	{
1487 1525
 		// This duplicates functionality in FunctionsPrint::format_fact_date()
1488 1526
 		global $factrec, $WT_TREE;
1489 1527
 
@@ -1540,7 +1578,8 @@  discard block
 block discarded – undo
1540 1578
 	/**
1541 1579
 	 * XML element Forced line break handler - HTML code
1542 1580
 	 */
1543
-	private function brStartHandler() {
1581
+	private function brStartHandler()
1582
+	{
1544 1583
 		if ($this->print_data && $this->process_gedcoms === 0) {
1545 1584
 			$this->current_element->addText('<br>');
1546 1585
 		}
@@ -1549,7 +1588,8 @@  discard block
 block discarded – undo
1549 1588
 	/**
1550 1589
 	 * XML <sp />element Forced space handler
1551 1590
 	 */
1552
-	private function spStartHandler() {
1591
+	private function spStartHandler()
1592
+	{
1553 1593
 		if ($this->print_data && $this->process_gedcoms === 0) {
1554 1594
 			$this->current_element->addText(' ');
1555 1595
 		}
@@ -1560,7 +1600,8 @@  discard block
 block discarded – undo
1560 1600
 	 *
1561 1601
 	 * @param array $attrs an array of key value pairs for the attributes
1562 1602
 	 */
1563
-	private function highlightedImageStartHandler($attrs) {
1603
+	private function highlightedImageStartHandler($attrs)
1604
+	{
1564 1605
 		global $WT_TREE;
1565 1606
 
1566 1607
 		$id    = '';
@@ -1661,7 +1702,8 @@  discard block
 block discarded – undo
1661 1702
 	 *
1662 1703
 	 * @param array $attrs an array of key value pairs for the attributes
1663 1704
 	 */
1664
-	private function imageStartHandler($attrs) {
1705
+	private function imageStartHandler($attrs)
1706
+	{
1665 1707
 		global $WT_TREE;
1666 1708
 
1667 1709
 		// mixed Position the top corner of this box on the page. the default is the current position
@@ -1778,7 +1820,8 @@  discard block
 block discarded – undo
1778 1820
 	 *
1779 1821
 	 * @param array $attrs an array of key value pairs for the attributes
1780 1822
 	 */
1781
-	private function lineStartHandler($attrs) {
1823
+	private function lineStartHandler($attrs)
1824
+	{
1782 1825
 		// Start horizontal position, current position (default)
1783 1826
 		$x1 = ".";
1784 1827
 		if (isset($attrs['x1'])) {
@@ -1833,7 +1876,8 @@  discard block
 block discarded – undo
1833 1876
 	 *
1834 1877
 	 * @param array $attrs an array of key value pairs for the attributes
1835 1878
 	 */
1836
-	private function listStartHandler($attrs) {
1879
+	private function listStartHandler($attrs)
1880
+	{
1837 1881
 		global $WT_TREE;
1838 1882
 
1839 1883
 		$this->process_repeats++;
@@ -2212,7 +2256,8 @@  discard block
 block discarded – undo
2212 2256
 	/**
2213 2257
 	 * XML <List>
2214 2258
 	 */
2215
-	private function listEndHandler() {
2259
+	private function listEndHandler()
2260
+	{
2216 2261
 		global $report;
2217 2262
 
2218 2263
 		$this->process_repeats--;
@@ -2291,7 +2336,8 @@  discard block
 block discarded – undo
2291 2336
 	 * The total number is collected from
2292 2337
 	 * List and Relatives
2293 2338
 	 */
2294
-	private function listTotalStartHandler() {
2339
+	private function listTotalStartHandler()
2340
+	{
2295 2341
 		if ($this->list_private == 0) {
2296 2342
 			$this->current_element->addText($this->list_total);
2297 2343
 		} else {
@@ -2304,7 +2350,8 @@  discard block
 block discarded – undo
2304 2350
 	 *
2305 2351
 	 * @param array $attrs an array of key value pairs for the attributes
2306 2352
 	 */
2307
-	private function relativesStartHandler($attrs) {
2353
+	private function relativesStartHandler($attrs)
2354
+	{
2308 2355
 		global $WT_TREE;
2309 2356
 
2310 2357
 		$this->process_repeats++;
@@ -2443,7 +2490,8 @@  discard block
 block discarded – undo
2443 2490
 	/**
2444 2491
 	 * XML </ Relatives>
2445 2492
 	 */
2446
-	private function relativesEndHandler() {
2493
+	private function relativesEndHandler()
2494
+	{
2447 2495
 		global $report, $WT_TREE;
2448 2496
 
2449 2497
 		$this->process_repeats--;
@@ -2518,7 +2566,8 @@  discard block
 block discarded – undo
2518 2566
 	 *
2519 2567
 	 * Prints the number of generations
2520 2568
 	 */
2521
-	private function generationStartHandler() {
2569
+	private function generationStartHandler()
2570
+	{
2522 2571
 		$this->current_element->addText($this->generation);
2523 2572
 	}
2524 2573
 
@@ -2527,7 +2576,8 @@  discard block
 block discarded – undo
2527 2576
 	 *
2528 2577
 	 * Has to be placed in an element (header, pageheader, body or footer)
2529 2578
 	 */
2530
-	private function newPageStartHandler() {
2579
+	private function newPageStartHandler()
2580
+	{
2531 2581
 		$temp = "addpage";
2532 2582
 		$this->wt_report->addElement($temp);
2533 2583
 	}
@@ -2538,7 +2588,8 @@  discard block
 block discarded – undo
2538 2588
 	 * @param string  $tag   HTML tag name
2539 2589
 	 * @param array[] $attrs an array of key value pairs for the attributes
2540 2590
 	 */
2541
-	private function htmlStartHandler($tag, $attrs) {
2591
+	private function htmlStartHandler($tag, $attrs)
2592
+	{
2542 2593
 		if ($tag === "tempdoc") {
2543 2594
 			return;
2544 2595
 		}
@@ -2555,7 +2606,8 @@  discard block
 block discarded – undo
2555 2606
 	 *
2556 2607
 	 * @param string $tag
2557 2608
 	 */
2558
-	private function htmlEndHandler($tag) {
2609
+	private function htmlEndHandler($tag)
2610
+	{
2559 2611
 		if ($tag === "tempdoc") {
2560 2612
 			return;
2561 2613
 		}
@@ -2573,42 +2625,48 @@  discard block
 block discarded – undo
2573 2625
 	/**
2574 2626
 	 * Handle <Input>
2575 2627
 	 */
2576
-	private function inputStartHandler() {
2628
+	private function inputStartHandler()
2629
+	{
2577 2630
 		// Dummy function, to prevent the default HtmlStartHandler() being called
2578 2631
 	}
2579 2632
 
2580 2633
 	/**
2581 2634
 	 * Handle </Input>
2582 2635
 	 */
2583
-	private function inputEndHandler() {
2636
+	private function inputEndHandler()
2637
+	{
2584 2638
 		// Dummy function, to prevent the default HtmlEndHandler() being called
2585 2639
 	}
2586 2640
 
2587 2641
 	/**
2588 2642
 	 * Handle <Report>
2589 2643
 	 */
2590
-	private function reportStartHandler() {
2644
+	private function reportStartHandler()
2645
+	{
2591 2646
 		// Dummy function, to prevent the default HtmlStartHandler() being called
2592 2647
 	}
2593 2648
 
2594 2649
 	/**
2595 2650
 	 * Handle </Report>
2596 2651
 	 */
2597
-	private function reportEndHandler() {
2652
+	private function reportEndHandler()
2653
+	{
2598 2654
 		// Dummy function, to prevent the default HtmlEndHandler() being called
2599 2655
 	}
2600 2656
 
2601 2657
 	/**
2602 2658
 	 * XML </titleEndHandler>
2603 2659
 	 */
2604
-	private function titleEndHandler() {
2660
+	private function titleEndHandler()
2661
+	{
2605 2662
 		$this->report_root->addTitle($this->text);
2606 2663
 	}
2607 2664
 
2608 2665
 	/**
2609 2666
 	 * XML </descriptionEndHandler>
2610 2667
 	 */
2611
-	private function descriptionEndHandler() {
2668
+	private function descriptionEndHandler()
2669
+	{
2612 2670
 		$this->report_root->addDescription($this->text);
2613 2671
 	}
2614 2672
 
@@ -2620,7 +2678,8 @@  discard block
 block discarded – undo
2620 2678
 	 * @param bool  $parents
2621 2679
 	 * @param int  $generations
2622 2680
 	 */
2623
-	private function addDescendancy(&$list, $pid, $parents = false, $generations = -1) {
2681
+	private function addDescendancy(&$list, $pid, $parents = false, $generations = -1)
2682
+	{
2624 2683
 		global $WT_TREE;
2625 2684
 
2626 2685
 		$person = Individual::getInstance($pid, $WT_TREE);
@@ -2681,7 +2740,8 @@  discard block
 block discarded – undo
2681 2740
 	 * @param bool  $children
2682 2741
 	 * @param int  $generations
2683 2742
 	 */
2684
-	private function addAncestors(&$list, $pid, $children = false, $generations = -1) {
2743
+	private function addAncestors(&$list, $pid, $children = false, $generations = -1)
2744
+	{
2685 2745
 		global $WT_TREE;
2686 2746
 
2687 2747
 		$genlist                = array($pid);
@@ -2734,7 +2794,8 @@  discard block
 block discarded – undo
2734 2794
 	 *
2735 2795
 	 * @return string the value of a gedcom tag from the given gedcom record
2736 2796
 	 */
2737
-	private function getGedcomValue($tag, $level, $gedrec) {
2797
+	private function getGedcomValue($tag, $level, $gedrec)
2798
+	{
2738 2799
 		global $WT_TREE;
2739 2800
 
2740 2801
 		if (empty($gedrec)) {
@@ -2810,7 +2871,8 @@  discard block
 block discarded – undo
2810 2871
 	 *
2811 2872
 	 * @return string
2812 2873
 	 */
2813
-	private function substituteVars($expression, $quote) {
2874
+	private function substituteVars($expression, $quote)
2875
+	{
2814 2876
 		$that = $this; // PHP5.3 cannot access $this inside a closure
2815 2877
 		return preg_replace_callback(
2816 2878
 			'/\$(\w+)/',
Please login to merge, or discard this patch.
app/GedcomCode/GedcomCodeTemp.php 4 patches
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -57,14 +57,14 @@
 block discarded – undo
57 57
 			'MADRI', 'MANAU', 'MANHA', 'MANIL', 'MANTI', 'MEDFO', 'MELBO', 'MEMPH',
58 58
 			'MERID', 'MEXIC', 'MNTVD', 'MONTE', 'MONTI', 'MONTR', 'MTIMP', 'NASHV',
59 59
 			'NAUV2', 'NAUVO', 'NBEAC', 'NUKUA', 'NYORK', 'NZEAL', 'OAKLA', 'OAXAC',
60
-			'OGDEN', 'OKLAH',	'OQUIR', 'ORLAN', 'PALEG', 'PALMY', 'PANAM', 'PAPEE',
61
-			'PAYSO', 'PERTH',	'PHOEN', 'POFFI', 'PORTL', 'PREST', 'PROCC', 'PROVO',
60
+			'OGDEN', 'OKLAH', 'OQUIR', 'ORLAN', 'PALEG', 'PALMY', 'PANAM', 'PAPEE',
61
+			'PAYSO', 'PERTH', 'PHOEN', 'POFFI', 'PORTL', 'PREST', 'PROCC', 'PROVO',
62 62
 			'QUETZ', 'RALEI', 'RECIF', 'REDLA', 'REGIN', 'RENO', 'REXBU', 'SACRA',
63
-			'SAMOA', 'SANTI',	'SANSA', 'SANTO', 'SDIEG', 'SDOMI', 'SEATT', 'SEOUL',
64
-			'SGEOR', 'SJOSE',	'SLAKE', 'SLOUI', 'SNOWF','SPAUL', 'SPMIN', 'SPOKA',
63
+			'SAMOA', 'SANTI', 'SANSA', 'SANTO', 'SDIEG', 'SDOMI', 'SEATT', 'SEOUL',
64
+			'SGEOR', 'SJOSE', 'SLAKE', 'SLOUI', 'SNOWF', 'SPAUL', 'SPMIN', 'SPOKA',
65 65
 			'STOCK', 'SUVA', 'SWISS', 'SYDNE', 'TAIPE', 'TAMPI', 'TEGUC', 'TGUTI',
66
-			'TIHUA', 'TOKYO',	'TORNO', 'TRUJI', 'TWINF', 'VANCO', 'VERAC', 'VERNA',
67
-			'VILLA', 'WASHI',	'WINTE',
66
+			'TIHUA', 'TOKYO', 'TORNO', 'TRUJI', 'TWINF', 'VANCO', 'VERAC', 'VERNA',
67
+			'VILLA', 'WASHI', 'WINTE',
68 68
 		);
69 69
 	}
70 70
 
Please login to merge, or discard this patch.
Indentation   +381 added lines, -381 removed lines patch added patch discarded remove patch
@@ -21,389 +21,389 @@
 block discarded – undo
21 21
  * Class GedcomCodeTemp - Functions and logic for GEDCOM "TEMP" codes
22 22
  */
23 23
 class GedcomCodeTemp {
24
-	/**
25
-	 * A list of GEDCOM tags that require a TEMP subtag
26
-	 *
27
-	 * @param string $tag
28
-	 *
29
-	 * @return bool
30
-	 */
31
-	public static function isTagLDS($tag) {
32
-		return $tag === 'BAPL' || $tag === 'CONL' || $tag === 'ENDL' || $tag === 'SLGC' || $tag === 'SLGS';
33
-	}
24
+    /**
25
+     * A list of GEDCOM tags that require a TEMP subtag
26
+     *
27
+     * @param string $tag
28
+     *
29
+     * @return bool
30
+     */
31
+    public static function isTagLDS($tag) {
32
+        return $tag === 'BAPL' || $tag === 'CONL' || $tag === 'ENDL' || $tag === 'SLGC' || $tag === 'SLGS';
33
+    }
34 34
 
35
-	/**
36
-	 * A list of all temple codes, from the GEDCOM 5.5.1 specification
37
-	 *
38
-	 * Note that this list is out-of-date. We could add recently built
39
-	 * temples, but what codes would we use?
40
-	 *
41
-	 * @link http://en.wikipedia.org/wiki/List_of_temples_of_The_Church_of_Jesus_Christ_of_Latter-day_Saints
42
-	 * @link http://www.ldschurchtemples.com/codes/
43
-	 *
44
-	 * @return string[]
45
-	 */
46
-	public static function templeCodes() {
47
-		return array(
48
-			'ABA', 'ACCRA', 'ADELA', 'ALBER', 'ALBUQ', 'ANCHO', 'ARIZO', 'ASUNC',
49
-			'ATLAN', 'BAIRE', 'BILLI', 'BIRMI', 'BISMA', 'BOGOT', 'BOISE', 'BOSTO',
50
-			'BOUNT', 'BRIGH', 'BRISB', 'BROUG', 'CALGA', 'CAMPI', 'CARAC', 'CEBUP',
51
-			'CHICA', 'CIUJU', 'COCHA', 'COLJU', 'COLSC', 'COLUM', 'COPEN', 'CORDO',
52
-			'CRIVE', 'CURIT', 'DALLA', 'DENVE', 'DETRO', 'DRAPE', 'EDMON', 'EHOUS',
53
-			'FORTL', 'FRANK', 'FREIB', 'FRESN', 'FUKUO', 'GILAV', 'GILBE', 'GUADA',
54
-			'GUATE', 'GUAYA', 'HAGUE', 'HALIF', 'HARTF', 'HAWAI', 'HELSI', 'HERMO',
55
-			'HKONG', 'HOUST', 'IFALL', 'INDIA', 'JOHAN', 'JRIVE', 'KANSA', 'KONA',
56
-			'KYIV', 'LANGE', 'LIMA', 'LOGAN', 'LONDO', 'LOUIS', 'LUBBO', 'LVEGA',
57
-			'MADRI', 'MANAU', 'MANHA', 'MANIL', 'MANTI', 'MEDFO', 'MELBO', 'MEMPH',
58
-			'MERID', 'MEXIC', 'MNTVD', 'MONTE', 'MONTI', 'MONTR', 'MTIMP', 'NASHV',
59
-			'NAUV2', 'NAUVO', 'NBEAC', 'NUKUA', 'NYORK', 'NZEAL', 'OAKLA', 'OAXAC',
60
-			'OGDEN', 'OKLAH',	'OQUIR', 'ORLAN', 'PALEG', 'PALMY', 'PANAM', 'PAPEE',
61
-			'PAYSO', 'PERTH',	'PHOEN', 'POFFI', 'PORTL', 'PREST', 'PROCC', 'PROVO',
62
-			'QUETZ', 'RALEI', 'RECIF', 'REDLA', 'REGIN', 'RENO', 'REXBU', 'SACRA',
63
-			'SAMOA', 'SANTI',	'SANSA', 'SANTO', 'SDIEG', 'SDOMI', 'SEATT', 'SEOUL',
64
-			'SGEOR', 'SJOSE',	'SLAKE', 'SLOUI', 'SNOWF','SPAUL', 'SPMIN', 'SPOKA',
65
-			'STOCK', 'SUVA', 'SWISS', 'SYDNE', 'TAIPE', 'TAMPI', 'TEGUC', 'TGUTI',
66
-			'TIHUA', 'TOKYO',	'TORNO', 'TRUJI', 'TWINF', 'VANCO', 'VERAC', 'VERNA',
67
-			'VILLA', 'WASHI',	'WINTE',
68
-		);
69
-	}
35
+    /**
36
+     * A list of all temple codes, from the GEDCOM 5.5.1 specification
37
+     *
38
+     * Note that this list is out-of-date. We could add recently built
39
+     * temples, but what codes would we use?
40
+     *
41
+     * @link http://en.wikipedia.org/wiki/List_of_temples_of_The_Church_of_Jesus_Christ_of_Latter-day_Saints
42
+     * @link http://www.ldschurchtemples.com/codes/
43
+     *
44
+     * @return string[]
45
+     */
46
+    public static function templeCodes() {
47
+        return array(
48
+            'ABA', 'ACCRA', 'ADELA', 'ALBER', 'ALBUQ', 'ANCHO', 'ARIZO', 'ASUNC',
49
+            'ATLAN', 'BAIRE', 'BILLI', 'BIRMI', 'BISMA', 'BOGOT', 'BOISE', 'BOSTO',
50
+            'BOUNT', 'BRIGH', 'BRISB', 'BROUG', 'CALGA', 'CAMPI', 'CARAC', 'CEBUP',
51
+            'CHICA', 'CIUJU', 'COCHA', 'COLJU', 'COLSC', 'COLUM', 'COPEN', 'CORDO',
52
+            'CRIVE', 'CURIT', 'DALLA', 'DENVE', 'DETRO', 'DRAPE', 'EDMON', 'EHOUS',
53
+            'FORTL', 'FRANK', 'FREIB', 'FRESN', 'FUKUO', 'GILAV', 'GILBE', 'GUADA',
54
+            'GUATE', 'GUAYA', 'HAGUE', 'HALIF', 'HARTF', 'HAWAI', 'HELSI', 'HERMO',
55
+            'HKONG', 'HOUST', 'IFALL', 'INDIA', 'JOHAN', 'JRIVE', 'KANSA', 'KONA',
56
+            'KYIV', 'LANGE', 'LIMA', 'LOGAN', 'LONDO', 'LOUIS', 'LUBBO', 'LVEGA',
57
+            'MADRI', 'MANAU', 'MANHA', 'MANIL', 'MANTI', 'MEDFO', 'MELBO', 'MEMPH',
58
+            'MERID', 'MEXIC', 'MNTVD', 'MONTE', 'MONTI', 'MONTR', 'MTIMP', 'NASHV',
59
+            'NAUV2', 'NAUVO', 'NBEAC', 'NUKUA', 'NYORK', 'NZEAL', 'OAKLA', 'OAXAC',
60
+            'OGDEN', 'OKLAH',	'OQUIR', 'ORLAN', 'PALEG', 'PALMY', 'PANAM', 'PAPEE',
61
+            'PAYSO', 'PERTH',	'PHOEN', 'POFFI', 'PORTL', 'PREST', 'PROCC', 'PROVO',
62
+            'QUETZ', 'RALEI', 'RECIF', 'REDLA', 'REGIN', 'RENO', 'REXBU', 'SACRA',
63
+            'SAMOA', 'SANTI',	'SANSA', 'SANTO', 'SDIEG', 'SDOMI', 'SEATT', 'SEOUL',
64
+            'SGEOR', 'SJOSE',	'SLAKE', 'SLOUI', 'SNOWF','SPAUL', 'SPMIN', 'SPOKA',
65
+            'STOCK', 'SUVA', 'SWISS', 'SYDNE', 'TAIPE', 'TAMPI', 'TEGUC', 'TGUTI',
66
+            'TIHUA', 'TOKYO',	'TORNO', 'TRUJI', 'TWINF', 'VANCO', 'VERAC', 'VERNA',
67
+            'VILLA', 'WASHI',	'WINTE',
68
+        );
69
+    }
70 70
 
71
-	/**
72
-	 * Get the localized name for a temple code
73
-	 *
74
-	 * @param string $temple_code
75
-	 *
76
-	 * @return string
77
-	 */
78
-	public static function templeName($temple_code) {
79
-		switch ($temple_code) {
80
-		case 'ABA':
81
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Aba, Nigeria');
82
-		case 'ACCRA':
83
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Accra, Ghana');
84
-		case 'ADELA':
85
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Adelaide, Australia');
86
-		case 'ALBER':
87
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Cardston, Alberta, Canada');
88
-		case 'ALBUQ':
89
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Albuquerque, New Mexico, United States');
90
-		case 'ANCHO':
91
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Anchorage, Alaska, United States');
92
-		case 'APIA':
93
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Apia, Samoa');
94
-		case 'ARIZO':
95
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Mesa, Arizona, United States');
96
-		case 'ASUNC':
97
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Asuncion, Paraguay');
98
-		case 'ATLAN':
99
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Atlanta, Georgia, United States');
100
-		case 'BAIRE':
101
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Buenos Aires, Argentina');
102
-		case 'BILLI':
103
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Billings, Montana, United States');
104
-		case 'BIRMI':
105
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Birmingham, Alabama, United States');
106
-		case 'BISMA':
107
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Bismarck, North Dakota, United States');
108
-		case 'BOGOT':
109
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Bogota, Colombia');
110
-		case 'BOISE':
111
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Boise, Idaho, United States');
112
-		case 'BOSTO':
113
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Boston, Massachusetts, United States');
114
-		case 'BOUNT':
115
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Bountiful, Utah, United States');
116
-		case 'BRIGH':
117
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Brigham City, Utah, United States');
118
-		case 'BRISB':
119
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Brisbane, Australia');
120
-		case 'BROUG':
121
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Baton Rouge, Louisiana, United States');
122
-		case 'CALGA':
123
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Calgary, Alberta, Canada');
124
-		case 'CAMPI':
125
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Campinas, Brazil');
126
-		case 'CARAC':
127
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Caracas, Venezuela');
128
-		case 'CEBUP':
129
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Cebu City, Philippines');
130
-		case 'CHICA':
131
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Chicago, Illinois, United States');
132
-		case 'CIUJU':
133
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Ciudad Juarez, Mexico');
134
-		case 'COCHA':
135
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Cochabamba, Bolivia');
136
-		case 'COLJU':
137
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Colonia Juarez, Mexico');
138
-		case 'COLSC':
139
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Columbia, South Carolina, United States');
140
-		case 'COLUM':
141
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Columbus, Ohio, United States');
142
-		case 'COPEN':
143
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Copenhagen, Denmark');
144
-		case 'CORDO':
145
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Cordoba, Argentina');
146
-		case 'CRIVE':
147
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Columbia River, Washington, United States');
148
-		case 'CURIT':
149
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Curitiba, Brazil');
150
-		case 'DALLA':
151
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Dallas, Texas, United States');
152
-		case 'DENVE':
153
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Denver, Colorado, United States');
154
-		case 'DETRO':
155
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Detroit, Michigan, United States');
156
-		case 'DRAPE':
157
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Draper, Utah, United States');
158
-		case 'EDMON':
159
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Edmonton, Alberta, Canada');
160
-		case 'EHOUS':
161
-			return /* I18N: Location of an historic LDS church temple - http://en.wikipedia.org/wiki/Endowment_house */ I18N::translate('Endowment House');
162
-		case 'FORTL':
163
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Fort Lauderdale, Florida, United States');
164
-		case 'FRANK':
165
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Frankfurt am Main, Germany');
166
-		case 'FREIB':
167
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Freiburg, Germany');
168
-		case 'FRESN':
169
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Fresno, California, United States');
170
-		case 'FUKUO':
171
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Fukuoka, Japan');
172
-		case 'GILAV':
173
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Gila Valley, Arizona, United States');
174
-		case 'GILBE':
175
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Gilbert, Arizona, United States');
176
-		case 'GUADA':
177
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Guadalajara, Mexico');
178
-		case 'GUATE':
179
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Guatemala City, Guatemala');
180
-		case 'GUAYA':
181
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Guayaquil, Ecuador');
182
-		case 'HAGUE':
183
-			return /* I18N: Location of an LDS church temple */ I18N::translate('The Hague, Netherlands');
184
-		case 'HALIF':
185
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Halifax, Nova Scotia, Canada');
186
-		case 'HARTF':
187
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Hartford, Connecticut, United States');
188
-		case 'HAWAI':
189
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Laie, Hawaii, United States');
190
-		case 'HELSI':
191
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Helsinki, Finland');
192
-		case 'HERMO':
193
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Hermosillo, Mexico');
194
-		case 'HKONG':
195
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Hong Kong');
196
-		case 'HOUST':
197
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Houston, Texas, United States');
198
-		case 'IFALL':
199
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Idaho Falls, Idaho, United States');
200
-		case 'INDIA':
201
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Indianapolis, Indiana, United States');
202
-		case 'JOHAN':
203
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Johannesburg, South Africa');
204
-		case 'JRIVE':
205
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Jordan River, Utah, United States');
206
-		case 'KANSA':
207
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Kansas City, Missouri, United States');
208
-		case 'KONA':
209
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Kona, Hawaii, United States');
210
-		case 'KYIV':
211
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Kiev, Ukraine');
212
-		case 'LANGE':
213
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Los Angeles, California, United States');
214
-		case 'LIMA':
215
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Lima, Peru');
216
-		case 'LOGAN':
217
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Logan, Utah, United States');
218
-		case 'LONDO':
219
-			return /* I18N: Location of an LDS church temple */ I18N::translate('London, England');
220
-		case 'LOUIS':
221
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Louisville, Kentucky, United States');
222
-		case 'LUBBO':
223
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Lubbock, Texas, United States');
224
-		case 'LVEGA':
225
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Las Vegas, Nevada, United States');
226
-		case 'MADRI':
227
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Madrid, Spain');
228
-		case 'MANAU':
229
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Manaus, Brazil');
230
-		case 'MANHA':
231
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Manhattan, New York, United States');
232
-		case 'MANIL':
233
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Manila, Philippines');
234
-		case 'MANTI':
235
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Manti, Utah, United States');
236
-		case 'MEDFO':
237
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Medford, Oregon, United States');
238
-		case 'MELBO':
239
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Melbourne, Australia');
240
-		case 'MEMPH':
241
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Memphis, Tennessee, United States');
242
-		case 'MERID':
243
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Merida, Mexico');
244
-		case 'MEXIC':
245
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Mexico City, Mexico');
246
-		case 'MNTVD':
247
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Montevideo, Uruguay');
248
-		case 'MONTE':
249
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Monterrey, Mexico');
250
-		case 'MONTI':
251
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Monticello, Utah, United States');
252
-		case 'MONTR':
253
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Montreal, Quebec, Canada');
254
-		case 'MTIMP':
255
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Mount Timpanogos, Utah, United States');
256
-		case 'NASHV':
257
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Nashville, Tennessee, United States');
258
-		case 'NAUV2':
259
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Nauvoo (new), Illinois, United States');
260
-		case 'NAUVO':
261
-			return /* I18N: Location of an historic LDS church temple */ I18N::translate('Nauvoo (original), Illinois, United States');
262
-		case 'NBEAC':
263
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Newport Beach, California, United States');
264
-		case 'NUKUA':
265
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Nuku’Alofa, Tonga');
266
-		case 'NYORK':
267
-			return /* I18N: Location of an historic LDS church temple */ I18N::translate('New York, New York, United States');
268
-		case 'NZEAL':
269
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Hamilton, New Zealand');
270
-		case 'OAKLA':
271
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Oakland, California, United States');
272
-		case 'OAXAC':
273
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Oaxaca, Mexico');
274
-		case 'OGDEN':
275
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Ogden, Utah, United States');
276
-		case 'OKLAH':
277
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Oklahoma City, Oklahoma, United States');
278
-		case 'OQUIR':
279
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Oquirrh Mountain, Utah, United States');
280
-		case 'ORLAN':
281
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Orlando, Florida, United States');
282
-		case 'PALEG':
283
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Porto Alegre, Brazil');
284
-		case 'PALMY':
285
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Palmyra, New York, United States');
286
-		case 'PANAM':
287
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Panama City, Panama');
288
-		case 'PAPEE':
289
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Papeete, Tahiti');
290
-		case 'PAYSO':
291
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Payson, Utah, United States');
292
-		case 'PERTH':
293
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Perth, Australia');
294
-		case 'PHOEN':
295
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Phoenix, Arizona, United States');
296
-		case 'POFFI':
297
-			return /* I18N: I18N: Location of an historic LDS church temple - http://en.wikipedia.org/wiki/President_of_the_Church */ I18N::translate('President’s Office');
298
-		case 'PORTL':
299
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Portland, Oregon, United States');
300
-		case 'PREST':
301
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Preston, England');
302
-		case 'PROCC':
303
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Provo City Center, Utah, United States');
304
-		case 'PROVO':
305
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Provo, Utah, United States');
306
-		case 'QUETZ':
307
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Quetzaltenango, Guatemala');
308
-		case 'RALEI':
309
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Raleigh, North Carolina, United States');
310
-		case 'RECIF':
311
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Recife, Brazil');
312
-		case 'REDLA':
313
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Redlands, California, United States');
314
-		case 'REGIN':
315
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Regina, Saskatchewan, Canada');
316
-		case 'RENO':
317
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Reno, Nevada, United States');
318
-		case 'REXBU':
319
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Rexburg, Idaho, United States');
320
-		case 'SACRA':
321
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Sacramento, California, United States');
322
-		case 'SANSA':
323
-			return /* I18N: Location of an LDS church temple */ I18N::translate('San Salvador, El Salvador');
324
-		case 'SANTI':
325
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Santiago, Chile');
326
-		case 'SANTO':
327
-			return /* I18N: Location of an LDS church temple */ I18N::translate('San Antonio, Texas, United States');
328
-		case 'SDIEG':
329
-			return /* I18N: Location of an LDS church temple */ I18N::translate('San Diego, California, United States');
330
-		case 'SDOMI':
331
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Santo Domingo, Dominican Republic');
332
-		case 'SEATT':
333
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Seattle, Washington, United States');
334
-		case 'SEOUL':
335
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Seoul, Korea');
336
-		case 'SGEOR':
337
-			return /* I18N: Location of an LDS church temple */ I18N::translate('St. George, Utah, United States');
338
-		case 'SJOSE':
339
-			return /* I18N: Location of an LDS church temple */ I18N::translate('San Jose, Costa Rica');
340
-		case 'SLAKE':
341
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Salt Lake City, Utah, United States');
342
-		case 'SLOUI':
343
-			return /* I18N: Location of an LDS church temple */ I18N::translate('St. Louis, Missouri, United States');
344
-		case 'SNOWF':
345
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Snowflake, Arizona, United States');
346
-		case 'SPAUL':
347
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Sao Paulo, Brazil');
348
-		case 'SPMIN':
349
-			return /* I18N: Location of an LDS church temple */ I18N::translate('St. Paul, Minnesota, United States');
350
-		case 'SPOKA':
351
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Spokane, Washington, United States');
352
-		case 'STOCK':
353
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Stockholm, Sweden');
354
-		case 'SUVA':
355
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Suva, Fiji');
356
-		case 'SWISS':
357
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Bern, Switzerland');
358
-		case 'SYDNE':
359
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Sydney, Australia');
360
-		case 'TAIPE':
361
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Taipei, Taiwan');
362
-		case 'TAMPI':
363
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Tampico, Mexico');
364
-		case 'TEGUC':
365
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Tegucigalpa, Honduras');
366
-		case 'TGUTI':
367
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Tuxtla Gutierrez, Mexico');
368
-		case 'TIJUA':
369
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Tijuana, Mexico');
370
-		case 'TOKYO':
371
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Tokyo, Japan');
372
-		case 'TORNO':
373
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Toronto, Ontario, Canada');
374
-		case 'TRUJI':
375
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Trujillo, Peru');
376
-		case 'TWINF':
377
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Twin Falls, Idaho, United States');
378
-		case 'VANCO':
379
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Vancouver, British Columbia, Canada');
380
-		case 'VERAC':
381
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Veracruz, Mexico');
382
-		case 'VERNA':
383
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Vernal, Utah, United States');
384
-		case 'VILLA':
385
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Villa Hermosa, Mexico');
386
-		case 'WASHI':
387
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Washington, District of Columbia, United States');
388
-		case 'WINTE':
389
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Winter Quarters, Nebraska, United States');
390
-		default:
391
-			return $temple_code;
392
-		}
393
-	}
71
+    /**
72
+     * Get the localized name for a temple code
73
+     *
74
+     * @param string $temple_code
75
+     *
76
+     * @return string
77
+     */
78
+    public static function templeName($temple_code) {
79
+        switch ($temple_code) {
80
+        case 'ABA':
81
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Aba, Nigeria');
82
+        case 'ACCRA':
83
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Accra, Ghana');
84
+        case 'ADELA':
85
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Adelaide, Australia');
86
+        case 'ALBER':
87
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Cardston, Alberta, Canada');
88
+        case 'ALBUQ':
89
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Albuquerque, New Mexico, United States');
90
+        case 'ANCHO':
91
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Anchorage, Alaska, United States');
92
+        case 'APIA':
93
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Apia, Samoa');
94
+        case 'ARIZO':
95
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Mesa, Arizona, United States');
96
+        case 'ASUNC':
97
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Asuncion, Paraguay');
98
+        case 'ATLAN':
99
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Atlanta, Georgia, United States');
100
+        case 'BAIRE':
101
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Buenos Aires, Argentina');
102
+        case 'BILLI':
103
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Billings, Montana, United States');
104
+        case 'BIRMI':
105
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Birmingham, Alabama, United States');
106
+        case 'BISMA':
107
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Bismarck, North Dakota, United States');
108
+        case 'BOGOT':
109
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Bogota, Colombia');
110
+        case 'BOISE':
111
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Boise, Idaho, United States');
112
+        case 'BOSTO':
113
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Boston, Massachusetts, United States');
114
+        case 'BOUNT':
115
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Bountiful, Utah, United States');
116
+        case 'BRIGH':
117
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Brigham City, Utah, United States');
118
+        case 'BRISB':
119
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Brisbane, Australia');
120
+        case 'BROUG':
121
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Baton Rouge, Louisiana, United States');
122
+        case 'CALGA':
123
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Calgary, Alberta, Canada');
124
+        case 'CAMPI':
125
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Campinas, Brazil');
126
+        case 'CARAC':
127
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Caracas, Venezuela');
128
+        case 'CEBUP':
129
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Cebu City, Philippines');
130
+        case 'CHICA':
131
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Chicago, Illinois, United States');
132
+        case 'CIUJU':
133
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Ciudad Juarez, Mexico');
134
+        case 'COCHA':
135
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Cochabamba, Bolivia');
136
+        case 'COLJU':
137
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Colonia Juarez, Mexico');
138
+        case 'COLSC':
139
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Columbia, South Carolina, United States');
140
+        case 'COLUM':
141
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Columbus, Ohio, United States');
142
+        case 'COPEN':
143
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Copenhagen, Denmark');
144
+        case 'CORDO':
145
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Cordoba, Argentina');
146
+        case 'CRIVE':
147
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Columbia River, Washington, United States');
148
+        case 'CURIT':
149
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Curitiba, Brazil');
150
+        case 'DALLA':
151
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Dallas, Texas, United States');
152
+        case 'DENVE':
153
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Denver, Colorado, United States');
154
+        case 'DETRO':
155
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Detroit, Michigan, United States');
156
+        case 'DRAPE':
157
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Draper, Utah, United States');
158
+        case 'EDMON':
159
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Edmonton, Alberta, Canada');
160
+        case 'EHOUS':
161
+            return /* I18N: Location of an historic LDS church temple - http://en.wikipedia.org/wiki/Endowment_house */ I18N::translate('Endowment House');
162
+        case 'FORTL':
163
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Fort Lauderdale, Florida, United States');
164
+        case 'FRANK':
165
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Frankfurt am Main, Germany');
166
+        case 'FREIB':
167
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Freiburg, Germany');
168
+        case 'FRESN':
169
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Fresno, California, United States');
170
+        case 'FUKUO':
171
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Fukuoka, Japan');
172
+        case 'GILAV':
173
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Gila Valley, Arizona, United States');
174
+        case 'GILBE':
175
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Gilbert, Arizona, United States');
176
+        case 'GUADA':
177
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Guadalajara, Mexico');
178
+        case 'GUATE':
179
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Guatemala City, Guatemala');
180
+        case 'GUAYA':
181
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Guayaquil, Ecuador');
182
+        case 'HAGUE':
183
+            return /* I18N: Location of an LDS church temple */ I18N::translate('The Hague, Netherlands');
184
+        case 'HALIF':
185
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Halifax, Nova Scotia, Canada');
186
+        case 'HARTF':
187
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Hartford, Connecticut, United States');
188
+        case 'HAWAI':
189
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Laie, Hawaii, United States');
190
+        case 'HELSI':
191
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Helsinki, Finland');
192
+        case 'HERMO':
193
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Hermosillo, Mexico');
194
+        case 'HKONG':
195
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Hong Kong');
196
+        case 'HOUST':
197
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Houston, Texas, United States');
198
+        case 'IFALL':
199
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Idaho Falls, Idaho, United States');
200
+        case 'INDIA':
201
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Indianapolis, Indiana, United States');
202
+        case 'JOHAN':
203
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Johannesburg, South Africa');
204
+        case 'JRIVE':
205
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Jordan River, Utah, United States');
206
+        case 'KANSA':
207
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Kansas City, Missouri, United States');
208
+        case 'KONA':
209
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Kona, Hawaii, United States');
210
+        case 'KYIV':
211
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Kiev, Ukraine');
212
+        case 'LANGE':
213
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Los Angeles, California, United States');
214
+        case 'LIMA':
215
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Lima, Peru');
216
+        case 'LOGAN':
217
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Logan, Utah, United States');
218
+        case 'LONDO':
219
+            return /* I18N: Location of an LDS church temple */ I18N::translate('London, England');
220
+        case 'LOUIS':
221
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Louisville, Kentucky, United States');
222
+        case 'LUBBO':
223
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Lubbock, Texas, United States');
224
+        case 'LVEGA':
225
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Las Vegas, Nevada, United States');
226
+        case 'MADRI':
227
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Madrid, Spain');
228
+        case 'MANAU':
229
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Manaus, Brazil');
230
+        case 'MANHA':
231
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Manhattan, New York, United States');
232
+        case 'MANIL':
233
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Manila, Philippines');
234
+        case 'MANTI':
235
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Manti, Utah, United States');
236
+        case 'MEDFO':
237
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Medford, Oregon, United States');
238
+        case 'MELBO':
239
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Melbourne, Australia');
240
+        case 'MEMPH':
241
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Memphis, Tennessee, United States');
242
+        case 'MERID':
243
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Merida, Mexico');
244
+        case 'MEXIC':
245
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Mexico City, Mexico');
246
+        case 'MNTVD':
247
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Montevideo, Uruguay');
248
+        case 'MONTE':
249
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Monterrey, Mexico');
250
+        case 'MONTI':
251
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Monticello, Utah, United States');
252
+        case 'MONTR':
253
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Montreal, Quebec, Canada');
254
+        case 'MTIMP':
255
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Mount Timpanogos, Utah, United States');
256
+        case 'NASHV':
257
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Nashville, Tennessee, United States');
258
+        case 'NAUV2':
259
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Nauvoo (new), Illinois, United States');
260
+        case 'NAUVO':
261
+            return /* I18N: Location of an historic LDS church temple */ I18N::translate('Nauvoo (original), Illinois, United States');
262
+        case 'NBEAC':
263
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Newport Beach, California, United States');
264
+        case 'NUKUA':
265
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Nuku’Alofa, Tonga');
266
+        case 'NYORK':
267
+            return /* I18N: Location of an historic LDS church temple */ I18N::translate('New York, New York, United States');
268
+        case 'NZEAL':
269
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Hamilton, New Zealand');
270
+        case 'OAKLA':
271
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Oakland, California, United States');
272
+        case 'OAXAC':
273
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Oaxaca, Mexico');
274
+        case 'OGDEN':
275
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Ogden, Utah, United States');
276
+        case 'OKLAH':
277
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Oklahoma City, Oklahoma, United States');
278
+        case 'OQUIR':
279
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Oquirrh Mountain, Utah, United States');
280
+        case 'ORLAN':
281
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Orlando, Florida, United States');
282
+        case 'PALEG':
283
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Porto Alegre, Brazil');
284
+        case 'PALMY':
285
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Palmyra, New York, United States');
286
+        case 'PANAM':
287
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Panama City, Panama');
288
+        case 'PAPEE':
289
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Papeete, Tahiti');
290
+        case 'PAYSO':
291
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Payson, Utah, United States');
292
+        case 'PERTH':
293
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Perth, Australia');
294
+        case 'PHOEN':
295
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Phoenix, Arizona, United States');
296
+        case 'POFFI':
297
+            return /* I18N: I18N: Location of an historic LDS church temple - http://en.wikipedia.org/wiki/President_of_the_Church */ I18N::translate('President’s Office');
298
+        case 'PORTL':
299
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Portland, Oregon, United States');
300
+        case 'PREST':
301
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Preston, England');
302
+        case 'PROCC':
303
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Provo City Center, Utah, United States');
304
+        case 'PROVO':
305
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Provo, Utah, United States');
306
+        case 'QUETZ':
307
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Quetzaltenango, Guatemala');
308
+        case 'RALEI':
309
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Raleigh, North Carolina, United States');
310
+        case 'RECIF':
311
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Recife, Brazil');
312
+        case 'REDLA':
313
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Redlands, California, United States');
314
+        case 'REGIN':
315
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Regina, Saskatchewan, Canada');
316
+        case 'RENO':
317
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Reno, Nevada, United States');
318
+        case 'REXBU':
319
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Rexburg, Idaho, United States');
320
+        case 'SACRA':
321
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Sacramento, California, United States');
322
+        case 'SANSA':
323
+            return /* I18N: Location of an LDS church temple */ I18N::translate('San Salvador, El Salvador');
324
+        case 'SANTI':
325
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Santiago, Chile');
326
+        case 'SANTO':
327
+            return /* I18N: Location of an LDS church temple */ I18N::translate('San Antonio, Texas, United States');
328
+        case 'SDIEG':
329
+            return /* I18N: Location of an LDS church temple */ I18N::translate('San Diego, California, United States');
330
+        case 'SDOMI':
331
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Santo Domingo, Dominican Republic');
332
+        case 'SEATT':
333
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Seattle, Washington, United States');
334
+        case 'SEOUL':
335
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Seoul, Korea');
336
+        case 'SGEOR':
337
+            return /* I18N: Location of an LDS church temple */ I18N::translate('St. George, Utah, United States');
338
+        case 'SJOSE':
339
+            return /* I18N: Location of an LDS church temple */ I18N::translate('San Jose, Costa Rica');
340
+        case 'SLAKE':
341
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Salt Lake City, Utah, United States');
342
+        case 'SLOUI':
343
+            return /* I18N: Location of an LDS church temple */ I18N::translate('St. Louis, Missouri, United States');
344
+        case 'SNOWF':
345
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Snowflake, Arizona, United States');
346
+        case 'SPAUL':
347
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Sao Paulo, Brazil');
348
+        case 'SPMIN':
349
+            return /* I18N: Location of an LDS church temple */ I18N::translate('St. Paul, Minnesota, United States');
350
+        case 'SPOKA':
351
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Spokane, Washington, United States');
352
+        case 'STOCK':
353
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Stockholm, Sweden');
354
+        case 'SUVA':
355
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Suva, Fiji');
356
+        case 'SWISS':
357
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Bern, Switzerland');
358
+        case 'SYDNE':
359
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Sydney, Australia');
360
+        case 'TAIPE':
361
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Taipei, Taiwan');
362
+        case 'TAMPI':
363
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Tampico, Mexico');
364
+        case 'TEGUC':
365
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Tegucigalpa, Honduras');
366
+        case 'TGUTI':
367
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Tuxtla Gutierrez, Mexico');
368
+        case 'TIJUA':
369
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Tijuana, Mexico');
370
+        case 'TOKYO':
371
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Tokyo, Japan');
372
+        case 'TORNO':
373
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Toronto, Ontario, Canada');
374
+        case 'TRUJI':
375
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Trujillo, Peru');
376
+        case 'TWINF':
377
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Twin Falls, Idaho, United States');
378
+        case 'VANCO':
379
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Vancouver, British Columbia, Canada');
380
+        case 'VERAC':
381
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Veracruz, Mexico');
382
+        case 'VERNA':
383
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Vernal, Utah, United States');
384
+        case 'VILLA':
385
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Villa Hermosa, Mexico');
386
+        case 'WASHI':
387
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Washington, District of Columbia, United States');
388
+        case 'WINTE':
389
+            return /* I18N: Location of an LDS church temple */ I18N::translate('Winter Quarters, Nebraska, United States');
390
+        default:
391
+            return $temple_code;
392
+        }
393
+    }
394 394
 
395
-	/**
396
-	 * A sorted list of all temple names
397
-	 *
398
-	 * @return string[]
399
-	 */
400
-	public static function templeNames() {
401
-		$temple_names = array();
402
-		foreach (self::templeCodes() as $temple_code) {
403
-			$temple_names[$temple_code] = self::templeName($temple_code);
404
-		}
405
-		uasort($temple_names, '\Fisharebest\Webtrees\I18N::strcasecmp');
395
+    /**
396
+     * A sorted list of all temple names
397
+     *
398
+     * @return string[]
399
+     */
400
+    public static function templeNames() {
401
+        $temple_names = array();
402
+        foreach (self::templeCodes() as $temple_code) {
403
+            $temple_names[$temple_code] = self::templeName($temple_code);
404
+        }
405
+        uasort($temple_names, '\Fisharebest\Webtrees\I18N::strcasecmp');
406 406
 
407
-		return $temple_names;
408
-	}
407
+        return $temple_names;
408
+    }
409 409
 }
Please login to merge, or discard this patch.
Switch Indentation   +312 added lines, -312 removed lines patch added patch discarded remove patch
@@ -77,318 +77,318 @@
 block discarded – undo
77 77
 	 */
78 78
 	public static function templeName($temple_code) {
79 79
 		switch ($temple_code) {
80
-		case 'ABA':
81
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Aba, Nigeria');
82
-		case 'ACCRA':
83
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Accra, Ghana');
84
-		case 'ADELA':
85
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Adelaide, Australia');
86
-		case 'ALBER':
87
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Cardston, Alberta, Canada');
88
-		case 'ALBUQ':
89
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Albuquerque, New Mexico, United States');
90
-		case 'ANCHO':
91
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Anchorage, Alaska, United States');
92
-		case 'APIA':
93
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Apia, Samoa');
94
-		case 'ARIZO':
95
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Mesa, Arizona, United States');
96
-		case 'ASUNC':
97
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Asuncion, Paraguay');
98
-		case 'ATLAN':
99
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Atlanta, Georgia, United States');
100
-		case 'BAIRE':
101
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Buenos Aires, Argentina');
102
-		case 'BILLI':
103
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Billings, Montana, United States');
104
-		case 'BIRMI':
105
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Birmingham, Alabama, United States');
106
-		case 'BISMA':
107
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Bismarck, North Dakota, United States');
108
-		case 'BOGOT':
109
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Bogota, Colombia');
110
-		case 'BOISE':
111
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Boise, Idaho, United States');
112
-		case 'BOSTO':
113
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Boston, Massachusetts, United States');
114
-		case 'BOUNT':
115
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Bountiful, Utah, United States');
116
-		case 'BRIGH':
117
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Brigham City, Utah, United States');
118
-		case 'BRISB':
119
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Brisbane, Australia');
120
-		case 'BROUG':
121
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Baton Rouge, Louisiana, United States');
122
-		case 'CALGA':
123
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Calgary, Alberta, Canada');
124
-		case 'CAMPI':
125
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Campinas, Brazil');
126
-		case 'CARAC':
127
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Caracas, Venezuela');
128
-		case 'CEBUP':
129
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Cebu City, Philippines');
130
-		case 'CHICA':
131
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Chicago, Illinois, United States');
132
-		case 'CIUJU':
133
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Ciudad Juarez, Mexico');
134
-		case 'COCHA':
135
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Cochabamba, Bolivia');
136
-		case 'COLJU':
137
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Colonia Juarez, Mexico');
138
-		case 'COLSC':
139
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Columbia, South Carolina, United States');
140
-		case 'COLUM':
141
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Columbus, Ohio, United States');
142
-		case 'COPEN':
143
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Copenhagen, Denmark');
144
-		case 'CORDO':
145
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Cordoba, Argentina');
146
-		case 'CRIVE':
147
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Columbia River, Washington, United States');
148
-		case 'CURIT':
149
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Curitiba, Brazil');
150
-		case 'DALLA':
151
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Dallas, Texas, United States');
152
-		case 'DENVE':
153
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Denver, Colorado, United States');
154
-		case 'DETRO':
155
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Detroit, Michigan, United States');
156
-		case 'DRAPE':
157
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Draper, Utah, United States');
158
-		case 'EDMON':
159
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Edmonton, Alberta, Canada');
160
-		case 'EHOUS':
161
-			return /* I18N: Location of an historic LDS church temple - http://en.wikipedia.org/wiki/Endowment_house */ I18N::translate('Endowment House');
162
-		case 'FORTL':
163
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Fort Lauderdale, Florida, United States');
164
-		case 'FRANK':
165
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Frankfurt am Main, Germany');
166
-		case 'FREIB':
167
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Freiburg, Germany');
168
-		case 'FRESN':
169
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Fresno, California, United States');
170
-		case 'FUKUO':
171
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Fukuoka, Japan');
172
-		case 'GILAV':
173
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Gila Valley, Arizona, United States');
174
-		case 'GILBE':
175
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Gilbert, Arizona, United States');
176
-		case 'GUADA':
177
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Guadalajara, Mexico');
178
-		case 'GUATE':
179
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Guatemala City, Guatemala');
180
-		case 'GUAYA':
181
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Guayaquil, Ecuador');
182
-		case 'HAGUE':
183
-			return /* I18N: Location of an LDS church temple */ I18N::translate('The Hague, Netherlands');
184
-		case 'HALIF':
185
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Halifax, Nova Scotia, Canada');
186
-		case 'HARTF':
187
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Hartford, Connecticut, United States');
188
-		case 'HAWAI':
189
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Laie, Hawaii, United States');
190
-		case 'HELSI':
191
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Helsinki, Finland');
192
-		case 'HERMO':
193
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Hermosillo, Mexico');
194
-		case 'HKONG':
195
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Hong Kong');
196
-		case 'HOUST':
197
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Houston, Texas, United States');
198
-		case 'IFALL':
199
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Idaho Falls, Idaho, United States');
200
-		case 'INDIA':
201
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Indianapolis, Indiana, United States');
202
-		case 'JOHAN':
203
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Johannesburg, South Africa');
204
-		case 'JRIVE':
205
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Jordan River, Utah, United States');
206
-		case 'KANSA':
207
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Kansas City, Missouri, United States');
208
-		case 'KONA':
209
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Kona, Hawaii, United States');
210
-		case 'KYIV':
211
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Kiev, Ukraine');
212
-		case 'LANGE':
213
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Los Angeles, California, United States');
214
-		case 'LIMA':
215
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Lima, Peru');
216
-		case 'LOGAN':
217
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Logan, Utah, United States');
218
-		case 'LONDO':
219
-			return /* I18N: Location of an LDS church temple */ I18N::translate('London, England');
220
-		case 'LOUIS':
221
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Louisville, Kentucky, United States');
222
-		case 'LUBBO':
223
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Lubbock, Texas, United States');
224
-		case 'LVEGA':
225
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Las Vegas, Nevada, United States');
226
-		case 'MADRI':
227
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Madrid, Spain');
228
-		case 'MANAU':
229
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Manaus, Brazil');
230
-		case 'MANHA':
231
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Manhattan, New York, United States');
232
-		case 'MANIL':
233
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Manila, Philippines');
234
-		case 'MANTI':
235
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Manti, Utah, United States');
236
-		case 'MEDFO':
237
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Medford, Oregon, United States');
238
-		case 'MELBO':
239
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Melbourne, Australia');
240
-		case 'MEMPH':
241
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Memphis, Tennessee, United States');
242
-		case 'MERID':
243
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Merida, Mexico');
244
-		case 'MEXIC':
245
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Mexico City, Mexico');
246
-		case 'MNTVD':
247
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Montevideo, Uruguay');
248
-		case 'MONTE':
249
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Monterrey, Mexico');
250
-		case 'MONTI':
251
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Monticello, Utah, United States');
252
-		case 'MONTR':
253
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Montreal, Quebec, Canada');
254
-		case 'MTIMP':
255
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Mount Timpanogos, Utah, United States');
256
-		case 'NASHV':
257
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Nashville, Tennessee, United States');
258
-		case 'NAUV2':
259
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Nauvoo (new), Illinois, United States');
260
-		case 'NAUVO':
261
-			return /* I18N: Location of an historic LDS church temple */ I18N::translate('Nauvoo (original), Illinois, United States');
262
-		case 'NBEAC':
263
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Newport Beach, California, United States');
264
-		case 'NUKUA':
265
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Nuku’Alofa, Tonga');
266
-		case 'NYORK':
267
-			return /* I18N: Location of an historic LDS church temple */ I18N::translate('New York, New York, United States');
268
-		case 'NZEAL':
269
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Hamilton, New Zealand');
270
-		case 'OAKLA':
271
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Oakland, California, United States');
272
-		case 'OAXAC':
273
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Oaxaca, Mexico');
274
-		case 'OGDEN':
275
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Ogden, Utah, United States');
276
-		case 'OKLAH':
277
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Oklahoma City, Oklahoma, United States');
278
-		case 'OQUIR':
279
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Oquirrh Mountain, Utah, United States');
280
-		case 'ORLAN':
281
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Orlando, Florida, United States');
282
-		case 'PALEG':
283
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Porto Alegre, Brazil');
284
-		case 'PALMY':
285
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Palmyra, New York, United States');
286
-		case 'PANAM':
287
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Panama City, Panama');
288
-		case 'PAPEE':
289
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Papeete, Tahiti');
290
-		case 'PAYSO':
291
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Payson, Utah, United States');
292
-		case 'PERTH':
293
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Perth, Australia');
294
-		case 'PHOEN':
295
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Phoenix, Arizona, United States');
296
-		case 'POFFI':
297
-			return /* I18N: I18N: Location of an historic LDS church temple - http://en.wikipedia.org/wiki/President_of_the_Church */ I18N::translate('President’s Office');
298
-		case 'PORTL':
299
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Portland, Oregon, United States');
300
-		case 'PREST':
301
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Preston, England');
302
-		case 'PROCC':
303
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Provo City Center, Utah, United States');
304
-		case 'PROVO':
305
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Provo, Utah, United States');
306
-		case 'QUETZ':
307
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Quetzaltenango, Guatemala');
308
-		case 'RALEI':
309
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Raleigh, North Carolina, United States');
310
-		case 'RECIF':
311
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Recife, Brazil');
312
-		case 'REDLA':
313
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Redlands, California, United States');
314
-		case 'REGIN':
315
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Regina, Saskatchewan, Canada');
316
-		case 'RENO':
317
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Reno, Nevada, United States');
318
-		case 'REXBU':
319
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Rexburg, Idaho, United States');
320
-		case 'SACRA':
321
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Sacramento, California, United States');
322
-		case 'SANSA':
323
-			return /* I18N: Location of an LDS church temple */ I18N::translate('San Salvador, El Salvador');
324
-		case 'SANTI':
325
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Santiago, Chile');
326
-		case 'SANTO':
327
-			return /* I18N: Location of an LDS church temple */ I18N::translate('San Antonio, Texas, United States');
328
-		case 'SDIEG':
329
-			return /* I18N: Location of an LDS church temple */ I18N::translate('San Diego, California, United States');
330
-		case 'SDOMI':
331
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Santo Domingo, Dominican Republic');
332
-		case 'SEATT':
333
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Seattle, Washington, United States');
334
-		case 'SEOUL':
335
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Seoul, Korea');
336
-		case 'SGEOR':
337
-			return /* I18N: Location of an LDS church temple */ I18N::translate('St. George, Utah, United States');
338
-		case 'SJOSE':
339
-			return /* I18N: Location of an LDS church temple */ I18N::translate('San Jose, Costa Rica');
340
-		case 'SLAKE':
341
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Salt Lake City, Utah, United States');
342
-		case 'SLOUI':
343
-			return /* I18N: Location of an LDS church temple */ I18N::translate('St. Louis, Missouri, United States');
344
-		case 'SNOWF':
345
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Snowflake, Arizona, United States');
346
-		case 'SPAUL':
347
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Sao Paulo, Brazil');
348
-		case 'SPMIN':
349
-			return /* I18N: Location of an LDS church temple */ I18N::translate('St. Paul, Minnesota, United States');
350
-		case 'SPOKA':
351
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Spokane, Washington, United States');
352
-		case 'STOCK':
353
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Stockholm, Sweden');
354
-		case 'SUVA':
355
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Suva, Fiji');
356
-		case 'SWISS':
357
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Bern, Switzerland');
358
-		case 'SYDNE':
359
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Sydney, Australia');
360
-		case 'TAIPE':
361
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Taipei, Taiwan');
362
-		case 'TAMPI':
363
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Tampico, Mexico');
364
-		case 'TEGUC':
365
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Tegucigalpa, Honduras');
366
-		case 'TGUTI':
367
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Tuxtla Gutierrez, Mexico');
368
-		case 'TIJUA':
369
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Tijuana, Mexico');
370
-		case 'TOKYO':
371
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Tokyo, Japan');
372
-		case 'TORNO':
373
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Toronto, Ontario, Canada');
374
-		case 'TRUJI':
375
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Trujillo, Peru');
376
-		case 'TWINF':
377
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Twin Falls, Idaho, United States');
378
-		case 'VANCO':
379
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Vancouver, British Columbia, Canada');
380
-		case 'VERAC':
381
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Veracruz, Mexico');
382
-		case 'VERNA':
383
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Vernal, Utah, United States');
384
-		case 'VILLA':
385
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Villa Hermosa, Mexico');
386
-		case 'WASHI':
387
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Washington, District of Columbia, United States');
388
-		case 'WINTE':
389
-			return /* I18N: Location of an LDS church temple */ I18N::translate('Winter Quarters, Nebraska, United States');
390
-		default:
391
-			return $temple_code;
80
+		    case 'ABA':
81
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Aba, Nigeria');
82
+		    case 'ACCRA':
83
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Accra, Ghana');
84
+		    case 'ADELA':
85
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Adelaide, Australia');
86
+		    case 'ALBER':
87
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Cardston, Alberta, Canada');
88
+		    case 'ALBUQ':
89
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Albuquerque, New Mexico, United States');
90
+		    case 'ANCHO':
91
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Anchorage, Alaska, United States');
92
+		    case 'APIA':
93
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Apia, Samoa');
94
+		    case 'ARIZO':
95
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Mesa, Arizona, United States');
96
+		    case 'ASUNC':
97
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Asuncion, Paraguay');
98
+		    case 'ATLAN':
99
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Atlanta, Georgia, United States');
100
+		    case 'BAIRE':
101
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Buenos Aires, Argentina');
102
+		    case 'BILLI':
103
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Billings, Montana, United States');
104
+		    case 'BIRMI':
105
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Birmingham, Alabama, United States');
106
+		    case 'BISMA':
107
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Bismarck, North Dakota, United States');
108
+		    case 'BOGOT':
109
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Bogota, Colombia');
110
+		    case 'BOISE':
111
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Boise, Idaho, United States');
112
+		    case 'BOSTO':
113
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Boston, Massachusetts, United States');
114
+		    case 'BOUNT':
115
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Bountiful, Utah, United States');
116
+		    case 'BRIGH':
117
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Brigham City, Utah, United States');
118
+		    case 'BRISB':
119
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Brisbane, Australia');
120
+		    case 'BROUG':
121
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Baton Rouge, Louisiana, United States');
122
+		    case 'CALGA':
123
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Calgary, Alberta, Canada');
124
+		    case 'CAMPI':
125
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Campinas, Brazil');
126
+		    case 'CARAC':
127
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Caracas, Venezuela');
128
+		    case 'CEBUP':
129
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Cebu City, Philippines');
130
+		    case 'CHICA':
131
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Chicago, Illinois, United States');
132
+		    case 'CIUJU':
133
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Ciudad Juarez, Mexico');
134
+		    case 'COCHA':
135
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Cochabamba, Bolivia');
136
+		    case 'COLJU':
137
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Colonia Juarez, Mexico');
138
+		    case 'COLSC':
139
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Columbia, South Carolina, United States');
140
+		    case 'COLUM':
141
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Columbus, Ohio, United States');
142
+		    case 'COPEN':
143
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Copenhagen, Denmark');
144
+		    case 'CORDO':
145
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Cordoba, Argentina');
146
+		    case 'CRIVE':
147
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Columbia River, Washington, United States');
148
+		    case 'CURIT':
149
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Curitiba, Brazil');
150
+		    case 'DALLA':
151
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Dallas, Texas, United States');
152
+		    case 'DENVE':
153
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Denver, Colorado, United States');
154
+		    case 'DETRO':
155
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Detroit, Michigan, United States');
156
+		    case 'DRAPE':
157
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Draper, Utah, United States');
158
+		    case 'EDMON':
159
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Edmonton, Alberta, Canada');
160
+		    case 'EHOUS':
161
+			    return /* I18N: Location of an historic LDS church temple - http://en.wikipedia.org/wiki/Endowment_house */ I18N::translate('Endowment House');
162
+		    case 'FORTL':
163
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Fort Lauderdale, Florida, United States');
164
+		    case 'FRANK':
165
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Frankfurt am Main, Germany');
166
+		    case 'FREIB':
167
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Freiburg, Germany');
168
+		    case 'FRESN':
169
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Fresno, California, United States');
170
+		    case 'FUKUO':
171
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Fukuoka, Japan');
172
+		    case 'GILAV':
173
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Gila Valley, Arizona, United States');
174
+		    case 'GILBE':
175
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Gilbert, Arizona, United States');
176
+		    case 'GUADA':
177
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Guadalajara, Mexico');
178
+		    case 'GUATE':
179
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Guatemala City, Guatemala');
180
+		    case 'GUAYA':
181
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Guayaquil, Ecuador');
182
+		    case 'HAGUE':
183
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('The Hague, Netherlands');
184
+		    case 'HALIF':
185
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Halifax, Nova Scotia, Canada');
186
+		    case 'HARTF':
187
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Hartford, Connecticut, United States');
188
+		    case 'HAWAI':
189
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Laie, Hawaii, United States');
190
+		    case 'HELSI':
191
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Helsinki, Finland');
192
+		    case 'HERMO':
193
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Hermosillo, Mexico');
194
+		    case 'HKONG':
195
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Hong Kong');
196
+		    case 'HOUST':
197
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Houston, Texas, United States');
198
+		    case 'IFALL':
199
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Idaho Falls, Idaho, United States');
200
+		    case 'INDIA':
201
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Indianapolis, Indiana, United States');
202
+		    case 'JOHAN':
203
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Johannesburg, South Africa');
204
+		    case 'JRIVE':
205
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Jordan River, Utah, United States');
206
+		    case 'KANSA':
207
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Kansas City, Missouri, United States');
208
+		    case 'KONA':
209
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Kona, Hawaii, United States');
210
+		    case 'KYIV':
211
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Kiev, Ukraine');
212
+		    case 'LANGE':
213
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Los Angeles, California, United States');
214
+		    case 'LIMA':
215
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Lima, Peru');
216
+		    case 'LOGAN':
217
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Logan, Utah, United States');
218
+		    case 'LONDO':
219
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('London, England');
220
+		    case 'LOUIS':
221
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Louisville, Kentucky, United States');
222
+		    case 'LUBBO':
223
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Lubbock, Texas, United States');
224
+		    case 'LVEGA':
225
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Las Vegas, Nevada, United States');
226
+		    case 'MADRI':
227
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Madrid, Spain');
228
+		    case 'MANAU':
229
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Manaus, Brazil');
230
+		    case 'MANHA':
231
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Manhattan, New York, United States');
232
+		    case 'MANIL':
233
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Manila, Philippines');
234
+		    case 'MANTI':
235
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Manti, Utah, United States');
236
+		    case 'MEDFO':
237
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Medford, Oregon, United States');
238
+		    case 'MELBO':
239
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Melbourne, Australia');
240
+		    case 'MEMPH':
241
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Memphis, Tennessee, United States');
242
+		    case 'MERID':
243
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Merida, Mexico');
244
+		    case 'MEXIC':
245
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Mexico City, Mexico');
246
+		    case 'MNTVD':
247
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Montevideo, Uruguay');
248
+		    case 'MONTE':
249
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Monterrey, Mexico');
250
+		    case 'MONTI':
251
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Monticello, Utah, United States');
252
+		    case 'MONTR':
253
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Montreal, Quebec, Canada');
254
+		    case 'MTIMP':
255
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Mount Timpanogos, Utah, United States');
256
+		    case 'NASHV':
257
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Nashville, Tennessee, United States');
258
+		    case 'NAUV2':
259
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Nauvoo (new), Illinois, United States');
260
+		    case 'NAUVO':
261
+			    return /* I18N: Location of an historic LDS church temple */ I18N::translate('Nauvoo (original), Illinois, United States');
262
+		    case 'NBEAC':
263
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Newport Beach, California, United States');
264
+		    case 'NUKUA':
265
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Nuku’Alofa, Tonga');
266
+		    case 'NYORK':
267
+			    return /* I18N: Location of an historic LDS church temple */ I18N::translate('New York, New York, United States');
268
+		    case 'NZEAL':
269
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Hamilton, New Zealand');
270
+		    case 'OAKLA':
271
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Oakland, California, United States');
272
+		    case 'OAXAC':
273
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Oaxaca, Mexico');
274
+		    case 'OGDEN':
275
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Ogden, Utah, United States');
276
+		    case 'OKLAH':
277
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Oklahoma City, Oklahoma, United States');
278
+		    case 'OQUIR':
279
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Oquirrh Mountain, Utah, United States');
280
+		    case 'ORLAN':
281
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Orlando, Florida, United States');
282
+		    case 'PALEG':
283
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Porto Alegre, Brazil');
284
+		    case 'PALMY':
285
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Palmyra, New York, United States');
286
+		    case 'PANAM':
287
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Panama City, Panama');
288
+		    case 'PAPEE':
289
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Papeete, Tahiti');
290
+		    case 'PAYSO':
291
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Payson, Utah, United States');
292
+		    case 'PERTH':
293
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Perth, Australia');
294
+		    case 'PHOEN':
295
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Phoenix, Arizona, United States');
296
+		    case 'POFFI':
297
+			    return /* I18N: I18N: Location of an historic LDS church temple - http://en.wikipedia.org/wiki/President_of_the_Church */ I18N::translate('President’s Office');
298
+		    case 'PORTL':
299
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Portland, Oregon, United States');
300
+		    case 'PREST':
301
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Preston, England');
302
+		    case 'PROCC':
303
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Provo City Center, Utah, United States');
304
+		    case 'PROVO':
305
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Provo, Utah, United States');
306
+		    case 'QUETZ':
307
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Quetzaltenango, Guatemala');
308
+		    case 'RALEI':
309
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Raleigh, North Carolina, United States');
310
+		    case 'RECIF':
311
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Recife, Brazil');
312
+		    case 'REDLA':
313
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Redlands, California, United States');
314
+		    case 'REGIN':
315
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Regina, Saskatchewan, Canada');
316
+		    case 'RENO':
317
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Reno, Nevada, United States');
318
+		    case 'REXBU':
319
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Rexburg, Idaho, United States');
320
+		    case 'SACRA':
321
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Sacramento, California, United States');
322
+		    case 'SANSA':
323
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('San Salvador, El Salvador');
324
+		    case 'SANTI':
325
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Santiago, Chile');
326
+		    case 'SANTO':
327
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('San Antonio, Texas, United States');
328
+		    case 'SDIEG':
329
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('San Diego, California, United States');
330
+		    case 'SDOMI':
331
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Santo Domingo, Dominican Republic');
332
+		    case 'SEATT':
333
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Seattle, Washington, United States');
334
+		    case 'SEOUL':
335
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Seoul, Korea');
336
+		    case 'SGEOR':
337
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('St. George, Utah, United States');
338
+		    case 'SJOSE':
339
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('San Jose, Costa Rica');
340
+		    case 'SLAKE':
341
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Salt Lake City, Utah, United States');
342
+		    case 'SLOUI':
343
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('St. Louis, Missouri, United States');
344
+		    case 'SNOWF':
345
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Snowflake, Arizona, United States');
346
+		    case 'SPAUL':
347
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Sao Paulo, Brazil');
348
+		    case 'SPMIN':
349
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('St. Paul, Minnesota, United States');
350
+		    case 'SPOKA':
351
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Spokane, Washington, United States');
352
+		    case 'STOCK':
353
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Stockholm, Sweden');
354
+		    case 'SUVA':
355
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Suva, Fiji');
356
+		    case 'SWISS':
357
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Bern, Switzerland');
358
+		    case 'SYDNE':
359
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Sydney, Australia');
360
+		    case 'TAIPE':
361
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Taipei, Taiwan');
362
+		    case 'TAMPI':
363
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Tampico, Mexico');
364
+		    case 'TEGUC':
365
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Tegucigalpa, Honduras');
366
+		    case 'TGUTI':
367
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Tuxtla Gutierrez, Mexico');
368
+		    case 'TIJUA':
369
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Tijuana, Mexico');
370
+		    case 'TOKYO':
371
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Tokyo, Japan');
372
+		    case 'TORNO':
373
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Toronto, Ontario, Canada');
374
+		    case 'TRUJI':
375
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Trujillo, Peru');
376
+		    case 'TWINF':
377
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Twin Falls, Idaho, United States');
378
+		    case 'VANCO':
379
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Vancouver, British Columbia, Canada');
380
+		    case 'VERAC':
381
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Veracruz, Mexico');
382
+		    case 'VERNA':
383
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Vernal, Utah, United States');
384
+		    case 'VILLA':
385
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Villa Hermosa, Mexico');
386
+		    case 'WASHI':
387
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Washington, District of Columbia, United States');
388
+		    case 'WINTE':
389
+			    return /* I18N: Location of an LDS church temple */ I18N::translate('Winter Quarters, Nebraska, United States');
390
+		    default:
391
+			    return $temple_code;
392 392
 		}
393 393
 	}
394 394
 
Please login to merge, or discard this patch.
Braces   +10 added lines, -5 removed lines patch added patch discarded remove patch
@@ -20,7 +20,8 @@  discard block
 block discarded – undo
20 20
 /**
21 21
  * Class GedcomCodeTemp - Functions and logic for GEDCOM "TEMP" codes
22 22
  */
23
-class GedcomCodeTemp {
23
+class GedcomCodeTemp
24
+{
24 25
 	/**
25 26
 	 * A list of GEDCOM tags that require a TEMP subtag
26 27
 	 *
@@ -28,7 +29,8 @@  discard block
 block discarded – undo
28 29
 	 *
29 30
 	 * @return bool
30 31
 	 */
31
-	public static function isTagLDS($tag) {
32
+	public static function isTagLDS($tag)
33
+	{
32 34
 		return $tag === 'BAPL' || $tag === 'CONL' || $tag === 'ENDL' || $tag === 'SLGC' || $tag === 'SLGS';
33 35
 	}
34 36
 
@@ -43,7 +45,8 @@  discard block
 block discarded – undo
43 45
 	 *
44 46
 	 * @return string[]
45 47
 	 */
46
-	public static function templeCodes() {
48
+	public static function templeCodes()
49
+	{
47 50
 		return array(
48 51
 			'ABA', 'ACCRA', 'ADELA', 'ALBER', 'ALBUQ', 'ANCHO', 'ARIZO', 'ASUNC',
49 52
 			'ATLAN', 'BAIRE', 'BILLI', 'BIRMI', 'BISMA', 'BOGOT', 'BOISE', 'BOSTO',
@@ -75,7 +78,8 @@  discard block
 block discarded – undo
75 78
 	 *
76 79
 	 * @return string
77 80
 	 */
78
-	public static function templeName($temple_code) {
81
+	public static function templeName($temple_code)
82
+	{
79 83
 		switch ($temple_code) {
80 84
 		case 'ABA':
81 85
 			return /* I18N: Location of an LDS church temple */ I18N::translate('Aba, Nigeria');
@@ -397,7 +401,8 @@  discard block
 block discarded – undo
397 401
 	 *
398 402
 	 * @return string[]
399 403
 	 */
400
-	public static function templeNames() {
404
+	public static function templeNames()
405
+	{
401 406
 		$temple_names = array();
402 407
 		foreach (self::templeCodes() as $temple_code) {
403 408
 			$temple_names[$temple_code] = self::templeName($temple_code);
Please login to merge, or discard this patch.
edituser.php 4 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -44,7 +44,7 @@
 block discarded – undo
44 44
 $form_pass2          = Filter::post('form_pass2', WT_REGEX_PASSWORD);
45 45
 $form_email          = Filter::postEmail('form_email');
46 46
 $form_rootid         = Filter::post('form_rootid', WT_REGEX_XREF);
47
-$form_theme          = Filter::post('form_theme', implode('|', array_keys(Theme::themeNames())), '');;
47
+$form_theme          = Filter::post('form_theme', implode('|', array_keys(Theme::themeNames())), ''); ;
48 48
 $form_language       = Filter::post('form_language');
49 49
 $form_timezone       = Filter::post('form_timezone');
50 50
 $form_contact_method = Filter::post('form_contact_method');
Please login to merge, or discard this patch.
Indentation   +58 added lines, -58 removed lines patch added patch discarded remove patch
@@ -31,9 +31,9 @@  discard block
 block discarded – undo
31 31
 
32 32
 // Need to be logged in
33 33
 if (!Auth::check()) {
34
-	header('Location: ' . WT_BASE_URL);
34
+    header('Location: ' . WT_BASE_URL);
35 35
 
36
-	return;
36
+    return;
37 37
 }
38 38
 
39 39
 // Extract form variables
@@ -52,66 +52,66 @@  discard block
 block discarded – undo
52 52
 
53 53
 // Respond to form action
54 54
 if ($form_action && Filter::checkCsrf()) {
55
-	switch ($form_action) {
56
-	case 'update':
57
-		if ($form_username !== Auth::user()->getUserName() && User::findByUserName($form_username)) {
58
-			FlashMessages::addMessage(I18N::translate('Duplicate username. A user with that username already exists. Please choose another username.'));
59
-		} elseif ($form_email !== Auth::user()->getEmail() && User::findByEmail($form_email)) {
60
-			FlashMessages::addMessage(I18N::translate('Duplicate email address. A user with that email already exists.'));
61
-		} else {
62
-			// Change username
63
-			if ($form_username !== Auth::user()->getUserName()) {
64
-				Log::addAuthenticationLog('User ' . Auth::user()->getUserName() . ' renamed to ' . $form_username);
65
-				Auth::user()->setUserName($form_username);
66
-			}
67
-
68
-			// Change password
69
-			if ($form_pass1 && $form_pass1 === $form_pass2) {
70
-				Auth::user()->setPassword($form_pass1);
71
-			}
72
-
73
-			// Change other settings
74
-			Auth::user()
75
-				->setRealName($form_realname)
76
-				->setEmail($form_email)
77
-				->setPreference('language', $form_language)
78
-				->setPreference('TIMEZONE', $form_timezone)
79
-				->setPreference('contactmethod', $form_contact_method)
80
-				->setPreference('visibleonline', $form_visible_online ? '1' : '0');
81
-
82
-			if ($form_theme === null) {
83
-				Auth::user()->deletePreference('theme');
84
-			} else {
85
-				Auth::user()->setPreference('theme', $form_theme);
86
-			}
87
-
88
-			$WT_TREE->setUserPreference(Auth::user(), 'rootid', $form_rootid);
89
-		}
90
-		break;
91
-
92
-	case 'delete':
93
-		// An administrator can only be deleted by another administrator
94
-		if (!Auth::user()->getPreference('canadmin')) {
95
-			// Keep a reference to the currently logged in user because after logging out this user,
96
-			// a call to Auth::user() will not return this user anymore
97
-			$currentUser = Auth::user();
98
-			Auth::logout();
99
-			$currentUser->delete();
100
-		}
101
-		break;
102
-	}
103
-
104
-	header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
105
-
106
-	return;
55
+    switch ($form_action) {
56
+    case 'update':
57
+        if ($form_username !== Auth::user()->getUserName() && User::findByUserName($form_username)) {
58
+            FlashMessages::addMessage(I18N::translate('Duplicate username. A user with that username already exists. Please choose another username.'));
59
+        } elseif ($form_email !== Auth::user()->getEmail() && User::findByEmail($form_email)) {
60
+            FlashMessages::addMessage(I18N::translate('Duplicate email address. A user with that email already exists.'));
61
+        } else {
62
+            // Change username
63
+            if ($form_username !== Auth::user()->getUserName()) {
64
+                Log::addAuthenticationLog('User ' . Auth::user()->getUserName() . ' renamed to ' . $form_username);
65
+                Auth::user()->setUserName($form_username);
66
+            }
67
+
68
+            // Change password
69
+            if ($form_pass1 && $form_pass1 === $form_pass2) {
70
+                Auth::user()->setPassword($form_pass1);
71
+            }
72
+
73
+            // Change other settings
74
+            Auth::user()
75
+                ->setRealName($form_realname)
76
+                ->setEmail($form_email)
77
+                ->setPreference('language', $form_language)
78
+                ->setPreference('TIMEZONE', $form_timezone)
79
+                ->setPreference('contactmethod', $form_contact_method)
80
+                ->setPreference('visibleonline', $form_visible_online ? '1' : '0');
81
+
82
+            if ($form_theme === null) {
83
+                Auth::user()->deletePreference('theme');
84
+            } else {
85
+                Auth::user()->setPreference('theme', $form_theme);
86
+            }
87
+
88
+            $WT_TREE->setUserPreference(Auth::user(), 'rootid', $form_rootid);
89
+        }
90
+        break;
91
+
92
+    case 'delete':
93
+        // An administrator can only be deleted by another administrator
94
+        if (!Auth::user()->getPreference('canadmin')) {
95
+            // Keep a reference to the currently logged in user because after logging out this user,
96
+            // a call to Auth::user() will not return this user anymore
97
+            $currentUser = Auth::user();
98
+            Auth::logout();
99
+            $currentUser->delete();
100
+        }
101
+        break;
102
+    }
103
+
104
+    header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
105
+
106
+    return;
107 107
 }
108 108
 
109 109
 $controller = new PageController;
110 110
 $controller
111
-	->setPageTitle(I18N::translate('My account'))
112
-	->pageHeader()
113
-	->addExternalJavascript(WT_AUTOCOMPLETE_JS_URL)
114
-	->addInlineJavascript('autocomplete();');
111
+    ->setPageTitle(I18N::translate('My account'))
112
+    ->pageHeader()
113
+    ->addExternalJavascript(WT_AUTOCOMPLETE_JS_URL)
114
+    ->addInlineJavascript('autocomplete();');
115 115
 
116 116
 $my_individual_record = Individual::getInstance($WT_TREE->getUserPreference(Auth::user(), 'gedcomid'), $WT_TREE);
117 117
 $default_individual   = Individual::getInstance($WT_TREE->getUserPreference(Auth::user(), 'rootid'), $WT_TREE);
Please login to merge, or discard this patch.
Switch Indentation   +46 added lines, -46 removed lines patch added patch discarded remove patch
@@ -53,52 +53,52 @@
 block discarded – undo
53 53
 // Respond to form action
54 54
 if ($form_action && Filter::checkCsrf()) {
55 55
 	switch ($form_action) {
56
-	case 'update':
57
-		if ($form_username !== Auth::user()->getUserName() && User::findByUserName($form_username)) {
58
-			FlashMessages::addMessage(I18N::translate('Duplicate username. A user with that username already exists. Please choose another username.'));
59
-		} elseif ($form_email !== Auth::user()->getEmail() && User::findByEmail($form_email)) {
60
-			FlashMessages::addMessage(I18N::translate('Duplicate email address. A user with that email already exists.'));
61
-		} else {
62
-			// Change username
63
-			if ($form_username !== Auth::user()->getUserName()) {
64
-				Log::addAuthenticationLog('User ' . Auth::user()->getUserName() . ' renamed to ' . $form_username);
65
-				Auth::user()->setUserName($form_username);
66
-			}
67
-
68
-			// Change password
69
-			if ($form_pass1 && $form_pass1 === $form_pass2) {
70
-				Auth::user()->setPassword($form_pass1);
71
-			}
72
-
73
-			// Change other settings
74
-			Auth::user()
75
-				->setRealName($form_realname)
76
-				->setEmail($form_email)
77
-				->setPreference('language', $form_language)
78
-				->setPreference('TIMEZONE', $form_timezone)
79
-				->setPreference('contactmethod', $form_contact_method)
80
-				->setPreference('visibleonline', $form_visible_online ? '1' : '0');
81
-
82
-			if ($form_theme === null) {
83
-				Auth::user()->deletePreference('theme');
84
-			} else {
85
-				Auth::user()->setPreference('theme', $form_theme);
86
-			}
87
-
88
-			$WT_TREE->setUserPreference(Auth::user(), 'rootid', $form_rootid);
89
-		}
90
-		break;
91
-
92
-	case 'delete':
93
-		// An administrator can only be deleted by another administrator
94
-		if (!Auth::user()->getPreference('canadmin')) {
95
-			// Keep a reference to the currently logged in user because after logging out this user,
96
-			// a call to Auth::user() will not return this user anymore
97
-			$currentUser = Auth::user();
98
-			Auth::logout();
99
-			$currentUser->delete();
100
-		}
101
-		break;
56
+	    case 'update':
57
+		    if ($form_username !== Auth::user()->getUserName() && User::findByUserName($form_username)) {
58
+			    FlashMessages::addMessage(I18N::translate('Duplicate username. A user with that username already exists. Please choose another username.'));
59
+		    } elseif ($form_email !== Auth::user()->getEmail() && User::findByEmail($form_email)) {
60
+			    FlashMessages::addMessage(I18N::translate('Duplicate email address. A user with that email already exists.'));
61
+		    } else {
62
+			    // Change username
63
+			    if ($form_username !== Auth::user()->getUserName()) {
64
+				    Log::addAuthenticationLog('User ' . Auth::user()->getUserName() . ' renamed to ' . $form_username);
65
+				    Auth::user()->setUserName($form_username);
66
+			    }
67
+
68
+			    // Change password
69
+			    if ($form_pass1 && $form_pass1 === $form_pass2) {
70
+				    Auth::user()->setPassword($form_pass1);
71
+			    }
72
+
73
+			    // Change other settings
74
+			    Auth::user()
75
+				    ->setRealName($form_realname)
76
+				    ->setEmail($form_email)
77
+				    ->setPreference('language', $form_language)
78
+				    ->setPreference('TIMEZONE', $form_timezone)
79
+				    ->setPreference('contactmethod', $form_contact_method)
80
+				    ->setPreference('visibleonline', $form_visible_online ? '1' : '0');
81
+
82
+			    if ($form_theme === null) {
83
+				    Auth::user()->deletePreference('theme');
84
+			    } else {
85
+				    Auth::user()->setPreference('theme', $form_theme);
86
+			    }
87
+
88
+			    $WT_TREE->setUserPreference(Auth::user(), 'rootid', $form_rootid);
89
+		    }
90
+		    break;
91
+
92
+	    case 'delete':
93
+		    // An administrator can only be deleted by another administrator
94
+		    if (!Auth::user()->getPreference('canadmin')) {
95
+			    // Keep a reference to the currently logged in user because after logging out this user,
96
+			    // a call to Auth::user() will not return this user anymore
97
+			    $currentUser = Auth::user();
98
+			    Auth::logout();
99
+			    $currentUser->delete();
100
+		    }
101
+		    break;
102 102
 	}
103 103
 
104 104
 	header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
Please login to merge, or discard this patch.
Braces   +5 added lines, -2 removed lines patch added patch discarded remove patch
@@ -183,8 +183,11 @@
 block discarded – undo
183 183
 			<div class="value">
184 184
 				<?php if ($my_individual_record): ?>
185 185
 				<?php echo $my_individual_record->formatList('span'); ?>
186
-				<?php else: ?>
187
-					<?php echo I18N::translateContext('unknown people', 'Unknown'); ?>
186
+				<?php else {
187
+    : ?>
188
+					<?php echo I18N::translateContext('unknown people', 'Unknown');
189
+}
190
+?>
188 191
 				<?php endif; ?>
189 192
 				<p class="small text-muted">
190 193
 					<?php echo I18N::translate('This is a link to your own record in the family tree. If this is the wrong individual, contact an administrator.'); ?>
Please login to merge, or discard this patch.
app/Functions/FunctionsPrintLists.php 4 patches
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -1820,24 +1820,24 @@
 block discarded – undo
1820 1820
 			if ($endjd === WT_CLIENT_JD) {
1821 1821
 				// We're dealing with the Today’s Events block
1822 1822
 				if ($filter === 0) {
1823
-					$html .=  I18N::translate('No events exist for today.');
1823
+					$html .= I18N::translate('No events exist for today.');
1824 1824
 				} else {
1825
-					$html .=  I18N::translate('No events for living individuals exist for today.');
1825
+					$html .= I18N::translate('No events for living individuals exist for today.');
1826 1826
 				}
1827 1827
 			} else {
1828 1828
 				// We're dealing with the Upcoming Events block
1829 1829
 				if ($filter === 0) {
1830 1830
 					if ($endjd === $startjd) {
1831
-						$html .=  I18N::translate('No events exist for tomorrow.');
1831
+						$html .= I18N::translate('No events exist for tomorrow.');
1832 1832
 					} else {
1833
-						$html .=  /* I18N: translation for %s==1 is unused; it is translated separately as “tomorrow” */ I18N::plural('No events exist for the next %s day.', 'No events exist for the next %s days.', $endjd - $startjd + 1, I18N::number($endjd - $startjd + 1));
1833
+						$html .= /* I18N: translation for %s==1 is unused; it is translated separately as “tomorrow” */ I18N::plural('No events exist for the next %s day.', 'No events exist for the next %s days.', $endjd - $startjd + 1, I18N::number($endjd - $startjd + 1));
1834 1834
 					}
1835 1835
 				} else {
1836 1836
 					if ($endjd === $startjd) {
1837
-						$html .=  I18N::translate('No events for living individuals exist for tomorrow.');
1837
+						$html .= I18N::translate('No events for living individuals exist for tomorrow.');
1838 1838
 					} else {
1839 1839
 						// I18N: translation for %s==1 is unused; it is translated separately as “tomorrow”
1840
-						$html .=  I18N::plural('No events for living people exist for the next %s day.', 'No events for living people exist for the next %s days.', $endjd - $startjd + 1, I18N::number($endjd - $startjd + 1));
1840
+						$html .= I18N::plural('No events for living people exist for the next %s day.', 'No events for living people exist for the next %s days.', $endjd - $startjd + 1, I18N::number($endjd - $startjd + 1));
1841 1841
 					}
1842 1842
 				}
1843 1843
 			}
Please login to merge, or discard this patch.
Switch Indentation   +39 added lines, -39 removed lines patch added patch discarded remove patch
@@ -1572,37 +1572,37 @@  discard block
 block discarded – undo
1572 1572
 
1573 1573
 		}
1574 1574
 		switch ($style) {
1575
-		case 1:
1576
-			return '<ul><li>' . implode('</li><li>', $html) . '</li></ul>';
1577
-		case 2:
1578
-			return implode(I18N::$list_separator, $html);
1579
-		case 3:
1580
-			$i     = 0;
1581
-			$count = count($html);
1582
-			if ($count > 36) {
1583
-				$col = 4;
1584
-			} elseif ($count > 18) {
1585
-				$col = 3;
1586
-			} elseif ($count > 6) {
1587
-				$col = 2;
1588
-			} else {
1589
-				$col = 1;
1590
-			}
1591
-			$newcol = ceil($count / $col);
1592
-			$html2  = '<table class="list_table"><tr>';
1593
-			$html2 .= '<td class="list_value" style="padding: 14px;">';
1594
-
1595
-			foreach ($html as $surns) {
1596
-				$html2 .= $surns . '<br>';
1597
-				$i++;
1598
-				if ($i == $newcol && $i < $count) {
1599
-					$html2 .= '</td><td class="list_value" style="padding: 14px;">';
1600
-					$newcol = $i + ceil($count / $col);
1601
-				}
1602
-			}
1603
-			$html2 .= '</td></tr></table>';
1604
-
1605
-			return $html2;
1575
+		    case 1:
1576
+			    return '<ul><li>' . implode('</li><li>', $html) . '</li></ul>';
1577
+		    case 2:
1578
+			    return implode(I18N::$list_separator, $html);
1579
+		    case 3:
1580
+			    $i     = 0;
1581
+			    $count = count($html);
1582
+			    if ($count > 36) {
1583
+				    $col = 4;
1584
+			    } elseif ($count > 18) {
1585
+				    $col = 3;
1586
+			    } elseif ($count > 6) {
1587
+				    $col = 2;
1588
+			    } else {
1589
+				    $col = 1;
1590
+			    }
1591
+			    $newcol = ceil($count / $col);
1592
+			    $html2  = '<table class="list_table"><tr>';
1593
+			    $html2 .= '<td class="list_value" style="padding: 14px;">';
1594
+
1595
+			    foreach ($html as $surns) {
1596
+				    $html2 .= $surns . '<br>';
1597
+				    $i++;
1598
+				    if ($i == $newcol && $i < $count) {
1599
+					    $html2 .= '</td><td class="list_value" style="padding: 14px;">';
1600
+					    $newcol = $i + ceil($count / $col);
1601
+				    }
1602
+			    }
1603
+			    $html2 .= '</td></tr></table>';
1604
+
1605
+			    return $html2;
1606 1606
 		}
1607 1607
 	}
1608 1608
 	/**
@@ -1783,14 +1783,14 @@  discard block
 block discarded – undo
1783 1783
 
1784 1784
 		// Now we've filtered the list, we can sort by event, if required
1785 1785
 		switch ($sort_by) {
1786
-		case 'anniv':
1787
-			// Data is already sorted by anniversary date
1788
-			break;
1789
-		case 'alpha':
1790
-			uasort($filtered_events, function (Fact $x, Fact $y) {
1791
-				return GedcomRecord::compare($x->getParent(), $y->getParent());
1792
-			});
1793
-			break;
1786
+		    case 'anniv':
1787
+			    // Data is already sorted by anniversary date
1788
+			    break;
1789
+		    case 'alpha':
1790
+			    uasort($filtered_events, function (Fact $x, Fact $y) {
1791
+				    return GedcomRecord::compare($x->getParent(), $y->getParent());
1792
+			    });
1793
+			    break;
1794 1794
 		}
1795 1795
 
1796 1796
 		foreach ($filtered_events as $fact) {
Please login to merge, or discard this patch.
Braces   +30 added lines, -15 removed lines patch added patch discarded remove patch
@@ -37,7 +37,8 @@  discard block
 block discarded – undo
37 37
 /**
38 38
  * Class FunctionsPrintLists - create sortable lists using datatables.net
39 39
  */
40
-class FunctionsPrintLists {
40
+class FunctionsPrintLists
41
+{
41 42
 	/**
42 43
 	 * Generate a SURN,GIVN and GIVN,SURN sortable name for an individual.
43 44
 	 * This allows table data to sort by surname or given names.
@@ -50,7 +51,8 @@  discard block
 block discarded – undo
50 51
 	 *
51 52
 	 * @return string[]
52 53
 	 */
53
-	private static function sortableNames(Individual $individual) {
54
+	private static function sortableNames(Individual $individual)
55
+	{
54 56
 		$names   = $individual->getAllNames();
55 57
 		$primary = $individual->getPrimaryName();
56 58
 
@@ -73,7 +75,8 @@  discard block
 block discarded – undo
73 75
 	 *
74 76
 	 * @return string
75 77
 	 */
76
-	public static function individualTable($indiviudals, $option = '') {
78
+	public static function individualTable($indiviudals, $option = '')
79
+	{
77 80
 		global $controller, $WT_TREE;
78 81
 
79 82
 		$table_id = 'table-indi-' . Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
@@ -516,7 +519,8 @@  discard block
 block discarded – undo
516 519
 	 *
517 520
 	 * @return string
518 521
 	 */
519
-	public static function familyTable($families) {
522
+	public static function familyTable($families)
523
+	{
520 524
 		global $WT_TREE, $controller;
521 525
 
522 526
 		$table_id = 'table-fam-' . Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
@@ -985,7 +989,8 @@  discard block
 block discarded – undo
985 989
 	 *
986 990
 	 * @return string
987 991
 	 */
988
-	public static function sourceTable($sources) {
992
+	public static function sourceTable($sources)
993
+	{
989 994
 		global $WT_TREE, $controller;
990 995
 
991 996
 		// Count the number of linked records. These numbers include private records.
@@ -1112,7 +1117,8 @@  discard block
 block discarded – undo
1112 1117
 	 *
1113 1118
 	 * @return string
1114 1119
 	 */
1115
-	public static function noteTable($notes) {
1120
+	public static function noteTable($notes)
1121
+	{
1116 1122
 		global $WT_TREE, $controller;
1117 1123
 
1118 1124
 		// Count the number of linked records. These numbers include private records.
@@ -1218,7 +1224,8 @@  discard block
 block discarded – undo
1218 1224
 	 *
1219 1225
 	 * @return string
1220 1226
 	 */
1221
-	public static function repositoryTable($repositories) {
1227
+	public static function repositoryTable($repositories)
1228
+	{
1222 1229
 		global $WT_TREE, $controller;
1223 1230
 
1224 1231
 		// Count the number of linked records. These numbers include private records.
@@ -1311,7 +1318,8 @@  discard block
 block discarded – undo
1311 1318
 	 *
1312 1319
 	 * @return string
1313 1320
 	 */
1314
-	public static function mediaTable($media_objects) {
1321
+	public static function mediaTable($media_objects)
1322
+	{
1315 1323
 		global $WT_TREE, $controller;
1316 1324
 
1317 1325
 		$html     = '';
@@ -1404,7 +1412,8 @@  discard block
 block discarded – undo
1404 1412
 	 *
1405 1413
 	 * @return string
1406 1414
 	 */
1407
-	public static function surnameTable($surnames, $script, Tree $tree) {
1415
+	public static function surnameTable($surnames, $script, Tree $tree)
1416
+	{
1408 1417
 		global $controller;
1409 1418
 
1410 1419
 		$html = '';
@@ -1494,7 +1503,8 @@  discard block
 block discarded – undo
1494 1503
 	 *
1495 1504
 	 * @return string
1496 1505
 	 */
1497
-	public static function surnameTagCloud($surnames, $script, $totals, Tree $tree) {
1506
+	public static function surnameTagCloud($surnames, $script, $totals, Tree $tree)
1507
+	{
1498 1508
 		$minimum = PHP_INT_MAX;
1499 1509
 		$maximum = 1;
1500 1510
 		foreach ($surnames as $surn => $surns) {
@@ -1537,7 +1547,8 @@  discard block
 block discarded – undo
1537 1547
 	 *
1538 1548
 	 * @return string
1539 1549
 	 */
1540
-	public static function surnameList($surnames, $style, $totals, $script, Tree $tree) {
1550
+	public static function surnameList($surnames, $style, $totals, $script, Tree $tree)
1551
+	{
1541 1552
 		$html = array();
1542 1553
 		foreach ($surnames as $surn => $surns) {
1543 1554
 			// Each surname links back to the indilist
@@ -1616,7 +1627,8 @@  discard block
 block discarded – undo
1616 1627
 	 *
1617 1628
 	 * @return string
1618 1629
 	 */
1619
-	public static function eventsTable($startjd, $endjd, $events = 'BIRT MARR DEAT', $only_living = false, $sort_by = 'anniv') {
1630
+	public static function eventsTable($startjd, $endjd, $events = 'BIRT MARR DEAT', $only_living = false, $sort_by = 'anniv')
1631
+	{
1620 1632
 		global $controller, $WT_TREE;
1621 1633
 
1622 1634
 		$html     = '';
@@ -1746,7 +1758,8 @@  discard block
 block discarded – undo
1746 1758
 	 *
1747 1759
 	 * @return string
1748 1760
 	 */
1749
-	public static function eventsList($startjd, $endjd, $events = 'BIRT MARR DEAT', $only_living = false, $sort_by = 'anniv') {
1761
+	public static function eventsList($startjd, $endjd, $events = 'BIRT MARR DEAT', $only_living = false, $sort_by = 'anniv')
1762
+	{
1750 1763
 		global $WT_TREE;
1751 1764
 
1752 1765
 		// Did we have any output? Did we skip anything?
@@ -1856,7 +1869,8 @@  discard block
 block discarded – undo
1856 1869
 	 *
1857 1870
 	 * @return string
1858 1871
 	 */
1859
-	public static function chartByAge($data, $title) {
1872
+	public static function chartByAge($data, $title)
1873
+	{
1860 1874
 		$count  = 0;
1861 1875
 		$agemax = 0;
1862 1876
 		$vmax   = 0;
@@ -1927,7 +1941,8 @@  discard block
 block discarded – undo
1927 1941
 	 *
1928 1942
 	 * @return string
1929 1943
 	 */
1930
-	public static function chartByDecade($data, $title) {
1944
+	public static function chartByDecade($data, $title)
1945
+	{
1931 1946
 		$count = 0;
1932 1947
 		$vmax  = 0;
1933 1948
 		foreach ($data as $v) {
Please login to merge, or discard this patch.
Indentation   +1341 added lines, -1341 removed lines patch added patch discarded remove patch
@@ -37,49 +37,49 @@  discard block
 block discarded – undo
37 37
  * Class FunctionsPrintLists - create sortable lists using datatables.net
38 38
  */
39 39
 class FunctionsPrintLists {
40
-	/**
41
-	 * Generate a SURN,GIVN and GIVN,SURN sortable name for an individual.
42
-	 * This allows table data to sort by surname or given names.
43
-	 *
44
-	 * Use AAAA as a separator (instead of ","), as Javascript localeCompare()
45
-	 * ignores punctuation and "ANN,ROACH" would sort after "ANNE,ROACH",
46
-	 * instead of before it.
47
-	 *
48
-	 * @param Individual $individual
49
-	 *
50
-	 * @return string[]
51
-	 */
52
-	private static function sortableNames(Individual $individual) {
53
-		$names   = $individual->getAllNames();
54
-		$primary = $individual->getPrimaryName();
55
-
56
-		list($surn, $givn) = explode(',', $names[$primary]['sort']);
57
-
58
-		$givn = str_replace('@P.N.', 'AAAA', $givn);
59
-		$surn = str_replace('@N.N.', 'AAAA', $surn);
60
-
61
-		return array(
62
-			$surn . 'AAAA' . $givn,
63
-			$givn . 'AAAA' . $surn,
64
-		);
65
-	}
66
-
67
-	/**
68
-	 * Print a table of individuals
69
-	 *
70
-	 * @param Individual[] $indiviudals
71
-	 * @param string       $option
72
-	 *
73
-	 * @return string
74
-	 */
75
-	public static function individualTable($indiviudals, $option = '') {
76
-		global $controller, $WT_TREE;
77
-
78
-		$table_id = 'table-indi-' . Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
79
-
80
-		$controller
81
-			->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
82
-			->addInlineJavascript('
40
+    /**
41
+     * Generate a SURN,GIVN and GIVN,SURN sortable name for an individual.
42
+     * This allows table data to sort by surname or given names.
43
+     *
44
+     * Use AAAA as a separator (instead of ","), as Javascript localeCompare()
45
+     * ignores punctuation and "ANN,ROACH" would sort after "ANNE,ROACH",
46
+     * instead of before it.
47
+     *
48
+     * @param Individual $individual
49
+     *
50
+     * @return string[]
51
+     */
52
+    private static function sortableNames(Individual $individual) {
53
+        $names   = $individual->getAllNames();
54
+        $primary = $individual->getPrimaryName();
55
+
56
+        list($surn, $givn) = explode(',', $names[$primary]['sort']);
57
+
58
+        $givn = str_replace('@P.N.', 'AAAA', $givn);
59
+        $surn = str_replace('@N.N.', 'AAAA', $surn);
60
+
61
+        return array(
62
+            $surn . 'AAAA' . $givn,
63
+            $givn . 'AAAA' . $surn,
64
+        );
65
+    }
66
+
67
+    /**
68
+     * Print a table of individuals
69
+     *
70
+     * @param Individual[] $indiviudals
71
+     * @param string       $option
72
+     *
73
+     * @return string
74
+     */
75
+    public static function individualTable($indiviudals, $option = '') {
76
+        global $controller, $WT_TREE;
77
+
78
+        $table_id = 'table-indi-' . Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
79
+
80
+        $controller
81
+            ->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
82
+            ->addInlineJavascript('
83 83
 				jQuery.fn.dataTableExt.oSort["text-asc"] = textCompareAsc;
84 84
 				jQuery.fn.dataTableExt.oSort["text-desc"] = textCompareDesc;
85 85
 				jQuery("#' . $table_id . '").dataTable( {
@@ -143,21 +143,21 @@  discard block
 block discarded – undo
143 143
 				jQuery(".loading-image").css("display", "none");
144 144
 			');
145 145
 
146
-		$max_age = (int) $WT_TREE->getPreference('MAX_ALIVE_AGE');
147
-
148
-		// Inititialise chart data
149
-		$deat_by_age = array();
150
-		for ($age = 0; $age <= $max_age; $age++) {
151
-			$deat_by_age[$age] = '';
152
-		}
153
-		$birt_by_decade = array();
154
-		$deat_by_decade = array();
155
-		for ($year = 1550; $year < 2030; $year += 10) {
156
-			$birt_by_decade[$year] = '';
157
-			$deat_by_decade[$year] = '';
158
-		}
159
-
160
-		$html = '
146
+        $max_age = (int) $WT_TREE->getPreference('MAX_ALIVE_AGE');
147
+
148
+        // Inititialise chart data
149
+        $deat_by_age = array();
150
+        for ($age = 0; $age <= $max_age; $age++) {
151
+            $deat_by_age[$age] = '';
152
+        }
153
+        $birt_by_decade = array();
154
+        $deat_by_decade = array();
155
+        for ($year = 1550; $year < 2030; $year += 10) {
156
+            $birt_by_decade[$year] = '';
157
+            $deat_by_decade[$year] = '';
158
+        }
159
+
160
+        $html = '
161 161
 			<div class="loading-image"></div>
162 162
 			<div class="indi-list">
163 163
 				<table id="' . $table_id . '">
@@ -312,175 +312,175 @@  discard block
 block discarded – undo
312 312
 					</tfoot>
313 313
 					<tbody>';
314 314
 
315
-		$hundred_years_ago = new Date(date('Y') - 100);
316
-		$unique_indis      = array(); // Don't double-count indis with multiple names.
317
-
318
-		foreach ($indiviudals as $key => $individual) {
319
-			if (!$individual->canShowName()) {
320
-				continue;
321
-			}
322
-			if ($individual->isPendingAddtion()) {
323
-				$class = ' class="new"';
324
-			} elseif ($individual->isPendingDeletion()) {
325
-				$class = ' class="old"';
326
-			} else {
327
-				$class = '';
328
-			}
329
-			$html .= '<tr' . $class . '>';
330
-			// Extract Given names and Surnames for sorting
331
-			list($surn_givn, $givn_surn) = self::sortableNames($individual);
332
-
333
-			$html .= '<td colspan="2" data-sort="' . Filter::escapeHtml($givn_surn) . '">';
334
-			foreach ($individual->getAllNames() as $num => $name) {
335
-				if ($name['type'] == 'NAME') {
336
-					$title = '';
337
-				} else {
338
-					$title = 'title="' . strip_tags(GedcomTag::getLabel($name['type'], $individual)) . '"';
339
-				}
340
-				if ($num == $individual->getPrimaryName()) {
341
-					$class             = ' class="name2"';
342
-					$sex_image         = $individual->getSexImage();
343
-				} else {
344
-					$class     = '';
345
-					$sex_image = '';
346
-				}
347
-				$html .= '<a ' . $title . ' href="' . $individual->getHtmlUrl() . '"' . $class . '>' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>' . $sex_image . '<br>';
348
-			}
349
-			$html .= $individual->getPrimaryParentsNames('parents details1', 'none');
350
-			$html .= '</td>';
351
-
352
-			// Hidden column for sortable name
353
-			$html .= '<td hidden data-sort="' . Filter::escapeHtml($surn_givn) . '"></td>';
354
-
355
-			// SOSA
356
-			$html .= '<td class="center" data-sort="' . $key . '">';
357
-			if ($option === 'sosa') {
358
-				$html .= '<a href="relationship.php?pid1=' . $indiviudals[1] . '&amp;pid2=' . $individual->getXref() . '" title="' . I18N::translate('Relationships') . '">' . I18N::number($key) . '</a>';
359
-			}
360
-			$html .= '</td>';
361
-
362
-			// Birth date
363
-			$birth_dates = $individual->getAllBirthDates();
364
-			$html .= '<td data-sort="' . $individual->getEstimatedBirthDate()->julianDay() . '">';
365
-			foreach ($birth_dates as $n => $birth_date) {
366
-				if ($n > 0) {
367
-					$html .= '<br>';
368
-				}
369
-				$html .= $birth_date->display(true);
370
-			}
371
-			$html .= '</td>';
372
-
373
-			// Birth anniversary
374
-			if (isset($birth_dates[0]) && $birth_dates[0]->gregorianYear() >= 1550 && $birth_dates[0]->gregorianYear() < 2030 && !isset($unique_indis[$individual->getXref()])) {
375
-				$birt_by_decade[(int) ($birth_dates[0]->gregorianYear() / 10) * 10] .= $individual->getSex();
376
-				$anniversary = Date::getAge($birth_dates[0], null, 2);
377
-			} else {
378
-				$anniversary = '';
379
-			}
380
-			$html .= '<td class="center" data-sort="' . -$individual->getEstimatedBirthDate()->julianDay() . '">' . $anniversary . '</td>';
381
-
382
-			// Birth place
383
-			$html .= '<td>';
384
-			foreach ($individual->getAllBirthPlaces() as $n => $birth_place) {
385
-				$tmp = new Place($birth_place, $individual->getTree());
386
-				if ($n > 0) {
387
-					$html .= '<br>';
388
-				}
389
-				$html .= '<a href="' . $tmp->getURL() . '" title="' . strip_tags($tmp->getFullName()) . '">';
390
-				$html .= FunctionsPrint::highlightSearchHits($tmp->getShortName()) . '</a>';
391
-			}
392
-			$html .= '</td>';
393
-
394
-			// Number of children
395
-			$number_of_children = $individual->getNumberOfChildren();
396
-			$html .= '<td class="center" data-sort="' . $number_of_children . '">' . I18N::number($number_of_children) . '</td>';
397
-
398
-			// Death date
399
-			$death_dates = $individual->getAllDeathDates();
400
-			$html .= '<td data-sort="' . $individual->getEstimatedDeathDate()->julianDay() . '">';
401
-			foreach ($death_dates as $num => $death_date) {
402
-				if ($num) {
403
-					$html .= '<br>';
404
-				}
405
-				$html .= $death_date->display(true);
406
-			}
407
-			$html .= '</td>';
408
-
409
-			// Death anniversary
410
-			if (isset($death_dates[0]) && $death_dates[0]->gregorianYear() >= 1550 && $death_dates[0]->gregorianYear() < 2030 && !isset($unique_indis[$individual->getXref()])) {
411
-				$birt_by_decade[(int) ($death_dates[0]->gregorianYear() / 10) * 10] .= $individual->getSex();
412
-				$anniversary = Date::getAge($death_dates[0], null, 2);
413
-			} else {
414
-				$anniversary = '';
415
-			}
416
-			$html .= '<td class="center" data-sort="' . -$individual->getEstimatedDeathDate()->julianDay() . '">' . $anniversary . '</td>';
417
-
418
-			// Age at death
419
-			if (isset($birth_dates[0]) && isset($death_dates[0])) {
420
-				$age_at_death      = Date::getAge($birth_dates[0], $death_dates[0], 0);
421
-				$age_at_death_sort = Date::getAge($birth_dates[0], $death_dates[0], 2);
422
-				if (!isset($unique_indis[$individual->getXref()]) && $age >= 0 && $age <= $max_age) {
423
-					$deat_by_age[$age_at_death] .= $individual->getSex();
424
-				}
425
-			} else {
426
-				$age_at_death      = '';
427
-				$age_at_death_sort = PHP_INT_MAX;
428
-			}
429
-			$html .= '<td class="center" data-sort="' . $age_at_death_sort . '">' . $age_at_death . '</td>';
430
-
431
-			// Death place
432
-			$html .= '<td>';
433
-			foreach ($individual->getAllDeathPlaces() as $n => $death_place) {
434
-				$tmp = new Place($death_place, $individual->getTree());
435
-				if ($n > 0) {
436
-					$html .= '<br>';
437
-				}
438
-				$html .= '<a href="' . $tmp->getURL() . '" title="' . strip_tags($tmp->getFullName()) . '">';
439
-				$html .= FunctionsPrint::highlightSearchHits($tmp->getShortName()) . '</a>';
440
-			}
441
-			$html .= '</td>';
442
-
443
-			// Last change
444
-			$html .= '<td data-sort="' . $individual->lastChangeTimestamp(true) . '">' . $individual->lastChangeTimestamp() . '</td>';
445
-
446
-			// Filter by sex
447
-			$html .= '<td hidden>' . $individual->getSex() . '</td>';
448
-
449
-			// Filter by birth date
450
-			$html .= '<td hidden>';
451
-			if (!$individual->canShow() || Date::compare($individual->getEstimatedBirthDate(), $hundred_years_ago) > 0) {
452
-				$html .= 'Y100';
453
-			} else {
454
-				$html .= 'YES';
455
-			}
456
-			$html .= '</td>';
457
-
458
-			// Filter by death date
459
-			$html .= '<td hidden>';
460
-			// Died in last 100 years? Died? Not dead?
461
-			if (isset($death_dates[0]) && Date::compare($death_dates[0], $hundred_years_ago) > 0) {
462
-				$html .= 'Y100';
463
-			} elseif ($individual->isDead()) {
464
-				$html .= 'YES';
465
-			} else {
466
-				$html .= 'N';
467
-			}
468
-			$html .= '</td>';
469
-
470
-			// Filter by roots/leaves
471
-			$html .= '<td hidden>';
472
-			if (!$individual->getChildFamilies()) {
473
-				$html .= 'R';
474
-			} elseif (!$individual->isDead() && $individual->getNumberOfChildren() < 1) {
475
-				$html .= 'L';
476
-				$html .= '&nbsp;';
477
-			}
478
-			$html .= '</td>';
479
-			$html .= '</tr>';
480
-
481
-			$unique_indis[$individual->getXref()] = true;
482
-		}
483
-		$html .= '
315
+        $hundred_years_ago = new Date(date('Y') - 100);
316
+        $unique_indis      = array(); // Don't double-count indis with multiple names.
317
+
318
+        foreach ($indiviudals as $key => $individual) {
319
+            if (!$individual->canShowName()) {
320
+                continue;
321
+            }
322
+            if ($individual->isPendingAddtion()) {
323
+                $class = ' class="new"';
324
+            } elseif ($individual->isPendingDeletion()) {
325
+                $class = ' class="old"';
326
+            } else {
327
+                $class = '';
328
+            }
329
+            $html .= '<tr' . $class . '>';
330
+            // Extract Given names and Surnames for sorting
331
+            list($surn_givn, $givn_surn) = self::sortableNames($individual);
332
+
333
+            $html .= '<td colspan="2" data-sort="' . Filter::escapeHtml($givn_surn) . '">';
334
+            foreach ($individual->getAllNames() as $num => $name) {
335
+                if ($name['type'] == 'NAME') {
336
+                    $title = '';
337
+                } else {
338
+                    $title = 'title="' . strip_tags(GedcomTag::getLabel($name['type'], $individual)) . '"';
339
+                }
340
+                if ($num == $individual->getPrimaryName()) {
341
+                    $class             = ' class="name2"';
342
+                    $sex_image         = $individual->getSexImage();
343
+                } else {
344
+                    $class     = '';
345
+                    $sex_image = '';
346
+                }
347
+                $html .= '<a ' . $title . ' href="' . $individual->getHtmlUrl() . '"' . $class . '>' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>' . $sex_image . '<br>';
348
+            }
349
+            $html .= $individual->getPrimaryParentsNames('parents details1', 'none');
350
+            $html .= '</td>';
351
+
352
+            // Hidden column for sortable name
353
+            $html .= '<td hidden data-sort="' . Filter::escapeHtml($surn_givn) . '"></td>';
354
+
355
+            // SOSA
356
+            $html .= '<td class="center" data-sort="' . $key . '">';
357
+            if ($option === 'sosa') {
358
+                $html .= '<a href="relationship.php?pid1=' . $indiviudals[1] . '&amp;pid2=' . $individual->getXref() . '" title="' . I18N::translate('Relationships') . '">' . I18N::number($key) . '</a>';
359
+            }
360
+            $html .= '</td>';
361
+
362
+            // Birth date
363
+            $birth_dates = $individual->getAllBirthDates();
364
+            $html .= '<td data-sort="' . $individual->getEstimatedBirthDate()->julianDay() . '">';
365
+            foreach ($birth_dates as $n => $birth_date) {
366
+                if ($n > 0) {
367
+                    $html .= '<br>';
368
+                }
369
+                $html .= $birth_date->display(true);
370
+            }
371
+            $html .= '</td>';
372
+
373
+            // Birth anniversary
374
+            if (isset($birth_dates[0]) && $birth_dates[0]->gregorianYear() >= 1550 && $birth_dates[0]->gregorianYear() < 2030 && !isset($unique_indis[$individual->getXref()])) {
375
+                $birt_by_decade[(int) ($birth_dates[0]->gregorianYear() / 10) * 10] .= $individual->getSex();
376
+                $anniversary = Date::getAge($birth_dates[0], null, 2);
377
+            } else {
378
+                $anniversary = '';
379
+            }
380
+            $html .= '<td class="center" data-sort="' . -$individual->getEstimatedBirthDate()->julianDay() . '">' . $anniversary . '</td>';
381
+
382
+            // Birth place
383
+            $html .= '<td>';
384
+            foreach ($individual->getAllBirthPlaces() as $n => $birth_place) {
385
+                $tmp = new Place($birth_place, $individual->getTree());
386
+                if ($n > 0) {
387
+                    $html .= '<br>';
388
+                }
389
+                $html .= '<a href="' . $tmp->getURL() . '" title="' . strip_tags($tmp->getFullName()) . '">';
390
+                $html .= FunctionsPrint::highlightSearchHits($tmp->getShortName()) . '</a>';
391
+            }
392
+            $html .= '</td>';
393
+
394
+            // Number of children
395
+            $number_of_children = $individual->getNumberOfChildren();
396
+            $html .= '<td class="center" data-sort="' . $number_of_children . '">' . I18N::number($number_of_children) . '</td>';
397
+
398
+            // Death date
399
+            $death_dates = $individual->getAllDeathDates();
400
+            $html .= '<td data-sort="' . $individual->getEstimatedDeathDate()->julianDay() . '">';
401
+            foreach ($death_dates as $num => $death_date) {
402
+                if ($num) {
403
+                    $html .= '<br>';
404
+                }
405
+                $html .= $death_date->display(true);
406
+            }
407
+            $html .= '</td>';
408
+
409
+            // Death anniversary
410
+            if (isset($death_dates[0]) && $death_dates[0]->gregorianYear() >= 1550 && $death_dates[0]->gregorianYear() < 2030 && !isset($unique_indis[$individual->getXref()])) {
411
+                $birt_by_decade[(int) ($death_dates[0]->gregorianYear() / 10) * 10] .= $individual->getSex();
412
+                $anniversary = Date::getAge($death_dates[0], null, 2);
413
+            } else {
414
+                $anniversary = '';
415
+            }
416
+            $html .= '<td class="center" data-sort="' . -$individual->getEstimatedDeathDate()->julianDay() . '">' . $anniversary . '</td>';
417
+
418
+            // Age at death
419
+            if (isset($birth_dates[0]) && isset($death_dates[0])) {
420
+                $age_at_death      = Date::getAge($birth_dates[0], $death_dates[0], 0);
421
+                $age_at_death_sort = Date::getAge($birth_dates[0], $death_dates[0], 2);
422
+                if (!isset($unique_indis[$individual->getXref()]) && $age >= 0 && $age <= $max_age) {
423
+                    $deat_by_age[$age_at_death] .= $individual->getSex();
424
+                }
425
+            } else {
426
+                $age_at_death      = '';
427
+                $age_at_death_sort = PHP_INT_MAX;
428
+            }
429
+            $html .= '<td class="center" data-sort="' . $age_at_death_sort . '">' . $age_at_death . '</td>';
430
+
431
+            // Death place
432
+            $html .= '<td>';
433
+            foreach ($individual->getAllDeathPlaces() as $n => $death_place) {
434
+                $tmp = new Place($death_place, $individual->getTree());
435
+                if ($n > 0) {
436
+                    $html .= '<br>';
437
+                }
438
+                $html .= '<a href="' . $tmp->getURL() . '" title="' . strip_tags($tmp->getFullName()) . '">';
439
+                $html .= FunctionsPrint::highlightSearchHits($tmp->getShortName()) . '</a>';
440
+            }
441
+            $html .= '</td>';
442
+
443
+            // Last change
444
+            $html .= '<td data-sort="' . $individual->lastChangeTimestamp(true) . '">' . $individual->lastChangeTimestamp() . '</td>';
445
+
446
+            // Filter by sex
447
+            $html .= '<td hidden>' . $individual->getSex() . '</td>';
448
+
449
+            // Filter by birth date
450
+            $html .= '<td hidden>';
451
+            if (!$individual->canShow() || Date::compare($individual->getEstimatedBirthDate(), $hundred_years_ago) > 0) {
452
+                $html .= 'Y100';
453
+            } else {
454
+                $html .= 'YES';
455
+            }
456
+            $html .= '</td>';
457
+
458
+            // Filter by death date
459
+            $html .= '<td hidden>';
460
+            // Died in last 100 years? Died? Not dead?
461
+            if (isset($death_dates[0]) && Date::compare($death_dates[0], $hundred_years_ago) > 0) {
462
+                $html .= 'Y100';
463
+            } elseif ($individual->isDead()) {
464
+                $html .= 'YES';
465
+            } else {
466
+                $html .= 'N';
467
+            }
468
+            $html .= '</td>';
469
+
470
+            // Filter by roots/leaves
471
+            $html .= '<td hidden>';
472
+            if (!$individual->getChildFamilies()) {
473
+                $html .= 'R';
474
+            } elseif (!$individual->isDead() && $individual->getNumberOfChildren() < 1) {
475
+                $html .= 'L';
476
+                $html .= '&nbsp;';
477
+            }
478
+            $html .= '</td>';
479
+            $html .= '</tr>';
480
+
481
+            $unique_indis[$individual->getXref()] = true;
482
+        }
483
+        $html .= '
484 484
 					</tbody>
485 485
 				</table>
486 486
 				<div id="indi_list_table-charts_' . $table_id . '" style="display:none">
@@ -502,24 +502,24 @@  discard block
 block discarded – undo
502 502
 				</div>
503 503
 			</div>';
504 504
 
505
-		return $html;
506
-	}
505
+        return $html;
506
+    }
507 507
 
508
-	/**
509
-	 * Print a table of families
510
-	 *
511
-	 * @param Family[] $families
512
-	 *
513
-	 * @return string
514
-	 */
515
-	public static function familyTable($families) {
516
-		global $WT_TREE, $controller;
508
+    /**
509
+     * Print a table of families
510
+     *
511
+     * @param Family[] $families
512
+     *
513
+     * @return string
514
+     */
515
+    public static function familyTable($families) {
516
+        global $WT_TREE, $controller;
517 517
 
518
-		$table_id = 'table-fam-' . Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
518
+        $table_id = 'table-fam-' . Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
519 519
 
520
-		$controller
521
-			->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
522
-			->addInlineJavascript('
520
+        $controller
521
+            ->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
522
+            ->addInlineJavascript('
523 523
 				jQuery.fn.dataTableExt.oSort["text-asc"] = textCompareAsc;
524 524
 				jQuery.fn.dataTableExt.oSort["text-desc"] = textCompareDesc;
525 525
 				jQuery("#' . $table_id . '").dataTable( {
@@ -581,21 +581,21 @@  discard block
 block discarded – undo
581 581
 				jQuery(".loading-image").css("display", "none");
582 582
 		');
583 583
 
584
-		$max_age = (int) $WT_TREE->getPreference('MAX_ALIVE_AGE');
585
-
586
-		// init chart data
587
-		$marr_by_age = array();
588
-		for ($age = 0; $age <= $max_age; $age++) {
589
-			$marr_by_age[$age] = '';
590
-		}
591
-		$birt_by_decade = array();
592
-		$marr_by_decade = array();
593
-		for ($year = 1550; $year < 2030; $year += 10) {
594
-			$birt_by_decade[$year] = '';
595
-			$marr_by_decade[$year] = '';
596
-		}
597
-
598
-		$html = '
584
+        $max_age = (int) $WT_TREE->getPreference('MAX_ALIVE_AGE');
585
+
586
+        // init chart data
587
+        $marr_by_age = array();
588
+        for ($age = 0; $age <= $max_age; $age++) {
589
+            $marr_by_age[$age] = '';
590
+        }
591
+        $birt_by_decade = array();
592
+        $marr_by_decade = array();
593
+        for ($year = 1550; $year < 2030; $year += 10) {
594
+            $birt_by_decade[$year] = '';
595
+            $marr_by_decade[$year] = '';
596
+        }
597
+
598
+        $html = '
599 599
 			<div class="loading-image"></div>
600 600
 			<div class="fam-list">
601 601
 				<table id="' . $table_id . '">
@@ -746,215 +746,215 @@  discard block
 block discarded – undo
746 746
 					</tfoot>
747 747
 					<tbody>';
748 748
 
749
-		$hundred_years_ago = new Date(date('Y') - 100);
750
-
751
-		foreach ($families as $family) {
752
-			// Retrieve husband and wife
753
-			$husb = $family->getHusband();
754
-			if (is_null($husb)) {
755
-				$husb = new Individual('H', '0 @H@ INDI', null, $family->getTree());
756
-			}
757
-			$wife = $family->getWife();
758
-			if (is_null($wife)) {
759
-				$wife = new Individual('W', '0 @W@ INDI', null, $family->getTree());
760
-			}
761
-			if (!$family->canShow()) {
762
-				continue;
763
-			}
764
-			if ($family->isPendingAddtion()) {
765
-				$class = ' class="new"';
766
-			} elseif ($family->isPendingDeletion()) {
767
-				$class = ' class="old"';
768
-			} else {
769
-				$class = '';
770
-			}
771
-			$html .= '<tr' . $class . '>';
772
-			// Husband name(s)
773
-			// Extract Given names and Surnames for sorting
774
-			list($surn_givn, $givn_surn) = self::sortableNames($husb);
775
-
776
-			$html .= '<td colspan="2" data-sort="' . Filter::escapeHtml($givn_surn) . '">';
777
-			foreach ($husb->getAllNames() as $num => $name) {
778
-				if ($name['type'] == 'NAME') {
779
-					$title = '';
780
-				} else {
781
-					$title = 'title="' . strip_tags(GedcomTag::getLabel($name['type'], $husb)) . '"';
782
-				}
783
-				if ($num == $husb->getPrimaryName()) {
784
-					$class             = ' class="name2"';
785
-					$sex_image         = $husb->getSexImage();
786
-				} else {
787
-					$class     = '';
788
-					$sex_image = '';
789
-				}
790
-				// Only show married names if they are the name we are filtering by.
791
-				if ($name['type'] != '_MARNM' || $num == $husb->getPrimaryName()) {
792
-					$html .= '<a ' . $title . ' href="' . $family->getHtmlUrl() . '"' . $class . '>' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>' . $sex_image . '<br>';
793
-				}
794
-			}
795
-			// Husband parents
796
-			$html .= $husb->getPrimaryParentsNames('parents details1', 'none');
797
-			$html .= '</td>';
798
-
799
-			// Hidden column for sortable name
800
-			$html .= '<td hidden data-sort="' . Filter::escapeHtml($surn_givn) . '"></td>';
801
-
802
-			// Husband age
803
-			$mdate = $family->getMarriageDate();
804
-			$hdate = $husb->getBirthDate();
805
-			if ($hdate->isOK() && $mdate->isOK()) {
806
-				if ($hdate->gregorianYear() >= 1550 && $hdate->gregorianYear() < 2030) {
807
-					$birt_by_decade[(int) ($hdate->gregorianYear() / 10) * 10] .= $husb->getSex();
808
-				}
809
-				$hage = Date::getAge($hdate, $mdate, 0);
810
-				if ($hage >= 0 && $hage <= $max_age) {
811
-					$marr_by_age[$hage] .= $husb->getSex();
812
-				}
813
-			}
814
-			$html .= '<td class="center" data=-sort="' . Date::getAge($hdate, $mdate, 1) . '">' . Date::getAge($hdate, $mdate, 2) . '</td>';
815
-
816
-			// Wife name(s)
817
-			// Extract Given names and Surnames for sorting
818
-			list($surn_givn, $givn_surn) = self::sortableNames($wife);
819
-			$html .= '<td colspan="2" data-sort="' . Filter::escapeHtml($givn_surn) . '">';
820
-			foreach ($wife->getAllNames() as $num => $name) {
821
-				if ($name['type'] == 'NAME') {
822
-					$title = '';
823
-				} else {
824
-					$title = 'title="' . strip_tags(GedcomTag::getLabel($name['type'], $wife)) . '"';
825
-				}
826
-				if ($num == $wife->getPrimaryName()) {
827
-					$class             = ' class="name2"';
828
-					$sex_image         = $wife->getSexImage();
829
-				} else {
830
-					$class     = '';
831
-					$sex_image = '';
832
-				}
833
-				// Only show married names if they are the name we are filtering by.
834
-				if ($name['type'] != '_MARNM' || $num == $wife->getPrimaryName()) {
835
-					$html .= '<a ' . $title . ' href="' . $family->getHtmlUrl() . '"' . $class . '>' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>' . $sex_image . '<br>';
836
-				}
837
-			}
838
-			// Wife parents
839
-			$html .= $wife->getPrimaryParentsNames('parents details1', 'none');
840
-			$html .= '</td>';
841
-
842
-			// Hidden column for sortable name
843
-			$html .= '<td hidden data-sort="' . Filter::escapeHtml($surn_givn) . '"></td>';
844
-
845
-			// Wife age
846
-			$mdate = $family->getMarriageDate();
847
-			$wdate = $wife->getBirthDate();
848
-			if ($wdate->isOK() && $mdate->isOK()) {
849
-				if ($wdate->gregorianYear() >= 1550 && $wdate->gregorianYear() < 2030) {
850
-					$birt_by_decade[(int) ($wdate->gregorianYear() / 10) * 10] .= $wife->getSex();
851
-				}
852
-				$wage = Date::getAge($wdate, $mdate, 0);
853
-				if ($wage >= 0 && $wage <= $max_age) {
854
-					$marr_by_age[$wage] .= $wife->getSex();
855
-				}
856
-			}
857
-			$html .= '<td class="center" data-sort="' . Date::getAge($wdate, $mdate, 1) . '">' . Date::getAge($wdate, $mdate, 2) . '</td>';
858
-
859
-			// Marriage date
860
-			$html .= '<td data-sort="' . $family->getMarriageDate()->julianDay() . '">';
861
-			if ($marriage_dates = $family->getAllMarriageDates()) {
862
-				foreach ($marriage_dates as $n => $marriage_date) {
863
-					if ($n) {
864
-						$html .= '<br>';
865
-					}
866
-					$html .= '<div>' . $marriage_date->display(true) . '</div>';
867
-				}
868
-				if ($marriage_dates[0]->gregorianYear() >= 1550 && $marriage_dates[0]->gregorianYear() < 2030) {
869
-					$marr_by_decade[(int) ($marriage_dates[0]->gregorianYear() / 10) * 10] .= $husb->getSex() . $wife->getSex();
870
-				}
871
-			} elseif ($family->getFacts('_NMR')) {
872
-				$html .= I18N::translate('no');
873
-			} elseif ($family->getFacts('MARR')) {
874
-				$html .= I18N::translate('yes');
875
-			} else {
876
-				$html .= '&nbsp;';
877
-			}
878
-			$html .= '</td>';
879
-
880
-			// Marriage anniversary
881
-			$html .= '<td class="center" data-sort="' . -$family->getMarriageDate()->julianDay() . '">' . Date::getAge($family->getMarriageDate(), null, 2) . '</td>';
882
-
883
-			// Marriage place
884
-			$html .= '<td>';
885
-			foreach ($family->getAllMarriagePlaces() as $n => $marriage_place) {
886
-				$tmp = new Place($marriage_place, $family->getTree());
887
-				if ($n) {
888
-					$html .= '<br>';
889
-				}
890
-				$html .= '<a href="' . $tmp->getURL() . '" title="' . strip_tags($tmp->getFullName()) . '">';
891
-				$html .= FunctionsPrint::highlightSearchHits($tmp->getShortName()) . '</a>';
892
-			}
893
-			$html .= '</td>';
894
-
895
-			// Number of children
896
-			$html .= '<td class="center" data-sort="' . $family->getNumberOfChildren() . '">' . I18N::number($family->getNumberOfChildren()) . '</td>';
897
-
898
-			// Last change
899
-			$html .= '<td data-sort="' . $family->lastChangeTimestamp(true) . '">' . $family->lastChangeTimestamp() . '</td>';
900
-
901
-			// Filter by marriage date
902
-			$html .= '<td hidden>';
903
-			if (!$family->canShow() || !$mdate->isOK()) {
904
-				$html .= 'U';
905
-			} else {
906
-				if (Date::compare($mdate, $hundred_years_ago) > 0) {
907
-					$html .= 'Y100';
908
-				} else {
909
-					$html .= 'YES';
910
-				}
911
-			}
912
-			if ($family->getFacts(WT_EVENTS_DIV)) {
913
-				$html .= 'D';
914
-			}
915
-			if (count($husb->getSpouseFamilies()) > 1 || count($wife->getSpouseFamilies()) > 1) {
916
-				$html .= 'M';
917
-			}
918
-			$html .= '</td>';
919
-
920
-			// Filter by alive/dead
921
-			$html .= '<td hidden>';
922
-			if ($husb->isDead() && $wife->isDead()) {
923
-				$html .= 'Y';
924
-			}
925
-			if ($husb->isDead() && !$wife->isDead()) {
926
-				if ($wife->getSex() == 'F') {
927
-					$html .= 'H';
928
-				}
929
-				if ($wife->getSex() == 'M') {
930
-					$html .= 'W';
931
-				} // male partners
932
-			}
933
-			if (!$husb->isDead() && $wife->isDead()) {
934
-				if ($husb->getSex() == 'M') {
935
-					$html .= 'W';
936
-				}
937
-				if ($husb->getSex() == 'F') {
938
-					$html .= 'H';
939
-				} // female partners
940
-			}
941
-			if (!$husb->isDead() && !$wife->isDead()) {
942
-				$html .= 'N';
943
-			}
944
-			$html .= '</td>';
945
-
946
-			// Filter by roots/leaves
947
-			$html .= '<td hidden>';
948
-			if (!$husb->getChildFamilies() && !$wife->getChildFamilies()) {
949
-				$html .= 'R';
950
-			} elseif (!$husb->isDead() && !$wife->isDead() && $family->getNumberOfChildren() === 0) {
951
-				$html .= 'L';
952
-			}
953
-			$html .= '</td>
749
+        $hundred_years_ago = new Date(date('Y') - 100);
750
+
751
+        foreach ($families as $family) {
752
+            // Retrieve husband and wife
753
+            $husb = $family->getHusband();
754
+            if (is_null($husb)) {
755
+                $husb = new Individual('H', '0 @H@ INDI', null, $family->getTree());
756
+            }
757
+            $wife = $family->getWife();
758
+            if (is_null($wife)) {
759
+                $wife = new Individual('W', '0 @W@ INDI', null, $family->getTree());
760
+            }
761
+            if (!$family->canShow()) {
762
+                continue;
763
+            }
764
+            if ($family->isPendingAddtion()) {
765
+                $class = ' class="new"';
766
+            } elseif ($family->isPendingDeletion()) {
767
+                $class = ' class="old"';
768
+            } else {
769
+                $class = '';
770
+            }
771
+            $html .= '<tr' . $class . '>';
772
+            // Husband name(s)
773
+            // Extract Given names and Surnames for sorting
774
+            list($surn_givn, $givn_surn) = self::sortableNames($husb);
775
+
776
+            $html .= '<td colspan="2" data-sort="' . Filter::escapeHtml($givn_surn) . '">';
777
+            foreach ($husb->getAllNames() as $num => $name) {
778
+                if ($name['type'] == 'NAME') {
779
+                    $title = '';
780
+                } else {
781
+                    $title = 'title="' . strip_tags(GedcomTag::getLabel($name['type'], $husb)) . '"';
782
+                }
783
+                if ($num == $husb->getPrimaryName()) {
784
+                    $class             = ' class="name2"';
785
+                    $sex_image         = $husb->getSexImage();
786
+                } else {
787
+                    $class     = '';
788
+                    $sex_image = '';
789
+                }
790
+                // Only show married names if they are the name we are filtering by.
791
+                if ($name['type'] != '_MARNM' || $num == $husb->getPrimaryName()) {
792
+                    $html .= '<a ' . $title . ' href="' . $family->getHtmlUrl() . '"' . $class . '>' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>' . $sex_image . '<br>';
793
+                }
794
+            }
795
+            // Husband parents
796
+            $html .= $husb->getPrimaryParentsNames('parents details1', 'none');
797
+            $html .= '</td>';
798
+
799
+            // Hidden column for sortable name
800
+            $html .= '<td hidden data-sort="' . Filter::escapeHtml($surn_givn) . '"></td>';
801
+
802
+            // Husband age
803
+            $mdate = $family->getMarriageDate();
804
+            $hdate = $husb->getBirthDate();
805
+            if ($hdate->isOK() && $mdate->isOK()) {
806
+                if ($hdate->gregorianYear() >= 1550 && $hdate->gregorianYear() < 2030) {
807
+                    $birt_by_decade[(int) ($hdate->gregorianYear() / 10) * 10] .= $husb->getSex();
808
+                }
809
+                $hage = Date::getAge($hdate, $mdate, 0);
810
+                if ($hage >= 0 && $hage <= $max_age) {
811
+                    $marr_by_age[$hage] .= $husb->getSex();
812
+                }
813
+            }
814
+            $html .= '<td class="center" data=-sort="' . Date::getAge($hdate, $mdate, 1) . '">' . Date::getAge($hdate, $mdate, 2) . '</td>';
815
+
816
+            // Wife name(s)
817
+            // Extract Given names and Surnames for sorting
818
+            list($surn_givn, $givn_surn) = self::sortableNames($wife);
819
+            $html .= '<td colspan="2" data-sort="' . Filter::escapeHtml($givn_surn) . '">';
820
+            foreach ($wife->getAllNames() as $num => $name) {
821
+                if ($name['type'] == 'NAME') {
822
+                    $title = '';
823
+                } else {
824
+                    $title = 'title="' . strip_tags(GedcomTag::getLabel($name['type'], $wife)) . '"';
825
+                }
826
+                if ($num == $wife->getPrimaryName()) {
827
+                    $class             = ' class="name2"';
828
+                    $sex_image         = $wife->getSexImage();
829
+                } else {
830
+                    $class     = '';
831
+                    $sex_image = '';
832
+                }
833
+                // Only show married names if they are the name we are filtering by.
834
+                if ($name['type'] != '_MARNM' || $num == $wife->getPrimaryName()) {
835
+                    $html .= '<a ' . $title . ' href="' . $family->getHtmlUrl() . '"' . $class . '>' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>' . $sex_image . '<br>';
836
+                }
837
+            }
838
+            // Wife parents
839
+            $html .= $wife->getPrimaryParentsNames('parents details1', 'none');
840
+            $html .= '</td>';
841
+
842
+            // Hidden column for sortable name
843
+            $html .= '<td hidden data-sort="' . Filter::escapeHtml($surn_givn) . '"></td>';
844
+
845
+            // Wife age
846
+            $mdate = $family->getMarriageDate();
847
+            $wdate = $wife->getBirthDate();
848
+            if ($wdate->isOK() && $mdate->isOK()) {
849
+                if ($wdate->gregorianYear() >= 1550 && $wdate->gregorianYear() < 2030) {
850
+                    $birt_by_decade[(int) ($wdate->gregorianYear() / 10) * 10] .= $wife->getSex();
851
+                }
852
+                $wage = Date::getAge($wdate, $mdate, 0);
853
+                if ($wage >= 0 && $wage <= $max_age) {
854
+                    $marr_by_age[$wage] .= $wife->getSex();
855
+                }
856
+            }
857
+            $html .= '<td class="center" data-sort="' . Date::getAge($wdate, $mdate, 1) . '">' . Date::getAge($wdate, $mdate, 2) . '</td>';
858
+
859
+            // Marriage date
860
+            $html .= '<td data-sort="' . $family->getMarriageDate()->julianDay() . '">';
861
+            if ($marriage_dates = $family->getAllMarriageDates()) {
862
+                foreach ($marriage_dates as $n => $marriage_date) {
863
+                    if ($n) {
864
+                        $html .= '<br>';
865
+                    }
866
+                    $html .= '<div>' . $marriage_date->display(true) . '</div>';
867
+                }
868
+                if ($marriage_dates[0]->gregorianYear() >= 1550 && $marriage_dates[0]->gregorianYear() < 2030) {
869
+                    $marr_by_decade[(int) ($marriage_dates[0]->gregorianYear() / 10) * 10] .= $husb->getSex() . $wife->getSex();
870
+                }
871
+            } elseif ($family->getFacts('_NMR')) {
872
+                $html .= I18N::translate('no');
873
+            } elseif ($family->getFacts('MARR')) {
874
+                $html .= I18N::translate('yes');
875
+            } else {
876
+                $html .= '&nbsp;';
877
+            }
878
+            $html .= '</td>';
879
+
880
+            // Marriage anniversary
881
+            $html .= '<td class="center" data-sort="' . -$family->getMarriageDate()->julianDay() . '">' . Date::getAge($family->getMarriageDate(), null, 2) . '</td>';
882
+
883
+            // Marriage place
884
+            $html .= '<td>';
885
+            foreach ($family->getAllMarriagePlaces() as $n => $marriage_place) {
886
+                $tmp = new Place($marriage_place, $family->getTree());
887
+                if ($n) {
888
+                    $html .= '<br>';
889
+                }
890
+                $html .= '<a href="' . $tmp->getURL() . '" title="' . strip_tags($tmp->getFullName()) . '">';
891
+                $html .= FunctionsPrint::highlightSearchHits($tmp->getShortName()) . '</a>';
892
+            }
893
+            $html .= '</td>';
894
+
895
+            // Number of children
896
+            $html .= '<td class="center" data-sort="' . $family->getNumberOfChildren() . '">' . I18N::number($family->getNumberOfChildren()) . '</td>';
897
+
898
+            // Last change
899
+            $html .= '<td data-sort="' . $family->lastChangeTimestamp(true) . '">' . $family->lastChangeTimestamp() . '</td>';
900
+
901
+            // Filter by marriage date
902
+            $html .= '<td hidden>';
903
+            if (!$family->canShow() || !$mdate->isOK()) {
904
+                $html .= 'U';
905
+            } else {
906
+                if (Date::compare($mdate, $hundred_years_ago) > 0) {
907
+                    $html .= 'Y100';
908
+                } else {
909
+                    $html .= 'YES';
910
+                }
911
+            }
912
+            if ($family->getFacts(WT_EVENTS_DIV)) {
913
+                $html .= 'D';
914
+            }
915
+            if (count($husb->getSpouseFamilies()) > 1 || count($wife->getSpouseFamilies()) > 1) {
916
+                $html .= 'M';
917
+            }
918
+            $html .= '</td>';
919
+
920
+            // Filter by alive/dead
921
+            $html .= '<td hidden>';
922
+            if ($husb->isDead() && $wife->isDead()) {
923
+                $html .= 'Y';
924
+            }
925
+            if ($husb->isDead() && !$wife->isDead()) {
926
+                if ($wife->getSex() == 'F') {
927
+                    $html .= 'H';
928
+                }
929
+                if ($wife->getSex() == 'M') {
930
+                    $html .= 'W';
931
+                } // male partners
932
+            }
933
+            if (!$husb->isDead() && $wife->isDead()) {
934
+                if ($husb->getSex() == 'M') {
935
+                    $html .= 'W';
936
+                }
937
+                if ($husb->getSex() == 'F') {
938
+                    $html .= 'H';
939
+                } // female partners
940
+            }
941
+            if (!$husb->isDead() && !$wife->isDead()) {
942
+                $html .= 'N';
943
+            }
944
+            $html .= '</td>';
945
+
946
+            // Filter by roots/leaves
947
+            $html .= '<td hidden>';
948
+            if (!$husb->getChildFamilies() && !$wife->getChildFamilies()) {
949
+                $html .= 'R';
950
+            } elseif (!$husb->isDead() && !$wife->isDead() && $family->getNumberOfChildren() === 0) {
951
+                $html .= 'L';
952
+            }
953
+            $html .= '</td>
954 954
 			</tr>';
955
-		}
955
+        }
956 956
 
957
-		$html .= '
957
+        $html .= '
958 958
 					</tbody>
959 959
 				</table>
960 960
 				<div id="fam_list_table-charts_' . $table_id . '" style="display:none">
@@ -970,40 +970,40 @@  discard block
 block discarded – undo
970 970
 				</div>
971 971
 			</div>';
972 972
 
973
-		return $html;
974
-	}
975
-
976
-	/**
977
-	 * Print a table of sources
978
-	 *
979
-	 * @param Source[] $sources
980
-	 *
981
-	 * @return string
982
-	 */
983
-	public static function sourceTable($sources) {
984
-		global $WT_TREE, $controller;
985
-
986
-		// Count the number of linked records. These numbers include private records.
987
-		// It is not good to bypass privacy, but many servers do not have the resources
988
-		// to process privacy for every record in the tree
989
-		$count_individuals = Database::prepare(
990
-			"SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##individuals` JOIN `##link` ON l_from = i_id AND l_file = i_file AND l_type = 'SOUR' GROUP BY l_to, l_file"
991
-		)->fetchAssoc();
992
-		$count_families = Database::prepare(
993
-			"SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##families` JOIN `##link` ON l_from = f_id AND l_file = f_file AND l_type = 'SOUR' GROUP BY l_to, l_file"
994
-		)->fetchAssoc();
995
-		$count_media = Database::prepare(
996
-			"SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##media` JOIN `##link` ON l_from = m_id AND l_file = m_file AND l_type = 'SOUR' GROUP BY l_to, l_file"
997
-		)->fetchAssoc();
998
-		$count_notes = Database::prepare(
999
-			"SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##other` JOIN `##link` ON l_from = o_id AND l_file = o_file AND o_type = 'NOTE' AND l_type = 'SOUR' GROUP BY l_to, l_file"
1000
-		)->fetchAssoc();
1001
-
1002
-		$html     = '';
1003
-		$table_id = 'table-sour-' . Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
1004
-		$controller
1005
-			->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
1006
-			->addInlineJavascript('
973
+        return $html;
974
+    }
975
+
976
+    /**
977
+     * Print a table of sources
978
+     *
979
+     * @param Source[] $sources
980
+     *
981
+     * @return string
982
+     */
983
+    public static function sourceTable($sources) {
984
+        global $WT_TREE, $controller;
985
+
986
+        // Count the number of linked records. These numbers include private records.
987
+        // It is not good to bypass privacy, but many servers do not have the resources
988
+        // to process privacy for every record in the tree
989
+        $count_individuals = Database::prepare(
990
+            "SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##individuals` JOIN `##link` ON l_from = i_id AND l_file = i_file AND l_type = 'SOUR' GROUP BY l_to, l_file"
991
+        )->fetchAssoc();
992
+        $count_families = Database::prepare(
993
+            "SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##families` JOIN `##link` ON l_from = f_id AND l_file = f_file AND l_type = 'SOUR' GROUP BY l_to, l_file"
994
+        )->fetchAssoc();
995
+        $count_media = Database::prepare(
996
+            "SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##media` JOIN `##link` ON l_from = m_id AND l_file = m_file AND l_type = 'SOUR' GROUP BY l_to, l_file"
997
+        )->fetchAssoc();
998
+        $count_notes = Database::prepare(
999
+            "SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##other` JOIN `##link` ON l_from = o_id AND l_file = o_file AND o_type = 'NOTE' AND l_type = 'SOUR' GROUP BY l_to, l_file"
1000
+        )->fetchAssoc();
1001
+
1002
+        $html     = '';
1003
+        $table_id = 'table-sour-' . Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
1004
+        $controller
1005
+            ->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
1006
+            ->addInlineJavascript('
1007 1007
 				jQuery.fn.dataTableExt.oSort["text-asc"] = textCompareAsc;
1008 1008
 				jQuery.fn.dataTableExt.oSort["text-desc"] = textCompareDesc;
1009 1009
 				jQuery("#' . $table_id . '").dataTable( {
@@ -1029,108 +1029,108 @@  discard block
 block discarded – undo
1029 1029
 				jQuery(".loading-image").css("display", "none");
1030 1030
 			');
1031 1031
 
1032
-		$html .= '<div class="loading-image"></div>';
1033
-		$html .= '<div class="source-list">';
1034
-		$html .= '<table id="' . $table_id . '"><thead><tr>';
1035
-		$html .= '<th>' . GedcomTag::getLabel('TITL') . '</th>';
1036
-		$html .= '<th>' . GedcomTag::getLabel('AUTH') . '</th>';
1037
-		$html .= '<th>' . I18N::translate('Individuals') . '</th>';
1038
-		$html .= '<th>' . I18N::translate('Families') . '</th>';
1039
-		$html .= '<th>' . I18N::translate('Media objects') . '</th>';
1040
-		$html .= '<th>' . I18N::translate('Shared notes') . '</th>';
1041
-		$html .= '<th>' . GedcomTag::getLabel('CHAN') . '</th>';
1042
-		$html .= '<th>' . I18N::translate('Delete') . '</th>';
1043
-		$html .= '</tr></thead>';
1044
-		$html .= '<tbody>';
1045
-
1046
-		foreach ($sources as $source) {
1047
-			if (!$source->canShow()) {
1048
-				continue;
1049
-			}
1050
-			if ($source->isPendingAddtion()) {
1051
-				$class = ' class="new"';
1052
-			} elseif ($source->isPendingDeletion()) {
1053
-				$class = ' class="old"';
1054
-			} else {
1055
-				$class = '';
1056
-			}
1057
-			$html .= '<tr' . $class . '>';
1058
-			// Source name(s)
1059
-			$html .= '<td data-sort="' . Filter::escapeHtml($source->getSortName()) . '">';
1060
-			foreach ($source->getAllNames() as $n => $name) {
1061
-				if ($n) {
1062
-					$html .= '<br>';
1063
-				}
1064
-				if ($n == $source->getPrimaryName()) {
1065
-					$html .= '<a class="name2" href="' . $source->getHtmlUrl() . '">' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>';
1066
-				} else {
1067
-					$html .= '<a href="' . $source->getHtmlUrl() . '">' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>';
1068
-				}
1069
-			}
1070
-			$html .= '</td>';
1071
-			// Author
1072
-			$auth = $source->getFirstFact('AUTH');
1073
-			if ($auth) {
1074
-				$author = $auth->getValue();
1075
-			} else {
1076
-				$author = '';
1077
-			}
1078
-			$html .= '<td data-sort="' . Filter::escapeHtml($author) . '">' . FunctionsPrint::highlightSearchHits($author) . '</td>';
1079
-			$key = $source->getXref() . '@' . $source->getTree()->getTreeId();
1080
-			// Count of linked individuals
1081
-			$num = array_key_exists($key, $count_individuals) ? $count_individuals[$key] : 0;
1082
-			$html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1083
-			// Count of linked families
1084
-			$num = array_key_exists($key, $count_families) ? $count_families[$key] : 0;
1085
-			$html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1086
-			// Count of linked media objects
1087
-			$num = array_key_exists($key, $count_media) ? $count_media[$key] : 0;
1088
-			$html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1089
-			// Count of linked notes
1090
-			$num = array_key_exists($key, $count_notes) ? $count_notes[$key] : 0;
1091
-			$html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1092
-			// Last change
1093
-			$html .= '<td data-sort="' . $source->lastChangeTimestamp(true) . '">' . $source->lastChangeTimestamp() . '</td>';
1094
-			// Delete
1095
-			$html .= '<td><a href="#" title="' . I18N::translate('Delete') . '" class="deleteicon" onclick="return delete_record(\'' . I18N::translate('Are you sure you want to delete “%s”?', Filter::escapeJs(Filter::unescapeHtml($source->getFullName()))) . "', '" . $source->getXref() . '\');"><span class="link_text">' . I18N::translate('Delete') . '</span></a></td>';
1096
-			$html .= '</tr>';
1097
-		}
1098
-		$html .= '</tbody></table></div>';
1099
-
1100
-		return $html;
1101
-	}
1102
-
1103
-	/**
1104
-	 * Print a table of shared notes
1105
-	 *
1106
-	 * @param Note[] $notes
1107
-	 *
1108
-	 * @return string
1109
-	 */
1110
-	public static function noteTable($notes) {
1111
-		global $WT_TREE, $controller;
1112
-
1113
-		// Count the number of linked records. These numbers include private records.
1114
-		// It is not good to bypass privacy, but many servers do not have the resources
1115
-		// to process privacy for every record in the tree
1116
-		$count_individuals = Database::prepare(
1117
-			"SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##individuals` JOIN `##link` ON l_from = i_id AND l_file = i_file AND l_type = 'NOTE' GROUP BY l_to, l_file"
1118
-		)->fetchAssoc();
1119
-		$count_families = Database::prepare(
1120
-			"SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##families` JOIN `##link` ON l_from = f_id AND l_file = f_file AND l_type = 'NOTE' GROUP BY l_to, l_file"
1121
-		)->fetchAssoc();
1122
-		$count_media = Database::prepare(
1123
-			"SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##media` JOIN `##link` ON l_from = m_id AND l_file = m_file AND l_type = 'NOTE' GROUP BY l_to, l_file"
1124
-		)->fetchAssoc();
1125
-		$count_sources = Database::prepare(
1126
-			"SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##sources` JOIN `##link` ON l_from = s_id AND l_file = s_file AND l_type = 'NOTE' GROUP BY l_to, l_file"
1127
-		)->fetchAssoc();
1128
-
1129
-		$html     = '';
1130
-		$table_id = 'table-note-' . Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
1131
-		$controller
1132
-			->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
1133
-			->addInlineJavascript('
1032
+        $html .= '<div class="loading-image"></div>';
1033
+        $html .= '<div class="source-list">';
1034
+        $html .= '<table id="' . $table_id . '"><thead><tr>';
1035
+        $html .= '<th>' . GedcomTag::getLabel('TITL') . '</th>';
1036
+        $html .= '<th>' . GedcomTag::getLabel('AUTH') . '</th>';
1037
+        $html .= '<th>' . I18N::translate('Individuals') . '</th>';
1038
+        $html .= '<th>' . I18N::translate('Families') . '</th>';
1039
+        $html .= '<th>' . I18N::translate('Media objects') . '</th>';
1040
+        $html .= '<th>' . I18N::translate('Shared notes') . '</th>';
1041
+        $html .= '<th>' . GedcomTag::getLabel('CHAN') . '</th>';
1042
+        $html .= '<th>' . I18N::translate('Delete') . '</th>';
1043
+        $html .= '</tr></thead>';
1044
+        $html .= '<tbody>';
1045
+
1046
+        foreach ($sources as $source) {
1047
+            if (!$source->canShow()) {
1048
+                continue;
1049
+            }
1050
+            if ($source->isPendingAddtion()) {
1051
+                $class = ' class="new"';
1052
+            } elseif ($source->isPendingDeletion()) {
1053
+                $class = ' class="old"';
1054
+            } else {
1055
+                $class = '';
1056
+            }
1057
+            $html .= '<tr' . $class . '>';
1058
+            // Source name(s)
1059
+            $html .= '<td data-sort="' . Filter::escapeHtml($source->getSortName()) . '">';
1060
+            foreach ($source->getAllNames() as $n => $name) {
1061
+                if ($n) {
1062
+                    $html .= '<br>';
1063
+                }
1064
+                if ($n == $source->getPrimaryName()) {
1065
+                    $html .= '<a class="name2" href="' . $source->getHtmlUrl() . '">' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>';
1066
+                } else {
1067
+                    $html .= '<a href="' . $source->getHtmlUrl() . '">' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>';
1068
+                }
1069
+            }
1070
+            $html .= '</td>';
1071
+            // Author
1072
+            $auth = $source->getFirstFact('AUTH');
1073
+            if ($auth) {
1074
+                $author = $auth->getValue();
1075
+            } else {
1076
+                $author = '';
1077
+            }
1078
+            $html .= '<td data-sort="' . Filter::escapeHtml($author) . '">' . FunctionsPrint::highlightSearchHits($author) . '</td>';
1079
+            $key = $source->getXref() . '@' . $source->getTree()->getTreeId();
1080
+            // Count of linked individuals
1081
+            $num = array_key_exists($key, $count_individuals) ? $count_individuals[$key] : 0;
1082
+            $html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1083
+            // Count of linked families
1084
+            $num = array_key_exists($key, $count_families) ? $count_families[$key] : 0;
1085
+            $html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1086
+            // Count of linked media objects
1087
+            $num = array_key_exists($key, $count_media) ? $count_media[$key] : 0;
1088
+            $html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1089
+            // Count of linked notes
1090
+            $num = array_key_exists($key, $count_notes) ? $count_notes[$key] : 0;
1091
+            $html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1092
+            // Last change
1093
+            $html .= '<td data-sort="' . $source->lastChangeTimestamp(true) . '">' . $source->lastChangeTimestamp() . '</td>';
1094
+            // Delete
1095
+            $html .= '<td><a href="#" title="' . I18N::translate('Delete') . '" class="deleteicon" onclick="return delete_record(\'' . I18N::translate('Are you sure you want to delete “%s”?', Filter::escapeJs(Filter::unescapeHtml($source->getFullName()))) . "', '" . $source->getXref() . '\');"><span class="link_text">' . I18N::translate('Delete') . '</span></a></td>';
1096
+            $html .= '</tr>';
1097
+        }
1098
+        $html .= '</tbody></table></div>';
1099
+
1100
+        return $html;
1101
+    }
1102
+
1103
+    /**
1104
+     * Print a table of shared notes
1105
+     *
1106
+     * @param Note[] $notes
1107
+     *
1108
+     * @return string
1109
+     */
1110
+    public static function noteTable($notes) {
1111
+        global $WT_TREE, $controller;
1112
+
1113
+        // Count the number of linked records. These numbers include private records.
1114
+        // It is not good to bypass privacy, but many servers do not have the resources
1115
+        // to process privacy for every record in the tree
1116
+        $count_individuals = Database::prepare(
1117
+            "SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##individuals` JOIN `##link` ON l_from = i_id AND l_file = i_file AND l_type = 'NOTE' GROUP BY l_to, l_file"
1118
+        )->fetchAssoc();
1119
+        $count_families = Database::prepare(
1120
+            "SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##families` JOIN `##link` ON l_from = f_id AND l_file = f_file AND l_type = 'NOTE' GROUP BY l_to, l_file"
1121
+        )->fetchAssoc();
1122
+        $count_media = Database::prepare(
1123
+            "SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##media` JOIN `##link` ON l_from = m_id AND l_file = m_file AND l_type = 'NOTE' GROUP BY l_to, l_file"
1124
+        )->fetchAssoc();
1125
+        $count_sources = Database::prepare(
1126
+            "SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##sources` JOIN `##link` ON l_from = s_id AND l_file = s_file AND l_type = 'NOTE' GROUP BY l_to, l_file"
1127
+        )->fetchAssoc();
1128
+
1129
+        $html     = '';
1130
+        $table_id = 'table-note-' . Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
1131
+        $controller
1132
+            ->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
1133
+            ->addInlineJavascript('
1134 1134
 				jQuery.fn.dataTableExt.oSort["text-asc"] = textCompareAsc;
1135 1135
 				jQuery.fn.dataTableExt.oSort["text-desc"] = textCompareDesc;
1136 1136
 				jQuery("#' . $table_id . '").dataTable({
@@ -1155,79 +1155,79 @@  discard block
 block discarded – undo
1155 1155
 				jQuery(".loading-image").css("display", "none");
1156 1156
 			');
1157 1157
 
1158
-		$html .= '<div class="loading-image"></div>';
1159
-		$html .= '<div class="note-list">';
1160
-		$html .= '<table id="' . $table_id . '"><thead><tr>';
1161
-		$html .= '<th>' . GedcomTag::getLabel('TITL') . '</th>';
1162
-		$html .= '<th>' . I18N::translate('Individuals') . '</th>';
1163
-		$html .= '<th>' . I18N::translate('Families') . '</th>';
1164
-		$html .= '<th>' . I18N::translate('Media objects') . '</th>';
1165
-		$html .= '<th>' . I18N::translate('Sources') . '</th>';
1166
-		$html .= '<th>' . GedcomTag::getLabel('CHAN') . '</th>';
1167
-		$html .= '<th>' . I18N::translate('Delete') . '</th>';
1168
-		$html .= '</tr></thead>';
1169
-		$html .= '<tbody>';
1170
-
1171
-		foreach ($notes as $note) {
1172
-			if (!$note->canShow()) {
1173
-				continue;
1174
-			}
1175
-			if ($note->isPendingAddtion()) {
1176
-				$class = ' class="new"';
1177
-			} elseif ($note->isPendingDeletion()) {
1178
-				$class = ' class="old"';
1179
-			} else {
1180
-				$class = '';
1181
-			}
1182
-			$html .= '<tr' . $class . '>';
1183
-			// Count of linked notes
1184
-			$html .= '<td data-sort="' . Filter::escapeHtml($note->getSortName()) . '"><a class="name2" href="' . $note->getHtmlUrl() . '">' . FunctionsPrint::highlightSearchHits($note->getFullName()) . '</a></td>';
1185
-			$key = $note->getXref() . '@' . $note->getTree()->getTreeId();
1186
-			// Count of linked individuals
1187
-			$num = array_key_exists($key, $count_individuals) ? $count_individuals[$key] : 0;
1188
-			$html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1189
-			// Count of linked families
1190
-			$num = array_key_exists($key, $count_families) ? $count_families[$key] : 0;
1191
-			$html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1192
-			// Count of linked media objects
1193
-			$num = array_key_exists($key, $count_media) ? $count_media[$key] : 0;
1194
-			$html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1195
-			// Count of linked sources
1196
-			$num = array_key_exists($key, $count_sources) ? $count_sources[$key] : 0;
1197
-			$html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1198
-			// Last change
1199
-			$html .= '<td data-sort="' . $note->lastChangeTimestamp(true) . '">' . $note->lastChangeTimestamp() . '</td>';
1200
-			// Delete
1201
-			$html .= '<td><a href="#" title="' . I18N::translate('Delete') . '" class="deleteicon" onclick="return delete_record(\'' . I18N::translate('Are you sure you want to delete “%s”?', Filter::escapeJs(Filter::unescapeHtml($note->getFullName()))) . "', '" . $note->getXref() . '\');"><span class="link_text">' . I18N::translate('Delete') . '</span></a></td>';
1202
-			$html .= '</tr>';
1203
-		}
1204
-		$html .= '</tbody></table></div>';
1205
-
1206
-		return $html;
1207
-	}
1208
-
1209
-	/**
1210
-	 * Print a table of repositories
1211
-	 *
1212
-	 * @param Repository[] $repositories
1213
-	 *
1214
-	 * @return string
1215
-	 */
1216
-	public static function repositoryTable($repositories) {
1217
-		global $WT_TREE, $controller;
1218
-
1219
-		// Count the number of linked records. These numbers include private records.
1220
-		// It is not good to bypass privacy, but many servers do not have the resources
1221
-		// to process privacy for every record in the tree
1222
-		$count_sources = Database::prepare(
1223
-			"SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##sources` JOIN `##link` ON l_from = s_id AND l_file = s_file AND l_type = 'REPO' GROUP BY l_to, l_file"
1224
-		)->fetchAssoc();
1225
-
1226
-		$html     = '';
1227
-		$table_id = 'table-repo-' . Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
1228
-		$controller
1229
-			->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
1230
-			->addInlineJavascript('
1158
+        $html .= '<div class="loading-image"></div>';
1159
+        $html .= '<div class="note-list">';
1160
+        $html .= '<table id="' . $table_id . '"><thead><tr>';
1161
+        $html .= '<th>' . GedcomTag::getLabel('TITL') . '</th>';
1162
+        $html .= '<th>' . I18N::translate('Individuals') . '</th>';
1163
+        $html .= '<th>' . I18N::translate('Families') . '</th>';
1164
+        $html .= '<th>' . I18N::translate('Media objects') . '</th>';
1165
+        $html .= '<th>' . I18N::translate('Sources') . '</th>';
1166
+        $html .= '<th>' . GedcomTag::getLabel('CHAN') . '</th>';
1167
+        $html .= '<th>' . I18N::translate('Delete') . '</th>';
1168
+        $html .= '</tr></thead>';
1169
+        $html .= '<tbody>';
1170
+
1171
+        foreach ($notes as $note) {
1172
+            if (!$note->canShow()) {
1173
+                continue;
1174
+            }
1175
+            if ($note->isPendingAddtion()) {
1176
+                $class = ' class="new"';
1177
+            } elseif ($note->isPendingDeletion()) {
1178
+                $class = ' class="old"';
1179
+            } else {
1180
+                $class = '';
1181
+            }
1182
+            $html .= '<tr' . $class . '>';
1183
+            // Count of linked notes
1184
+            $html .= '<td data-sort="' . Filter::escapeHtml($note->getSortName()) . '"><a class="name2" href="' . $note->getHtmlUrl() . '">' . FunctionsPrint::highlightSearchHits($note->getFullName()) . '</a></td>';
1185
+            $key = $note->getXref() . '@' . $note->getTree()->getTreeId();
1186
+            // Count of linked individuals
1187
+            $num = array_key_exists($key, $count_individuals) ? $count_individuals[$key] : 0;
1188
+            $html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1189
+            // Count of linked families
1190
+            $num = array_key_exists($key, $count_families) ? $count_families[$key] : 0;
1191
+            $html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1192
+            // Count of linked media objects
1193
+            $num = array_key_exists($key, $count_media) ? $count_media[$key] : 0;
1194
+            $html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1195
+            // Count of linked sources
1196
+            $num = array_key_exists($key, $count_sources) ? $count_sources[$key] : 0;
1197
+            $html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1198
+            // Last change
1199
+            $html .= '<td data-sort="' . $note->lastChangeTimestamp(true) . '">' . $note->lastChangeTimestamp() . '</td>';
1200
+            // Delete
1201
+            $html .= '<td><a href="#" title="' . I18N::translate('Delete') . '" class="deleteicon" onclick="return delete_record(\'' . I18N::translate('Are you sure you want to delete “%s”?', Filter::escapeJs(Filter::unescapeHtml($note->getFullName()))) . "', '" . $note->getXref() . '\');"><span class="link_text">' . I18N::translate('Delete') . '</span></a></td>';
1202
+            $html .= '</tr>';
1203
+        }
1204
+        $html .= '</tbody></table></div>';
1205
+
1206
+        return $html;
1207
+    }
1208
+
1209
+    /**
1210
+     * Print a table of repositories
1211
+     *
1212
+     * @param Repository[] $repositories
1213
+     *
1214
+     * @return string
1215
+     */
1216
+    public static function repositoryTable($repositories) {
1217
+        global $WT_TREE, $controller;
1218
+
1219
+        // Count the number of linked records. These numbers include private records.
1220
+        // It is not good to bypass privacy, but many servers do not have the resources
1221
+        // to process privacy for every record in the tree
1222
+        $count_sources = Database::prepare(
1223
+            "SELECT CONCAT(l_to, '@', l_file), COUNT(*) FROM `##sources` JOIN `##link` ON l_from = s_id AND l_file = s_file AND l_type = 'REPO' GROUP BY l_to, l_file"
1224
+        )->fetchAssoc();
1225
+
1226
+        $html     = '';
1227
+        $table_id = 'table-repo-' . Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
1228
+        $controller
1229
+            ->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
1230
+            ->addInlineJavascript('
1231 1231
 				jQuery.fn.dataTableExt.oSort["text-asc"] = textCompareAsc;
1232 1232
 				jQuery.fn.dataTableExt.oSort["text-desc"] = textCompareDesc;
1233 1233
 				jQuery("#' . $table_id . '").dataTable({
@@ -1249,71 +1249,71 @@  discard block
 block discarded – undo
1249 1249
 				jQuery(".loading-image").css("display", "none");
1250 1250
 			');
1251 1251
 
1252
-		$html .= '<div class="loading-image"></div>';
1253
-		$html .= '<div class="repo-list">';
1254
-		$html .= '<table id="' . $table_id . '"><thead><tr>';
1255
-		$html .= '<th>' . I18N::translate('Repository name') . '</th>';
1256
-		$html .= '<th>' . I18N::translate('Sources') . '</th>';
1257
-		$html .= '<th>' . GedcomTag::getLabel('CHAN') . '</th>';
1258
-		$html .= '<th>' . I18N::translate('Delete') . '</th>';
1259
-		$html .= '</tr></thead>';
1260
-		$html .= '<tbody>';
1261
-
1262
-		foreach ($repositories as $repository) {
1263
-			if (!$repository->canShow()) {
1264
-				continue;
1265
-			}
1266
-			if ($repository->isPendingAddtion()) {
1267
-				$class = ' class="new"';
1268
-			} elseif ($repository->isPendingDeletion()) {
1269
-				$class = ' class="old"';
1270
-			} else {
1271
-				$class = '';
1272
-			}
1273
-			$html .= '<tr' . $class . '>';
1274
-			// Repository name(s)
1275
-			$html .= '<td data-sort="' . Filter::escapeHtml($repository->getSortName()) . '">';
1276
-			foreach ($repository->getAllNames() as $n => $name) {
1277
-				if ($n) {
1278
-					$html .= '<br>';
1279
-				}
1280
-				if ($n == $repository->getPrimaryName()) {
1281
-					$html .= '<a class="name2" href="' . $repository->getHtmlUrl() . '">' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>';
1282
-				} else {
1283
-					$html .= '<a href="' . $repository->getHtmlUrl() . '">' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>';
1284
-				}
1285
-			}
1286
-			$html .= '</td>';
1287
-			$key = $repository->getXref() . '@' . $repository->getTree()->getTreeId();
1288
-			// Count of linked sources
1289
-			$num = array_key_exists($key, $count_sources) ? $count_sources[$key] : 0;
1290
-			$html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1291
-			// Last change
1292
-			$html .= '<td data-sort="' . $repository->lastChangeTimestamp(true) . '">' . $repository->lastChangeTimestamp() . '</td>';
1293
-			// Delete
1294
-			$html .= '<td><a href="#" title="' . I18N::translate('Delete') . '" class="deleteicon" onclick="return delete_record(\'' . I18N::translate('Are you sure you want to delete “%s”?', Filter::escapeJs(Filter::unescapeHtml($repository->getFullName()))) . "', '" . $repository->getXref() . '\');"><span class="link_text">' . I18N::translate('Delete') . '</span></a></td>';
1295
-			$html .= '</tr>';
1296
-		}
1297
-		$html .= '</tbody></table></div>';
1298
-
1299
-		return $html;
1300
-	}
1301
-
1302
-	/**
1303
-	 * Print a table of media objects
1304
-	 *
1305
-	 * @param Media[] $media_objects
1306
-	 *
1307
-	 * @return string
1308
-	 */
1309
-	public static function mediaTable($media_objects) {
1310
-		global $WT_TREE, $controller;
1311
-
1312
-		$html     = '';
1313
-		$table_id = 'table-obje-' . Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
1314
-		$controller
1315
-			->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
1316
-			->addInlineJavascript('
1252
+        $html .= '<div class="loading-image"></div>';
1253
+        $html .= '<div class="repo-list">';
1254
+        $html .= '<table id="' . $table_id . '"><thead><tr>';
1255
+        $html .= '<th>' . I18N::translate('Repository name') . '</th>';
1256
+        $html .= '<th>' . I18N::translate('Sources') . '</th>';
1257
+        $html .= '<th>' . GedcomTag::getLabel('CHAN') . '</th>';
1258
+        $html .= '<th>' . I18N::translate('Delete') . '</th>';
1259
+        $html .= '</tr></thead>';
1260
+        $html .= '<tbody>';
1261
+
1262
+        foreach ($repositories as $repository) {
1263
+            if (!$repository->canShow()) {
1264
+                continue;
1265
+            }
1266
+            if ($repository->isPendingAddtion()) {
1267
+                $class = ' class="new"';
1268
+            } elseif ($repository->isPendingDeletion()) {
1269
+                $class = ' class="old"';
1270
+            } else {
1271
+                $class = '';
1272
+            }
1273
+            $html .= '<tr' . $class . '>';
1274
+            // Repository name(s)
1275
+            $html .= '<td data-sort="' . Filter::escapeHtml($repository->getSortName()) . '">';
1276
+            foreach ($repository->getAllNames() as $n => $name) {
1277
+                if ($n) {
1278
+                    $html .= '<br>';
1279
+                }
1280
+                if ($n == $repository->getPrimaryName()) {
1281
+                    $html .= '<a class="name2" href="' . $repository->getHtmlUrl() . '">' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>';
1282
+                } else {
1283
+                    $html .= '<a href="' . $repository->getHtmlUrl() . '">' . FunctionsPrint::highlightSearchHits($name['full']) . '</a>';
1284
+                }
1285
+            }
1286
+            $html .= '</td>';
1287
+            $key = $repository->getXref() . '@' . $repository->getTree()->getTreeId();
1288
+            // Count of linked sources
1289
+            $num = array_key_exists($key, $count_sources) ? $count_sources[$key] : 0;
1290
+            $html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1291
+            // Last change
1292
+            $html .= '<td data-sort="' . $repository->lastChangeTimestamp(true) . '">' . $repository->lastChangeTimestamp() . '</td>';
1293
+            // Delete
1294
+            $html .= '<td><a href="#" title="' . I18N::translate('Delete') . '" class="deleteicon" onclick="return delete_record(\'' . I18N::translate('Are you sure you want to delete “%s”?', Filter::escapeJs(Filter::unescapeHtml($repository->getFullName()))) . "', '" . $repository->getXref() . '\');"><span class="link_text">' . I18N::translate('Delete') . '</span></a></td>';
1295
+            $html .= '</tr>';
1296
+        }
1297
+        $html .= '</tbody></table></div>';
1298
+
1299
+        return $html;
1300
+    }
1301
+
1302
+    /**
1303
+     * Print a table of media objects
1304
+     *
1305
+     * @param Media[] $media_objects
1306
+     *
1307
+     * @return string
1308
+     */
1309
+    public static function mediaTable($media_objects) {
1310
+        global $WT_TREE, $controller;
1311
+
1312
+        $html     = '';
1313
+        $table_id = 'table-obje-' . Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
1314
+        $controller
1315
+            ->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
1316
+            ->addInlineJavascript('
1317 1317
 				jQuery.fn.dataTableExt.oSort["text-asc"] = textCompareAsc;
1318 1318
 				jQuery.fn.dataTableExt.oSort["text-desc"] = textCompareDesc;
1319 1319
 				jQuery("#' . $table_id . '").dataTable({
@@ -1337,75 +1337,75 @@  discard block
 block discarded – undo
1337 1337
 				jQuery(".loading-image").css("display", "none");
1338 1338
 			');
1339 1339
 
1340
-		$html .= '<div class="loading-image"></div>';
1341
-		$html .= '<div class="media-list">';
1342
-		$html .= '<table id="' . $table_id . '"><thead><tr>';
1343
-		$html .= '<th>' . I18N::translate('Media') . '</th>';
1344
-		$html .= '<th>' . GedcomTag::getLabel('TITL') . '</th>';
1345
-		$html .= '<th>' . I18N::translate('Individuals') . '</th>';
1346
-		$html .= '<th>' . I18N::translate('Families') . '</th>';
1347
-		$html .= '<th>' . I18N::translate('Sources') . '</th>';
1348
-		$html .= '<th>' . GedcomTag::getLabel('CHAN') . '</th>';
1349
-		$html .= '</tr></thead>';
1350
-		$html .= '<tbody>';
1351
-
1352
-		foreach ($media_objects as $media_object) {
1353
-			if ($media_object->canShow()) {
1354
-				$name = $media_object->getFullName();
1355
-				if ($media_object->isPendingAddtion()) {
1356
-					$class = ' class="new"';
1357
-				} elseif ($media_object->isPendingDeletion()) {
1358
-					$class = ' class="old"';
1359
-				} else {
1360
-					$class = '';
1361
-				}
1362
-				$html .= '<tr' . $class . '>';
1363
-				// Media object thumbnail
1364
-				$html .= '<td>' . $media_object->displayImage() . '</td>';
1365
-				// Media object name(s)
1366
-				$html .= '<td data-sort="' . Filter::escapeHtml($media_object->getSortName()) . '">';
1367
-				$html .= '<a href="' . $media_object->getHtmlUrl() . '" class="list_item name2">';
1368
-				$html .= FunctionsPrint::highlightSearchHits($name) . '</a>';
1369
-				if (Auth::isEditor($media_object->getTree())) {
1370
-					$html .= '<br><a href="' . $media_object->getHtmlUrl() . '">' . basename($media_object->getFilename()) . '</a>';
1371
-				}
1372
-				$html .= '</td>';
1373
-
1374
-				// Count of linked individuals
1375
-				$num = count($media_object->linkedIndividuals('OBJE'));
1376
-				$html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1377
-				// Count of linked families
1378
-				$num = count($media_object->linkedFamilies('OBJE'));
1379
-				$html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1380
-				// Count of linked sources
1381
-				$num = count($media_object->linkedSources('OBJE'));
1382
-				$html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1383
-				// Last change
1384
-				$html .= '<td data-sort="' . $media_object->lastChangeTimestamp(true) . '">' . $media_object->lastChangeTimestamp() . '</td>';
1385
-				$html .= '</tr>';
1386
-			}
1387
-		}
1388
-		$html .= '</tbody></table></div>';
1389
-
1390
-		return $html;
1391
-	}
1392
-
1393
-	/**
1394
-	 * Print a table of surnames, for the top surnames block, the indi/fam lists, etc.
1395
-	 *
1396
-	 * @param string[][] $surnames array (of SURN, of array of SPFX_SURN, of array of PID)
1397
-	 * @param string $script "indilist.php" (counts of individuals) or "famlist.php" (counts of spouses)
1398
-	 * @param Tree $tree generate links for this tree
1399
-	 *
1400
-	 * @return string
1401
-	 */
1402
-	public static function surnameTable($surnames, $script, Tree $tree) {
1403
-		global $controller;
1404
-
1405
-		$html = '';
1406
-		$controller
1407
-			->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
1408
-			->addInlineJavascript('
1340
+        $html .= '<div class="loading-image"></div>';
1341
+        $html .= '<div class="media-list">';
1342
+        $html .= '<table id="' . $table_id . '"><thead><tr>';
1343
+        $html .= '<th>' . I18N::translate('Media') . '</th>';
1344
+        $html .= '<th>' . GedcomTag::getLabel('TITL') . '</th>';
1345
+        $html .= '<th>' . I18N::translate('Individuals') . '</th>';
1346
+        $html .= '<th>' . I18N::translate('Families') . '</th>';
1347
+        $html .= '<th>' . I18N::translate('Sources') . '</th>';
1348
+        $html .= '<th>' . GedcomTag::getLabel('CHAN') . '</th>';
1349
+        $html .= '</tr></thead>';
1350
+        $html .= '<tbody>';
1351
+
1352
+        foreach ($media_objects as $media_object) {
1353
+            if ($media_object->canShow()) {
1354
+                $name = $media_object->getFullName();
1355
+                if ($media_object->isPendingAddtion()) {
1356
+                    $class = ' class="new"';
1357
+                } elseif ($media_object->isPendingDeletion()) {
1358
+                    $class = ' class="old"';
1359
+                } else {
1360
+                    $class = '';
1361
+                }
1362
+                $html .= '<tr' . $class . '>';
1363
+                // Media object thumbnail
1364
+                $html .= '<td>' . $media_object->displayImage() . '</td>';
1365
+                // Media object name(s)
1366
+                $html .= '<td data-sort="' . Filter::escapeHtml($media_object->getSortName()) . '">';
1367
+                $html .= '<a href="' . $media_object->getHtmlUrl() . '" class="list_item name2">';
1368
+                $html .= FunctionsPrint::highlightSearchHits($name) . '</a>';
1369
+                if (Auth::isEditor($media_object->getTree())) {
1370
+                    $html .= '<br><a href="' . $media_object->getHtmlUrl() . '">' . basename($media_object->getFilename()) . '</a>';
1371
+                }
1372
+                $html .= '</td>';
1373
+
1374
+                // Count of linked individuals
1375
+                $num = count($media_object->linkedIndividuals('OBJE'));
1376
+                $html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1377
+                // Count of linked families
1378
+                $num = count($media_object->linkedFamilies('OBJE'));
1379
+                $html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1380
+                // Count of linked sources
1381
+                $num = count($media_object->linkedSources('OBJE'));
1382
+                $html .= '<td class="center" data-sort="' . $num . '">' . I18N::number($num) . '</td>';
1383
+                // Last change
1384
+                $html .= '<td data-sort="' . $media_object->lastChangeTimestamp(true) . '">' . $media_object->lastChangeTimestamp() . '</td>';
1385
+                $html .= '</tr>';
1386
+            }
1387
+        }
1388
+        $html .= '</tbody></table></div>';
1389
+
1390
+        return $html;
1391
+    }
1392
+
1393
+    /**
1394
+     * Print a table of surnames, for the top surnames block, the indi/fam lists, etc.
1395
+     *
1396
+     * @param string[][] $surnames array (of SURN, of array of SPFX_SURN, of array of PID)
1397
+     * @param string $script "indilist.php" (counts of individuals) or "famlist.php" (counts of spouses)
1398
+     * @param Tree $tree generate links for this tree
1399
+     *
1400
+     * @return string
1401
+     */
1402
+    public static function surnameTable($surnames, $script, Tree $tree) {
1403
+        global $controller;
1404
+
1405
+        $html = '';
1406
+        $controller
1407
+            ->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
1408
+            ->addInlineJavascript('
1409 1409
 				jQuery.fn.dataTableExt.oSort["text-asc"] = textCompareAsc;
1410 1410
 				jQuery.fn.dataTableExt.oSort["text-desc"] = textCompareDesc;
1411 1411
 				jQuery(".surname-list").dataTable({
@@ -1422,203 +1422,203 @@  discard block
 block discarded – undo
1422 1422
 				});
1423 1423
 			');
1424 1424
 
1425
-		if ($script == 'famlist.php') {
1426
-			$col_heading = I18N::translate('Spouses');
1427
-		} else {
1428
-			$col_heading = I18N::translate('Individuals');
1429
-		}
1430
-
1431
-		$html .=
1432
-			'<table class="surname-list">' .
1433
-			'<thead>' .
1434
-			'<tr>' .
1435
-			'<th>' . GedcomTag::getLabel('SURN') . '</th>' .
1436
-			'<th>' . $col_heading . '</th>' .
1437
-			'</tr>' .
1438
-			'</thead>';
1439
-
1440
-		$html .= '<tbody>';
1441
-		foreach ($surnames as $surn => $surns) {
1442
-			// Each surname links back to the indi/fam surname list
1443
-			if ($surn) {
1444
-				$url = $script . '?surname=' . rawurlencode($surn) . '&amp;ged=' . $tree->getNameUrl();
1445
-			} else {
1446
-				$url = $script . '?alpha=,&amp;ged=' . $tree->getNameUrl();
1447
-			}
1448
-			$html .= '<tr>';
1449
-			// Surname
1450
-			$html .= '<td data-sort="' . Filter::escapeHtml($surn) . '">';
1451
-			// Multiple surname variants, e.g. von Groot, van Groot, van der Groot, etc.
1452
-			foreach ($surns as $spfxsurn => $indis) {
1453
-				if ($spfxsurn) {
1454
-					$html .= '<a href="' . $url . '" dir="auto">' . Filter::escapeHtml($spfxsurn) . '</a><br>';
1455
-				} else {
1456
-					// No surname, but a value from "2 SURN"? A common workaround for toponyms, etc.
1457
-					$html .= '<a href="' . $url . '" dir="auto">' . Filter::escapeHtml($surn) . '</a><br>';
1458
-				}
1459
-			}
1460
-			$html .= '</td>';
1461
-			// Surname count
1462
-			$subtotal = 0;
1463
-			foreach ($surns as $indis) {
1464
-				$subtotal += count($indis);
1465
-			}
1466
-			$html .= '<td class="center" data-sort="' . $subtotal . '">';
1467
-			foreach ($surns as $indis) {
1468
-				$html .= I18N::number(count($indis)) . '<br>';
1469
-			}
1470
-			if (count($surns) > 1) {
1471
-				// More than one surname variant? Show a subtotal
1472
-				$html .= I18N::number($subtotal);
1473
-			}
1474
-			$html .= '</td>';
1475
-			$html .= '</tr>';
1476
-		}
1477
-		$html .= '</tbody></table>';
1478
-
1479
-		return $html;
1480
-	}
1481
-
1482
-	/**
1483
-	 * Print a tagcloud of surnames.
1484
-	 *
1485
-	 * @param string[][] $surnames array (of SURN, of array of SPFX_SURN, of array of PID)
1486
-	 * @param string $script indilist or famlist
1487
-	 * @param bool $totals show totals after each name
1488
-	 * @param Tree $tree generate links to this tree
1489
-	 *
1490
-	 * @return string
1491
-	 */
1492
-	public static function surnameTagCloud($surnames, $script, $totals, Tree $tree) {
1493
-		$minimum = PHP_INT_MAX;
1494
-		$maximum = 1;
1495
-		foreach ($surnames as $surn => $surns) {
1496
-			foreach ($surns as $spfxsurn => $indis) {
1497
-				$maximum = max($maximum, count($indis));
1498
-				$minimum = min($minimum, count($indis));
1499
-			}
1500
-		}
1501
-
1502
-		$html = '';
1503
-		foreach ($surnames as $surn => $surns) {
1504
-			foreach ($surns as $spfxsurn => $indis) {
1505
-				if ($maximum === $minimum) {
1506
-					// All surnames occur the same number of times
1507
-					$size = 150.0;
1508
-				} else {
1509
-					$size = 75.0 + 125.0 * (count($indis) - $minimum) / ($maximum - $minimum);
1510
-				}
1511
-				$html .= '<a style="font-size:' . $size . '%" href="' . $script . '?surname=' . Filter::escapeUrl($surn) . '&amp;ged=' . $tree->getNameUrl() . '">';
1512
-				if ($totals) {
1513
-					$html .= I18N::translate('%1$s (%2$s)', '<span dir="auto">' . $spfxsurn . '</span>', I18N::number(count($indis)));
1514
-				} else {
1515
-					$html .= $spfxsurn;
1516
-				}
1517
-				$html .= '</a> ';
1518
-			}
1519
-		}
1520
-
1521
-		return '<div class="tag_cloud">' . $html . '</div>';
1522
-	}
1523
-
1524
-	/**
1525
-	 * Print a list of surnames.
1526
-	 *
1527
-	 * @param string[][] $surnames array (of SURN, of array of SPFX_SURN, of array of PID)
1528
-	 * @param int $style 1=bullet list, 2=semicolon-separated list, 3=tabulated list with up to 4 columns
1529
-	 * @param bool $totals show totals after each name
1530
-	 * @param string $script indilist or famlist
1531
-	 * @param Tree $tree Link back to the individual list in this tree
1532
-	 *
1533
-	 * @return string
1534
-	 */
1535
-	public static function surnameList($surnames, $style, $totals, $script, Tree $tree) {
1536
-		$html = array();
1537
-		foreach ($surnames as $surn => $surns) {
1538
-			// Each surname links back to the indilist
1539
-			if ($surn) {
1540
-				$url = $script . '?surname=' . urlencode($surn) . '&amp;ged=' . $tree->getNameUrl();
1541
-			} else {
1542
-				$url = $script . '?alpha=,&amp;ged=' . $tree->getNameUrl();
1543
-			}
1544
-			// If all the surnames are just case variants, then merge them into one
1545
-			// Comment out this block if you want SMITH listed separately from Smith
1546
-			$first_spfxsurn = null;
1547
-			foreach ($surns as $spfxsurn => $indis) {
1548
-				if ($first_spfxsurn) {
1549
-					if (I18N::strtoupper($spfxsurn) == I18N::strtoupper($first_spfxsurn)) {
1550
-						$surns[$first_spfxsurn] = array_merge($surns[$first_spfxsurn], $surns[$spfxsurn]);
1551
-						unset($surns[$spfxsurn]);
1552
-					}
1553
-				} else {
1554
-					$first_spfxsurn = $spfxsurn;
1555
-				}
1556
-			}
1557
-			$subhtml = '<a href="' . $url . '" dir="auto">' . Filter::escapeHtml(implode(I18N::$list_separator, array_keys($surns))) . '</a>';
1558
-
1559
-			if ($totals) {
1560
-				$subtotal = 0;
1561
-				foreach ($surns as $indis) {
1562
-					$subtotal += count($indis);
1563
-				}
1564
-				$subhtml .= '&nbsp;(' . I18N::number($subtotal) . ')';
1565
-			}
1566
-			$html[] = $subhtml;
1567
-
1568
-		}
1569
-		switch ($style) {
1570
-		case 1:
1571
-			return '<ul><li>' . implode('</li><li>', $html) . '</li></ul>';
1572
-		case 2:
1573
-			return implode(I18N::$list_separator, $html);
1574
-		case 3:
1575
-			$i     = 0;
1576
-			$count = count($html);
1577
-			if ($count > 36) {
1578
-				$col = 4;
1579
-			} elseif ($count > 18) {
1580
-				$col = 3;
1581
-			} elseif ($count > 6) {
1582
-				$col = 2;
1583
-			} else {
1584
-				$col = 1;
1585
-			}
1586
-			$newcol = ceil($count / $col);
1587
-			$html2  = '<table class="list_table"><tr>';
1588
-			$html2 .= '<td class="list_value" style="padding: 14px;">';
1589
-
1590
-			foreach ($html as $surns) {
1591
-				$html2 .= $surns . '<br>';
1592
-				$i++;
1593
-				if ($i == $newcol && $i < $count) {
1594
-					$html2 .= '</td><td class="list_value" style="padding: 14px;">';
1595
-					$newcol = $i + ceil($count / $col);
1596
-				}
1597
-			}
1598
-			$html2 .= '</td></tr></table>';
1599
-
1600
-			return $html2;
1601
-		}
1602
-	}
1603
-	/**
1604
-	 * Print a table of events
1605
-	 *
1606
-	 * @param int $startjd
1607
-	 * @param int $endjd
1608
-	 * @param string $events
1609
-	 * @param bool $only_living
1610
-	 * @param string $sort_by
1611
-	 *
1612
-	 * @return string
1613
-	 */
1614
-	public static function eventsTable($startjd, $endjd, $events = 'BIRT MARR DEAT', $only_living = false, $sort_by = 'anniv') {
1615
-		global $controller, $WT_TREE;
1616
-
1617
-		$html     = '';
1618
-		$table_id = 'table-even-' . Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
1619
-		$controller
1620
-			->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
1621
-			->addInlineJavascript('
1425
+        if ($script == 'famlist.php') {
1426
+            $col_heading = I18N::translate('Spouses');
1427
+        } else {
1428
+            $col_heading = I18N::translate('Individuals');
1429
+        }
1430
+
1431
+        $html .=
1432
+            '<table class="surname-list">' .
1433
+            '<thead>' .
1434
+            '<tr>' .
1435
+            '<th>' . GedcomTag::getLabel('SURN') . '</th>' .
1436
+            '<th>' . $col_heading . '</th>' .
1437
+            '</tr>' .
1438
+            '</thead>';
1439
+
1440
+        $html .= '<tbody>';
1441
+        foreach ($surnames as $surn => $surns) {
1442
+            // Each surname links back to the indi/fam surname list
1443
+            if ($surn) {
1444
+                $url = $script . '?surname=' . rawurlencode($surn) . '&amp;ged=' . $tree->getNameUrl();
1445
+            } else {
1446
+                $url = $script . '?alpha=,&amp;ged=' . $tree->getNameUrl();
1447
+            }
1448
+            $html .= '<tr>';
1449
+            // Surname
1450
+            $html .= '<td data-sort="' . Filter::escapeHtml($surn) . '">';
1451
+            // Multiple surname variants, e.g. von Groot, van Groot, van der Groot, etc.
1452
+            foreach ($surns as $spfxsurn => $indis) {
1453
+                if ($spfxsurn) {
1454
+                    $html .= '<a href="' . $url . '" dir="auto">' . Filter::escapeHtml($spfxsurn) . '</a><br>';
1455
+                } else {
1456
+                    // No surname, but a value from "2 SURN"? A common workaround for toponyms, etc.
1457
+                    $html .= '<a href="' . $url . '" dir="auto">' . Filter::escapeHtml($surn) . '</a><br>';
1458
+                }
1459
+            }
1460
+            $html .= '</td>';
1461
+            // Surname count
1462
+            $subtotal = 0;
1463
+            foreach ($surns as $indis) {
1464
+                $subtotal += count($indis);
1465
+            }
1466
+            $html .= '<td class="center" data-sort="' . $subtotal . '">';
1467
+            foreach ($surns as $indis) {
1468
+                $html .= I18N::number(count($indis)) . '<br>';
1469
+            }
1470
+            if (count($surns) > 1) {
1471
+                // More than one surname variant? Show a subtotal
1472
+                $html .= I18N::number($subtotal);
1473
+            }
1474
+            $html .= '</td>';
1475
+            $html .= '</tr>';
1476
+        }
1477
+        $html .= '</tbody></table>';
1478
+
1479
+        return $html;
1480
+    }
1481
+
1482
+    /**
1483
+     * Print a tagcloud of surnames.
1484
+     *
1485
+     * @param string[][] $surnames array (of SURN, of array of SPFX_SURN, of array of PID)
1486
+     * @param string $script indilist or famlist
1487
+     * @param bool $totals show totals after each name
1488
+     * @param Tree $tree generate links to this tree
1489
+     *
1490
+     * @return string
1491
+     */
1492
+    public static function surnameTagCloud($surnames, $script, $totals, Tree $tree) {
1493
+        $minimum = PHP_INT_MAX;
1494
+        $maximum = 1;
1495
+        foreach ($surnames as $surn => $surns) {
1496
+            foreach ($surns as $spfxsurn => $indis) {
1497
+                $maximum = max($maximum, count($indis));
1498
+                $minimum = min($minimum, count($indis));
1499
+            }
1500
+        }
1501
+
1502
+        $html = '';
1503
+        foreach ($surnames as $surn => $surns) {
1504
+            foreach ($surns as $spfxsurn => $indis) {
1505
+                if ($maximum === $minimum) {
1506
+                    // All surnames occur the same number of times
1507
+                    $size = 150.0;
1508
+                } else {
1509
+                    $size = 75.0 + 125.0 * (count($indis) - $minimum) / ($maximum - $minimum);
1510
+                }
1511
+                $html .= '<a style="font-size:' . $size . '%" href="' . $script . '?surname=' . Filter::escapeUrl($surn) . '&amp;ged=' . $tree->getNameUrl() . '">';
1512
+                if ($totals) {
1513
+                    $html .= I18N::translate('%1$s (%2$s)', '<span dir="auto">' . $spfxsurn . '</span>', I18N::number(count($indis)));
1514
+                } else {
1515
+                    $html .= $spfxsurn;
1516
+                }
1517
+                $html .= '</a> ';
1518
+            }
1519
+        }
1520
+
1521
+        return '<div class="tag_cloud">' . $html . '</div>';
1522
+    }
1523
+
1524
+    /**
1525
+     * Print a list of surnames.
1526
+     *
1527
+     * @param string[][] $surnames array (of SURN, of array of SPFX_SURN, of array of PID)
1528
+     * @param int $style 1=bullet list, 2=semicolon-separated list, 3=tabulated list with up to 4 columns
1529
+     * @param bool $totals show totals after each name
1530
+     * @param string $script indilist or famlist
1531
+     * @param Tree $tree Link back to the individual list in this tree
1532
+     *
1533
+     * @return string
1534
+     */
1535
+    public static function surnameList($surnames, $style, $totals, $script, Tree $tree) {
1536
+        $html = array();
1537
+        foreach ($surnames as $surn => $surns) {
1538
+            // Each surname links back to the indilist
1539
+            if ($surn) {
1540
+                $url = $script . '?surname=' . urlencode($surn) . '&amp;ged=' . $tree->getNameUrl();
1541
+            } else {
1542
+                $url = $script . '?alpha=,&amp;ged=' . $tree->getNameUrl();
1543
+            }
1544
+            // If all the surnames are just case variants, then merge them into one
1545
+            // Comment out this block if you want SMITH listed separately from Smith
1546
+            $first_spfxsurn = null;
1547
+            foreach ($surns as $spfxsurn => $indis) {
1548
+                if ($first_spfxsurn) {
1549
+                    if (I18N::strtoupper($spfxsurn) == I18N::strtoupper($first_spfxsurn)) {
1550
+                        $surns[$first_spfxsurn] = array_merge($surns[$first_spfxsurn], $surns[$spfxsurn]);
1551
+                        unset($surns[$spfxsurn]);
1552
+                    }
1553
+                } else {
1554
+                    $first_spfxsurn = $spfxsurn;
1555
+                }
1556
+            }
1557
+            $subhtml = '<a href="' . $url . '" dir="auto">' . Filter::escapeHtml(implode(I18N::$list_separator, array_keys($surns))) . '</a>';
1558
+
1559
+            if ($totals) {
1560
+                $subtotal = 0;
1561
+                foreach ($surns as $indis) {
1562
+                    $subtotal += count($indis);
1563
+                }
1564
+                $subhtml .= '&nbsp;(' . I18N::number($subtotal) . ')';
1565
+            }
1566
+            $html[] = $subhtml;
1567
+
1568
+        }
1569
+        switch ($style) {
1570
+        case 1:
1571
+            return '<ul><li>' . implode('</li><li>', $html) . '</li></ul>';
1572
+        case 2:
1573
+            return implode(I18N::$list_separator, $html);
1574
+        case 3:
1575
+            $i     = 0;
1576
+            $count = count($html);
1577
+            if ($count > 36) {
1578
+                $col = 4;
1579
+            } elseif ($count > 18) {
1580
+                $col = 3;
1581
+            } elseif ($count > 6) {
1582
+                $col = 2;
1583
+            } else {
1584
+                $col = 1;
1585
+            }
1586
+            $newcol = ceil($count / $col);
1587
+            $html2  = '<table class="list_table"><tr>';
1588
+            $html2 .= '<td class="list_value" style="padding: 14px;">';
1589
+
1590
+            foreach ($html as $surns) {
1591
+                $html2 .= $surns . '<br>';
1592
+                $i++;
1593
+                if ($i == $newcol && $i < $count) {
1594
+                    $html2 .= '</td><td class="list_value" style="padding: 14px;">';
1595
+                    $newcol = $i + ceil($count / $col);
1596
+                }
1597
+            }
1598
+            $html2 .= '</td></tr></table>';
1599
+
1600
+            return $html2;
1601
+        }
1602
+    }
1603
+    /**
1604
+     * Print a table of events
1605
+     *
1606
+     * @param int $startjd
1607
+     * @param int $endjd
1608
+     * @param string $events
1609
+     * @param bool $only_living
1610
+     * @param string $sort_by
1611
+     *
1612
+     * @return string
1613
+     */
1614
+    public static function eventsTable($startjd, $endjd, $events = 'BIRT MARR DEAT', $only_living = false, $sort_by = 'anniv') {
1615
+        global $controller, $WT_TREE;
1616
+
1617
+        $html     = '';
1618
+        $table_id = 'table-even-' . Uuid::uuid4(); // lists requires a unique ID in case there are multiple lists per page
1619
+        $controller
1620
+            ->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
1621
+            ->addInlineJavascript('
1622 1622
 				jQuery.fn.dataTableExt.oSort["text-asc"] = textCompareAsc;
1623 1623
 				jQuery.fn.dataTableExt.oSort["text-desc"] = textCompareDesc;
1624 1624
 				jQuery("#' . $table_id . '").dataTable({
@@ -1640,341 +1640,341 @@  discard block
 block discarded – undo
1640 1640
 				});
1641 1641
 			');
1642 1642
 
1643
-		// Did we have any output? Did we skip anything?
1644
-		$filter          = 0;
1645
-		$filtered_events = array();
1646
-
1647
-		foreach (FunctionsDb::getEventsList($startjd, $endjd, $events, $WT_TREE) as $fact) {
1648
-			$record = $fact->getParent();
1649
-			// Only living people ?
1650
-			if ($only_living) {
1651
-				if ($record instanceof Individual && $record->isDead()) {
1652
-					$filter++;
1653
-					continue;
1654
-				}
1655
-				if ($record instanceof Family) {
1656
-					$husb = $record->getHusband();
1657
-					if (is_null($husb) || $husb->isDead()) {
1658
-						$filter++;
1659
-						continue;
1660
-					}
1661
-					$wife = $record->getWife();
1662
-					if (is_null($wife) || $wife->isDead()) {
1663
-						$filter++;
1664
-						continue;
1665
-					}
1666
-				}
1667
-			}
1668
-
1669
-			$filtered_events[] = $fact;
1670
-		}
1671
-
1672
-		if (!empty($filtered_events)) {
1673
-			$html .= '<table id="' . $table_id . '" class="width100">';
1674
-			$html .= '<thead><tr>';
1675
-			$html .= '<th>' . I18N::translate('Record') . '</th>';
1676
-			$html .= '<th>' . GedcomTag::getLabel('DATE') . '</th>';
1677
-			$html .= '<th><i class="icon-reminder" title="' . I18N::translate('Anniversary') . '"></i></th>';
1678
-			$html .= '<th>' . GedcomTag::getLabel('EVEN') . '</th>';
1679
-			$html .= '</tr></thead><tbody>';
1680
-
1681
-			foreach ($filtered_events as $n => $fact) {
1682
-				$record = $fact->getParent();
1683
-				$html .= '<tr>';
1684
-				$html .= '<td data-sort="' . Filter::escapeHtml($record->getSortName()) . '">';
1685
-				$html .= '<a href="' . $record->getHtmlUrl() . '">' . $record->getFullName() . '</a>';
1686
-				if ($record instanceof Individual) {
1687
-					$html .= $record->getSexImage();
1688
-				}
1689
-				$html .= '</td>';
1690
-				$html .= '<td data-sort="' . $fact->getDate()->minimumJulianDay() . '">';
1691
-				$html .= $fact->getDate()->display();
1692
-				$html .= '</td>';
1693
-				$html .= '<td class="center" data-sort="' . $fact->anniv . '">';
1694
-				$html .= ($fact->anniv ? I18N::number($fact->anniv) : '');
1695
-				$html .= '</td>';
1696
-				$html .= '<td class="center">' . $fact->getLabel() . '</td>';
1697
-				$html .= '</tr>';
1698
-			}
1699
-
1700
-			$html .= '</tbody></table>';
1701
-		} else {
1702
-			if ($endjd === WT_CLIENT_JD) {
1703
-				// We're dealing with the Today’s Events block
1704
-				if ($filter === 0) {
1705
-					$html .=  I18N::translate('No events exist for today.');
1706
-				} else {
1707
-					$html .=  I18N::translate('No events for living individuals exist for today.');
1708
-				}
1709
-			} else {
1710
-				// We're dealing with the Upcoming Events block
1711
-				if ($filter === 0) {
1712
-					if ($endjd === $startjd) {
1713
-						$html .=  I18N::translate('No events exist for tomorrow.');
1714
-					} else {
1715
-						$html .=  /* I18N: translation for %s==1 is unused; it is translated separately as “tomorrow” */ I18N::plural('No events exist for the next %s day.', 'No events exist for the next %s days.', $endjd - $startjd + 1, I18N::number($endjd - $startjd + 1));
1716
-					}
1717
-				} else {
1718
-					if ($endjd === $startjd) {
1719
-						$html .=  I18N::translate('No events for living individuals exist for tomorrow.');
1720
-					} else {
1721
-						// I18N: translation for %s==1 is unused; it is translated separately as “tomorrow”
1722
-						$html .=  I18N::plural('No events for living people exist for the next %s day.', 'No events for living people exist for the next %s days.', $endjd - $startjd + 1, I18N::number($endjd - $startjd + 1));
1723
-					}
1724
-				}
1725
-			}
1726
-		}
1727
-
1728
-		return $html;
1729
-	}
1730
-
1731
-	/**
1732
-	 * Print a list of events
1733
-	 *
1734
-	 * This performs the same function as print_events_table(), but formats the output differently.
1735
-	 *
1736
-	 * @param int $startjd
1737
-	 * @param int $endjd
1738
-	 * @param string $events
1739
-	 * @param bool $only_living
1740
-	 * @param string $sort_by
1741
-	 *
1742
-	 * @return string
1743
-	 */
1744
-	public static function eventsList($startjd, $endjd, $events = 'BIRT MARR DEAT', $only_living = false, $sort_by = 'anniv') {
1745
-		global $WT_TREE;
1746
-
1747
-		// Did we have any output? Did we skip anything?
1748
-		$output          = 0;
1749
-		$filter          = 0;
1750
-		$filtered_events = array();
1751
-		$html            = '';
1752
-		foreach (FunctionsDb::getEventsList($startjd, $endjd, $events, $WT_TREE) as $fact) {
1753
-			$record = $fact->getParent();
1754
-			// only living people ?
1755
-			if ($only_living) {
1756
-				if ($record instanceof Individual && $record->isDead()) {
1757
-					$filter++;
1758
-					continue;
1759
-				}
1760
-				if ($record instanceof Family) {
1761
-					$husb = $record->getHusband();
1762
-					if (is_null($husb) || $husb->isDead()) {
1763
-						$filter++;
1764
-						continue;
1765
-					}
1766
-					$wife = $record->getWife();
1767
-					if (is_null($wife) || $wife->isDead()) {
1768
-						$filter++;
1769
-						continue;
1770
-					}
1771
-				}
1772
-			}
1773
-
1774
-			$output++;
1775
-
1776
-			$filtered_events[] = $fact;
1777
-		}
1778
-
1779
-		// Now we've filtered the list, we can sort by event, if required
1780
-		switch ($sort_by) {
1781
-		case 'anniv':
1782
-			// Data is already sorted by anniversary date
1783
-			break;
1784
-		case 'alpha':
1785
-			uasort($filtered_events, function (Fact $x, Fact $y) {
1786
-				return GedcomRecord::compare($x->getParent(), $y->getParent());
1787
-			});
1788
-			break;
1789
-		}
1790
-
1791
-		foreach ($filtered_events as $fact) {
1792
-			$record = $fact->getParent();
1793
-			$html .= '<a href="' . $record->getHtmlUrl() . '" class="list_item name2">' . $record->getFullName() . '</a>';
1794
-			if ($record instanceof Individual) {
1795
-				$html .= $record->getSexImage();
1796
-			}
1797
-			$html .= '<br><div class="indent">';
1798
-			$html .= $fact->getLabel() . ' — ' . $fact->getDate()->display(true);
1799
-			if ($fact->anniv) {
1800
-				$html .= ' (' . I18N::translate('%s year anniversary', I18N::number($fact->anniv)) . ')';
1801
-			}
1802
-			if (!$fact->getPlace()->isEmpty()) {
1803
-				$html .= ' — <a href="' . $fact->getPlace()->getURL() . '">' . $fact->getPlace()->getFullName() . '</a>';
1804
-			}
1805
-			$html .= '</div>';
1806
-		}
1807
-
1808
-		// Print a final summary message about restricted/filtered facts
1809
-		$summary = '';
1810
-		if ($endjd == WT_CLIENT_JD) {
1811
-			// We're dealing with the Today’s Events block
1812
-			if ($output == 0) {
1813
-				if ($filter == 0) {
1814
-					$summary = I18N::translate('No events exist for today.');
1815
-				} else {
1816
-					$summary = I18N::translate('No events for living individuals exist for today.');
1817
-				}
1818
-			}
1819
-		} else {
1820
-			// We're dealing with the Upcoming Events block
1821
-			if ($output == 0) {
1822
-				if ($filter == 0) {
1823
-					if ($endjd == $startjd) {
1824
-						$summary = I18N::translate('No events exist for tomorrow.');
1825
-					} else {
1826
-						// I18N: translation for %s==1 is unused; it is translated separately as “tomorrow”
1827
-						$summary = I18N::plural('No events exist for the next %s day.', 'No events exist for the next %s days.', $endjd - $startjd + 1, I18N::number($endjd - $startjd + 1));
1828
-					}
1829
-				} else {
1830
-					if ($endjd == $startjd) {
1831
-						$summary = I18N::translate('No events for living individuals exist for tomorrow.');
1832
-					} else {
1833
-						// I18N: translation for %s==1 is unused; it is translated separately as “tomorrow”
1834
-						$summary = I18N::plural('No events for living people exist for the next %s day.', 'No events for living people exist for the next %s days.', $endjd - $startjd + 1, I18N::number($endjd - $startjd + 1));
1835
-					}
1836
-				}
1837
-			}
1838
-		}
1839
-		if ($summary) {
1840
-			$html .= '<b>' . $summary . '</b>';
1841
-		}
1842
-
1843
-		return $html;
1844
-	}
1845
-
1846
-	/**
1847
-	 * Print a chart by age using Google chart API
1848
-	 *
1849
-	 * @param int[] $data
1850
-	 * @param string $title
1851
-	 *
1852
-	 * @return string
1853
-	 */
1854
-	public static function chartByAge($data, $title) {
1855
-		$count  = 0;
1856
-		$agemax = 0;
1857
-		$vmax   = 0;
1858
-		$avg    = 0;
1859
-		foreach ($data as $age => $v) {
1860
-			$n      = strlen($v);
1861
-			$vmax   = max($vmax, $n);
1862
-			$agemax = max($agemax, $age);
1863
-			$count += $n;
1864
-			$avg += $age * $n;
1865
-		}
1866
-		if ($count < 1) {
1867
-			return '';
1868
-		}
1869
-		$avg       = round($avg / $count);
1870
-		$chart_url = "https://chart.googleapis.com/chart?cht=bvs"; // chart type
1871
-		$chart_url .= "&amp;chs=725x150"; // size
1872
-		$chart_url .= "&amp;chbh=3,2,2"; // bvg : 4,1,2
1873
-		$chart_url .= "&amp;chf=bg,s,FFFFFF99"; //background color
1874
-		$chart_url .= "&amp;chco=0000FF,FFA0CB,FF0000"; // bar color
1875
-		$chart_url .= "&amp;chdl=" . rawurlencode(I18N::translate('Males')) . "|" . rawurlencode(I18N::translate('Females')) . "|" . rawurlencode(I18N::translate('Average age') . ": " . $avg); // legend & average age
1876
-		$chart_url .= "&amp;chtt=" . rawurlencode($title); // title
1877
-		$chart_url .= "&amp;chxt=x,y,r"; // axis labels specification
1878
-		$chart_url .= "&amp;chm=V,FF0000,0," . ($avg - 0.3) . ",1"; // average age line marker
1879
-		$chart_url .= "&amp;chxl=0:|"; // label
1880
-		for ($age = 0; $age <= $agemax; $age += 5) {
1881
-			$chart_url .= $age . "|||||"; // x axis
1882
-		}
1883
-		$chart_url .= "|1:||" . rawurlencode(I18N::percentage($vmax / $count)); // y axis
1884
-		$chart_url .= "|2:||";
1885
-		$step = $vmax;
1886
-		for ($d = $vmax; $d > 0; $d--) {
1887
-			if ($vmax < ($d * 10 + 1) && ($vmax % $d) == 0) {
1888
-				$step = $d;
1889
-			}
1890
-		}
1891
-		if ($step == $vmax) {
1892
-			for ($d = $vmax - 1; $d > 0; $d--) {
1893
-				if (($vmax - 1) < ($d * 10 + 1) && (($vmax - 1) % $d) == 0) {
1894
-					$step = $d;
1895
-				}
1896
-			}
1897
-		}
1898
-		for ($n = $step; $n < $vmax; $n += $step) {
1899
-			$chart_url .= $n . "|";
1900
-		}
1901
-		$chart_url .= rawurlencode($vmax . " / " . $count); // r axis
1902
-		$chart_url .= "&amp;chg=100," . round(100 * $step / $vmax, 1) . ",1,5"; // grid
1903
-		$chart_url .= "&amp;chd=s:"; // data : simple encoding from A=0 to 9=61
1904
-		$CHART_ENCODING61 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
1905
-		for ($age = 0; $age <= $agemax; $age++) {
1906
-			$chart_url .= $CHART_ENCODING61[(int) (substr_count($data[$age], "M") * 61 / $vmax)];
1907
-		}
1908
-		$chart_url .= ",";
1909
-		for ($age = 0; $age <= $agemax; $age++) {
1910
-			$chart_url .= $CHART_ENCODING61[(int) (substr_count($data[$age], "F") * 61 / $vmax)];
1911
-		}
1912
-		$html = '<img src="' . $chart_url . '" alt="' . $title . '" title="' . $title . '" class="gchart">';
1913
-
1914
-		return $html;
1915
-	}
1916
-
1917
-	/**
1918
-	 * Print a chart by decade using Google chart API
1919
-	 *
1920
-	 * @param int[] $data
1921
-	 * @param string $title
1922
-	 *
1923
-	 * @return string
1924
-	 */
1925
-	public static function chartByDecade($data, $title) {
1926
-		$count = 0;
1927
-		$vmax  = 0;
1928
-		foreach ($data as $v) {
1929
-			$n    = strlen($v);
1930
-			$vmax = max($vmax, $n);
1931
-			$count += $n;
1932
-		}
1933
-		if ($count < 1) {
1934
-			return '';
1935
-		}
1936
-		$chart_url = "https://chart.googleapis.com/chart?cht=bvs"; // chart type
1937
-		$chart_url .= "&amp;chs=360x150"; // size
1938
-		$chart_url .= "&amp;chbh=3,3"; // bvg : 4,1,2
1939
-		$chart_url .= "&amp;chf=bg,s,FFFFFF99"; //background color
1940
-		$chart_url .= "&amp;chco=0000FF,FFA0CB"; // bar color
1941
-		$chart_url .= "&amp;chtt=" . rawurlencode($title); // title
1942
-		$chart_url .= "&amp;chxt=x,y,r"; // axis labels specification
1943
-		$chart_url .= "&amp;chxl=0:|&lt;|||"; // <1570
1944
-		for ($y = 1600; $y < 2030; $y += 50) {
1945
-			$chart_url .= $y . "|||||"; // x axis
1946
-		}
1947
-		$chart_url .= "|1:||" . rawurlencode(I18N::percentage($vmax / $count)); // y axis
1948
-		$chart_url .= "|2:||";
1949
-		$step = $vmax;
1950
-		for ($d = $vmax; $d > 0; $d--) {
1951
-			if ($vmax < ($d * 10 + 1) && ($vmax % $d) == 0) {
1952
-				$step = $d;
1953
-			}
1954
-		}
1955
-		if ($step == $vmax) {
1956
-			for ($d = $vmax - 1; $d > 0; $d--) {
1957
-				if (($vmax - 1) < ($d * 10 + 1) && (($vmax - 1) % $d) == 0) {
1958
-					$step = $d;
1959
-				}
1960
-			}
1961
-		}
1962
-		for ($n = $step; $n < $vmax; $n += $step) {
1963
-			$chart_url .= $n . "|";
1964
-		}
1965
-		$chart_url .= rawurlencode($vmax . " / " . $count); // r axis
1966
-		$chart_url .= "&amp;chg=100," . round(100 * $step / $vmax, 1) . ",1,5"; // grid
1967
-		$chart_url .= "&amp;chd=s:"; // data : simple encoding from A=0 to 9=61
1968
-		$CHART_ENCODING61 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
1969
-		for ($y = 1570; $y < 2030; $y += 10) {
1970
-			$chart_url .= $CHART_ENCODING61[(int) (substr_count($data[$y], "M") * 61 / $vmax)];
1971
-		}
1972
-		$chart_url .= ",";
1973
-		for ($y = 1570; $y < 2030; $y += 10) {
1974
-			$chart_url .= $CHART_ENCODING61[(int) (substr_count($data[$y], "F") * 61 / $vmax)];
1975
-		}
1976
-		$html = '<img src="' . $chart_url . '" alt="' . $title . '" title="' . $title . '" class="gchart">';
1977
-
1978
-		return $html;
1979
-	}
1643
+        // Did we have any output? Did we skip anything?
1644
+        $filter          = 0;
1645
+        $filtered_events = array();
1646
+
1647
+        foreach (FunctionsDb::getEventsList($startjd, $endjd, $events, $WT_TREE) as $fact) {
1648
+            $record = $fact->getParent();
1649
+            // Only living people ?
1650
+            if ($only_living) {
1651
+                if ($record instanceof Individual && $record->isDead()) {
1652
+                    $filter++;
1653
+                    continue;
1654
+                }
1655
+                if ($record instanceof Family) {
1656
+                    $husb = $record->getHusband();
1657
+                    if (is_null($husb) || $husb->isDead()) {
1658
+                        $filter++;
1659
+                        continue;
1660
+                    }
1661
+                    $wife = $record->getWife();
1662
+                    if (is_null($wife) || $wife->isDead()) {
1663
+                        $filter++;
1664
+                        continue;
1665
+                    }
1666
+                }
1667
+            }
1668
+
1669
+            $filtered_events[] = $fact;
1670
+        }
1671
+
1672
+        if (!empty($filtered_events)) {
1673
+            $html .= '<table id="' . $table_id . '" class="width100">';
1674
+            $html .= '<thead><tr>';
1675
+            $html .= '<th>' . I18N::translate('Record') . '</th>';
1676
+            $html .= '<th>' . GedcomTag::getLabel('DATE') . '</th>';
1677
+            $html .= '<th><i class="icon-reminder" title="' . I18N::translate('Anniversary') . '"></i></th>';
1678
+            $html .= '<th>' . GedcomTag::getLabel('EVEN') . '</th>';
1679
+            $html .= '</tr></thead><tbody>';
1680
+
1681
+            foreach ($filtered_events as $n => $fact) {
1682
+                $record = $fact->getParent();
1683
+                $html .= '<tr>';
1684
+                $html .= '<td data-sort="' . Filter::escapeHtml($record->getSortName()) . '">';
1685
+                $html .= '<a href="' . $record->getHtmlUrl() . '">' . $record->getFullName() . '</a>';
1686
+                if ($record instanceof Individual) {
1687
+                    $html .= $record->getSexImage();
1688
+                }
1689
+                $html .= '</td>';
1690
+                $html .= '<td data-sort="' . $fact->getDate()->minimumJulianDay() . '">';
1691
+                $html .= $fact->getDate()->display();
1692
+                $html .= '</td>';
1693
+                $html .= '<td class="center" data-sort="' . $fact->anniv . '">';
1694
+                $html .= ($fact->anniv ? I18N::number($fact->anniv) : '');
1695
+                $html .= '</td>';
1696
+                $html .= '<td class="center">' . $fact->getLabel() . '</td>';
1697
+                $html .= '</tr>';
1698
+            }
1699
+
1700
+            $html .= '</tbody></table>';
1701
+        } else {
1702
+            if ($endjd === WT_CLIENT_JD) {
1703
+                // We're dealing with the Today’s Events block
1704
+                if ($filter === 0) {
1705
+                    $html .=  I18N::translate('No events exist for today.');
1706
+                } else {
1707
+                    $html .=  I18N::translate('No events for living individuals exist for today.');
1708
+                }
1709
+            } else {
1710
+                // We're dealing with the Upcoming Events block
1711
+                if ($filter === 0) {
1712
+                    if ($endjd === $startjd) {
1713
+                        $html .=  I18N::translate('No events exist for tomorrow.');
1714
+                    } else {
1715
+                        $html .=  /* I18N: translation for %s==1 is unused; it is translated separately as “tomorrow” */ I18N::plural('No events exist for the next %s day.', 'No events exist for the next %s days.', $endjd - $startjd + 1, I18N::number($endjd - $startjd + 1));
1716
+                    }
1717
+                } else {
1718
+                    if ($endjd === $startjd) {
1719
+                        $html .=  I18N::translate('No events for living individuals exist for tomorrow.');
1720
+                    } else {
1721
+                        // I18N: translation for %s==1 is unused; it is translated separately as “tomorrow”
1722
+                        $html .=  I18N::plural('No events for living people exist for the next %s day.', 'No events for living people exist for the next %s days.', $endjd - $startjd + 1, I18N::number($endjd - $startjd + 1));
1723
+                    }
1724
+                }
1725
+            }
1726
+        }
1727
+
1728
+        return $html;
1729
+    }
1730
+
1731
+    /**
1732
+     * Print a list of events
1733
+     *
1734
+     * This performs the same function as print_events_table(), but formats the output differently.
1735
+     *
1736
+     * @param int $startjd
1737
+     * @param int $endjd
1738
+     * @param string $events
1739
+     * @param bool $only_living
1740
+     * @param string $sort_by
1741
+     *
1742
+     * @return string
1743
+     */
1744
+    public static function eventsList($startjd, $endjd, $events = 'BIRT MARR DEAT', $only_living = false, $sort_by = 'anniv') {
1745
+        global $WT_TREE;
1746
+
1747
+        // Did we have any output? Did we skip anything?
1748
+        $output          = 0;
1749
+        $filter          = 0;
1750
+        $filtered_events = array();
1751
+        $html            = '';
1752
+        foreach (FunctionsDb::getEventsList($startjd, $endjd, $events, $WT_TREE) as $fact) {
1753
+            $record = $fact->getParent();
1754
+            // only living people ?
1755
+            if ($only_living) {
1756
+                if ($record instanceof Individual && $record->isDead()) {
1757
+                    $filter++;
1758
+                    continue;
1759
+                }
1760
+                if ($record instanceof Family) {
1761
+                    $husb = $record->getHusband();
1762
+                    if (is_null($husb) || $husb->isDead()) {
1763
+                        $filter++;
1764
+                        continue;
1765
+                    }
1766
+                    $wife = $record->getWife();
1767
+                    if (is_null($wife) || $wife->isDead()) {
1768
+                        $filter++;
1769
+                        continue;
1770
+                    }
1771
+                }
1772
+            }
1773
+
1774
+            $output++;
1775
+
1776
+            $filtered_events[] = $fact;
1777
+        }
1778
+
1779
+        // Now we've filtered the list, we can sort by event, if required
1780
+        switch ($sort_by) {
1781
+        case 'anniv':
1782
+            // Data is already sorted by anniversary date
1783
+            break;
1784
+        case 'alpha':
1785
+            uasort($filtered_events, function (Fact $x, Fact $y) {
1786
+                return GedcomRecord::compare($x->getParent(), $y->getParent());
1787
+            });
1788
+            break;
1789
+        }
1790
+
1791
+        foreach ($filtered_events as $fact) {
1792
+            $record = $fact->getParent();
1793
+            $html .= '<a href="' . $record->getHtmlUrl() . '" class="list_item name2">' . $record->getFullName() . '</a>';
1794
+            if ($record instanceof Individual) {
1795
+                $html .= $record->getSexImage();
1796
+            }
1797
+            $html .= '<br><div class="indent">';
1798
+            $html .= $fact->getLabel() . ' — ' . $fact->getDate()->display(true);
1799
+            if ($fact->anniv) {
1800
+                $html .= ' (' . I18N::translate('%s year anniversary', I18N::number($fact->anniv)) . ')';
1801
+            }
1802
+            if (!$fact->getPlace()->isEmpty()) {
1803
+                $html .= ' — <a href="' . $fact->getPlace()->getURL() . '">' . $fact->getPlace()->getFullName() . '</a>';
1804
+            }
1805
+            $html .= '</div>';
1806
+        }
1807
+
1808
+        // Print a final summary message about restricted/filtered facts
1809
+        $summary = '';
1810
+        if ($endjd == WT_CLIENT_JD) {
1811
+            // We're dealing with the Today’s Events block
1812
+            if ($output == 0) {
1813
+                if ($filter == 0) {
1814
+                    $summary = I18N::translate('No events exist for today.');
1815
+                } else {
1816
+                    $summary = I18N::translate('No events for living individuals exist for today.');
1817
+                }
1818
+            }
1819
+        } else {
1820
+            // We're dealing with the Upcoming Events block
1821
+            if ($output == 0) {
1822
+                if ($filter == 0) {
1823
+                    if ($endjd == $startjd) {
1824
+                        $summary = I18N::translate('No events exist for tomorrow.');
1825
+                    } else {
1826
+                        // I18N: translation for %s==1 is unused; it is translated separately as “tomorrow”
1827
+                        $summary = I18N::plural('No events exist for the next %s day.', 'No events exist for the next %s days.', $endjd - $startjd + 1, I18N::number($endjd - $startjd + 1));
1828
+                    }
1829
+                } else {
1830
+                    if ($endjd == $startjd) {
1831
+                        $summary = I18N::translate('No events for living individuals exist for tomorrow.');
1832
+                    } else {
1833
+                        // I18N: translation for %s==1 is unused; it is translated separately as “tomorrow”
1834
+                        $summary = I18N::plural('No events for living people exist for the next %s day.', 'No events for living people exist for the next %s days.', $endjd - $startjd + 1, I18N::number($endjd - $startjd + 1));
1835
+                    }
1836
+                }
1837
+            }
1838
+        }
1839
+        if ($summary) {
1840
+            $html .= '<b>' . $summary . '</b>';
1841
+        }
1842
+
1843
+        return $html;
1844
+    }
1845
+
1846
+    /**
1847
+     * Print a chart by age using Google chart API
1848
+     *
1849
+     * @param int[] $data
1850
+     * @param string $title
1851
+     *
1852
+     * @return string
1853
+     */
1854
+    public static function chartByAge($data, $title) {
1855
+        $count  = 0;
1856
+        $agemax = 0;
1857
+        $vmax   = 0;
1858
+        $avg    = 0;
1859
+        foreach ($data as $age => $v) {
1860
+            $n      = strlen($v);
1861
+            $vmax   = max($vmax, $n);
1862
+            $agemax = max($agemax, $age);
1863
+            $count += $n;
1864
+            $avg += $age * $n;
1865
+        }
1866
+        if ($count < 1) {
1867
+            return '';
1868
+        }
1869
+        $avg       = round($avg / $count);
1870
+        $chart_url = "https://chart.googleapis.com/chart?cht=bvs"; // chart type
1871
+        $chart_url .= "&amp;chs=725x150"; // size
1872
+        $chart_url .= "&amp;chbh=3,2,2"; // bvg : 4,1,2
1873
+        $chart_url .= "&amp;chf=bg,s,FFFFFF99"; //background color
1874
+        $chart_url .= "&amp;chco=0000FF,FFA0CB,FF0000"; // bar color
1875
+        $chart_url .= "&amp;chdl=" . rawurlencode(I18N::translate('Males')) . "|" . rawurlencode(I18N::translate('Females')) . "|" . rawurlencode(I18N::translate('Average age') . ": " . $avg); // legend & average age
1876
+        $chart_url .= "&amp;chtt=" . rawurlencode($title); // title
1877
+        $chart_url .= "&amp;chxt=x,y,r"; // axis labels specification
1878
+        $chart_url .= "&amp;chm=V,FF0000,0," . ($avg - 0.3) . ",1"; // average age line marker
1879
+        $chart_url .= "&amp;chxl=0:|"; // label
1880
+        for ($age = 0; $age <= $agemax; $age += 5) {
1881
+            $chart_url .= $age . "|||||"; // x axis
1882
+        }
1883
+        $chart_url .= "|1:||" . rawurlencode(I18N::percentage($vmax / $count)); // y axis
1884
+        $chart_url .= "|2:||";
1885
+        $step = $vmax;
1886
+        for ($d = $vmax; $d > 0; $d--) {
1887
+            if ($vmax < ($d * 10 + 1) && ($vmax % $d) == 0) {
1888
+                $step = $d;
1889
+            }
1890
+        }
1891
+        if ($step == $vmax) {
1892
+            for ($d = $vmax - 1; $d > 0; $d--) {
1893
+                if (($vmax - 1) < ($d * 10 + 1) && (($vmax - 1) % $d) == 0) {
1894
+                    $step = $d;
1895
+                }
1896
+            }
1897
+        }
1898
+        for ($n = $step; $n < $vmax; $n += $step) {
1899
+            $chart_url .= $n . "|";
1900
+        }
1901
+        $chart_url .= rawurlencode($vmax . " / " . $count); // r axis
1902
+        $chart_url .= "&amp;chg=100," . round(100 * $step / $vmax, 1) . ",1,5"; // grid
1903
+        $chart_url .= "&amp;chd=s:"; // data : simple encoding from A=0 to 9=61
1904
+        $CHART_ENCODING61 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
1905
+        for ($age = 0; $age <= $agemax; $age++) {
1906
+            $chart_url .= $CHART_ENCODING61[(int) (substr_count($data[$age], "M") * 61 / $vmax)];
1907
+        }
1908
+        $chart_url .= ",";
1909
+        for ($age = 0; $age <= $agemax; $age++) {
1910
+            $chart_url .= $CHART_ENCODING61[(int) (substr_count($data[$age], "F") * 61 / $vmax)];
1911
+        }
1912
+        $html = '<img src="' . $chart_url . '" alt="' . $title . '" title="' . $title . '" class="gchart">';
1913
+
1914
+        return $html;
1915
+    }
1916
+
1917
+    /**
1918
+     * Print a chart by decade using Google chart API
1919
+     *
1920
+     * @param int[] $data
1921
+     * @param string $title
1922
+     *
1923
+     * @return string
1924
+     */
1925
+    public static function chartByDecade($data, $title) {
1926
+        $count = 0;
1927
+        $vmax  = 0;
1928
+        foreach ($data as $v) {
1929
+            $n    = strlen($v);
1930
+            $vmax = max($vmax, $n);
1931
+            $count += $n;
1932
+        }
1933
+        if ($count < 1) {
1934
+            return '';
1935
+        }
1936
+        $chart_url = "https://chart.googleapis.com/chart?cht=bvs"; // chart type
1937
+        $chart_url .= "&amp;chs=360x150"; // size
1938
+        $chart_url .= "&amp;chbh=3,3"; // bvg : 4,1,2
1939
+        $chart_url .= "&amp;chf=bg,s,FFFFFF99"; //background color
1940
+        $chart_url .= "&amp;chco=0000FF,FFA0CB"; // bar color
1941
+        $chart_url .= "&amp;chtt=" . rawurlencode($title); // title
1942
+        $chart_url .= "&amp;chxt=x,y,r"; // axis labels specification
1943
+        $chart_url .= "&amp;chxl=0:|&lt;|||"; // <1570
1944
+        for ($y = 1600; $y < 2030; $y += 50) {
1945
+            $chart_url .= $y . "|||||"; // x axis
1946
+        }
1947
+        $chart_url .= "|1:||" . rawurlencode(I18N::percentage($vmax / $count)); // y axis
1948
+        $chart_url .= "|2:||";
1949
+        $step = $vmax;
1950
+        for ($d = $vmax; $d > 0; $d--) {
1951
+            if ($vmax < ($d * 10 + 1) && ($vmax % $d) == 0) {
1952
+                $step = $d;
1953
+            }
1954
+        }
1955
+        if ($step == $vmax) {
1956
+            for ($d = $vmax - 1; $d > 0; $d--) {
1957
+                if (($vmax - 1) < ($d * 10 + 1) && (($vmax - 1) % $d) == 0) {
1958
+                    $step = $d;
1959
+                }
1960
+            }
1961
+        }
1962
+        for ($n = $step; $n < $vmax; $n += $step) {
1963
+            $chart_url .= $n . "|";
1964
+        }
1965
+        $chart_url .= rawurlencode($vmax . " / " . $count); // r axis
1966
+        $chart_url .= "&amp;chg=100," . round(100 * $step / $vmax, 1) . ",1,5"; // grid
1967
+        $chart_url .= "&amp;chd=s:"; // data : simple encoding from A=0 to 9=61
1968
+        $CHART_ENCODING61 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
1969
+        for ($y = 1570; $y < 2030; $y += 10) {
1970
+            $chart_url .= $CHART_ENCODING61[(int) (substr_count($data[$y], "M") * 61 / $vmax)];
1971
+        }
1972
+        $chart_url .= ",";
1973
+        for ($y = 1570; $y < 2030; $y += 10) {
1974
+            $chart_url .= $CHART_ENCODING61[(int) (substr_count($data[$y], "F") * 61 / $vmax)];
1975
+        }
1976
+        $html = '<img src="' . $chart_url . '" alt="' . $title . '" title="' . $title . '" class="gchart">';
1977
+
1978
+        return $html;
1979
+    }
1980 1980
 }
Please login to merge, or discard this patch.
app/Module.php 3 patches
Upper-Lower-Casing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -135,7 +135,7 @@
 block discarded – undo
135 135
 					}
136 136
 				} catch (\Exception $ex) {
137 137
 					// The module has been deleted or is broken? Disable it.
138
-					Log::addConfigurationLog("Module {$module_name} is missing or broken - disabling it");
138
+					Log::addConfigurationLog("module {$module_name} is missing or broken - disabling it");
139 139
 					Database::prepare(
140 140
 						"UPDATE `##module` SET status = 'disabled' WHERE module_name = :module_name"
141 141
 					)->execute(array(
Please login to merge, or discard this patch.
Indentation   +411 added lines, -411 removed lines patch added patch discarded remove patch
@@ -28,440 +28,440 @@
 block discarded – undo
28 28
  * Functions for managing and maintaining modules.
29 29
  */
30 30
 class Module {
31
-	/**
32
-	 * Get a list of all core modules.  We need to identify
33
-	 * third-party during upgrade and on the module admin page.
34
-	 *
35
-	 * @return string[]
36
-	 */
37
-	public static function getCoreModuleNames() {
38
-		return array(
39
-			'GEDFact_assistant',
40
-			'ahnentafel_report',
41
-			'ancestors_chart',
42
-			'batch_update',
43
-			'bdm_report',
44
-			'birth_report',
45
-			'cemetery_report',
46
-			'change_report',
47
-			'charts',
48
-			'ckeditor',
49
-			'clippings',
50
-			'compact_tree_chart',
51
-			'death_report',
52
-			'descendancy',
53
-			'descendancy_chart',
54
-			'descendancy_report',
55
-			'extra_info',
56
-			'fact_sources',
57
-			'families',
58
-			'family_book_chart',
59
-			'family_group_report',
60
-			'family_nav',
61
-			'fan_chart',
62
-			'faq',
63
-			'gedcom_block',
64
-			'gedcom_favorites',
65
-			'gedcom_news',
66
-			'gedcom_stats',
67
-			'googlemap',
68
-			'hourglass_chart',
69
-			'html',
70
-			'individual_ext_report',
71
-			'individual_report',
72
-			'individuals',
73
-			'lifespans_chart',
74
-			'lightbox',
75
-			'logged_in',
76
-			'login_block',
77
-			'marriage_report',
78
-			'media',
79
-			'missing_facts_report',
80
-			'notes',
81
-			'occupation_report',
82
-			'page_menu',
83
-			'pedigree_chart',
84
-			'pedigree_report',
85
-			'personal_facts',
86
-			'random_media',
87
-			'recent_changes',
88
-			'relationships_chart',
89
-			'relative_ext_report',
90
-			'relatives',
91
-			'review_changes',
92
-			'sitemap',
93
-			'sources_tab',
94
-			'statistics_chart',
95
-			'stories',
96
-			'theme_select',
97
-			'timeline_chart',
98
-			'todays_events',
99
-			'todo',
100
-			'top10_givnnames',
101
-			'top10_pageviews',
102
-			'top10_surnames',
103
-			'tree',
104
-			'upcoming_events',
105
-			'user_blog',
106
-			'user_favorites',
107
-			'user_messages',
108
-			'user_welcome',
109
-			'yahrzeit',
110
-		);
111
-	}
31
+    /**
32
+     * Get a list of all core modules.  We need to identify
33
+     * third-party during upgrade and on the module admin page.
34
+     *
35
+     * @return string[]
36
+     */
37
+    public static function getCoreModuleNames() {
38
+        return array(
39
+            'GEDFact_assistant',
40
+            'ahnentafel_report',
41
+            'ancestors_chart',
42
+            'batch_update',
43
+            'bdm_report',
44
+            'birth_report',
45
+            'cemetery_report',
46
+            'change_report',
47
+            'charts',
48
+            'ckeditor',
49
+            'clippings',
50
+            'compact_tree_chart',
51
+            'death_report',
52
+            'descendancy',
53
+            'descendancy_chart',
54
+            'descendancy_report',
55
+            'extra_info',
56
+            'fact_sources',
57
+            'families',
58
+            'family_book_chart',
59
+            'family_group_report',
60
+            'family_nav',
61
+            'fan_chart',
62
+            'faq',
63
+            'gedcom_block',
64
+            'gedcom_favorites',
65
+            'gedcom_news',
66
+            'gedcom_stats',
67
+            'googlemap',
68
+            'hourglass_chart',
69
+            'html',
70
+            'individual_ext_report',
71
+            'individual_report',
72
+            'individuals',
73
+            'lifespans_chart',
74
+            'lightbox',
75
+            'logged_in',
76
+            'login_block',
77
+            'marriage_report',
78
+            'media',
79
+            'missing_facts_report',
80
+            'notes',
81
+            'occupation_report',
82
+            'page_menu',
83
+            'pedigree_chart',
84
+            'pedigree_report',
85
+            'personal_facts',
86
+            'random_media',
87
+            'recent_changes',
88
+            'relationships_chart',
89
+            'relative_ext_report',
90
+            'relatives',
91
+            'review_changes',
92
+            'sitemap',
93
+            'sources_tab',
94
+            'statistics_chart',
95
+            'stories',
96
+            'theme_select',
97
+            'timeline_chart',
98
+            'todays_events',
99
+            'todo',
100
+            'top10_givnnames',
101
+            'top10_pageviews',
102
+            'top10_surnames',
103
+            'tree',
104
+            'upcoming_events',
105
+            'user_blog',
106
+            'user_favorites',
107
+            'user_messages',
108
+            'user_welcome',
109
+            'yahrzeit',
110
+        );
111
+    }
112 112
 
113
-	/**
114
-	 * Get a list of all active (enabled) modules.
115
-	 *
116
-	 * @return AbstractModule[]
117
-	 */
118
-	private static function getActiveModules() {
119
-		/** @var AbstractModule[] - Only query the database once. */
120
-		static $modules;
113
+    /**
114
+     * Get a list of all active (enabled) modules.
115
+     *
116
+     * @return AbstractModule[]
117
+     */
118
+    private static function getActiveModules() {
119
+        /** @var AbstractModule[] - Only query the database once. */
120
+        static $modules;
121 121
 
122
-		if ($modules === null) {
123
-			$module_names = Database::prepare(
124
-				"SELECT module_name FROM `##module` WHERE status = 'enabled'"
125
-			)->fetchOneColumn();
122
+        if ($modules === null) {
123
+            $module_names = Database::prepare(
124
+                "SELECT module_name FROM `##module` WHERE status = 'enabled'"
125
+            )->fetchOneColumn();
126 126
 
127
-			$modules = array();
128
-			foreach ($module_names as $module_name) {
129
-				try {
130
-					$module = include WT_ROOT . WT_MODULES_DIR . $module_name . '/module.php';
131
-					if ($module instanceof AbstractModule) {
132
-						$modules[$module->getName()] = $module;
133
-					} else {
134
-						throw new \Exception;
135
-					}
136
-				} catch (\Exception $ex) {
137
-					// The module has been deleted or is broken? Disable it.
138
-					Log::addConfigurationLog("Module {$module_name} is missing or broken - disabling it");
139
-					Database::prepare(
140
-						"UPDATE `##module` SET status = 'disabled' WHERE module_name = :module_name"
141
-					)->execute(array(
142
-						'module_name' => $module_name,
143
-					));
144
-				}
145
-			}
146
-		}
127
+            $modules = array();
128
+            foreach ($module_names as $module_name) {
129
+                try {
130
+                    $module = include WT_ROOT . WT_MODULES_DIR . $module_name . '/module.php';
131
+                    if ($module instanceof AbstractModule) {
132
+                        $modules[$module->getName()] = $module;
133
+                    } else {
134
+                        throw new \Exception;
135
+                    }
136
+                } catch (\Exception $ex) {
137
+                    // The module has been deleted or is broken? Disable it.
138
+                    Log::addConfigurationLog("Module {$module_name} is missing or broken - disabling it");
139
+                    Database::prepare(
140
+                        "UPDATE `##module` SET status = 'disabled' WHERE module_name = :module_name"
141
+                    )->execute(array(
142
+                        'module_name' => $module_name,
143
+                    ));
144
+                }
145
+            }
146
+        }
147 147
 
148
-		return $modules;
149
-	}
148
+        return $modules;
149
+    }
150 150
 
151
-	/**
152
-	 * Get a list of modules which (a) provide a specific function and (b) we have permission to see.
153
-	 *
154
-	 * We cannot currently use auto-loading for modules, as there may be user-defined
155
-	 * modules about which the auto-loader knows nothing.
156
-	 *
157
-	 * @param Tree   $tree
158
-	 * @param string $component The type of module, such as "tab", "report" or "menu"
159
-	 *
160
-	 * @return AbstractModule[]
161
-	 */
162
-	private static function getActiveModulesByComponent(Tree $tree, $component) {
163
-		$module_names = Database::prepare(
164
-			"SELECT module_name" .
165
-			" FROM `##module`" .
166
-			" JOIN `##module_privacy` USING (module_name)" .
167
-			" WHERE gedcom_id = :tree_id AND component = :component AND status = 'enabled' AND access_level >= :access_level" .
168
-			" ORDER BY CASE component WHEN 'menu' THEN menu_order WHEN 'sidebar' THEN sidebar_order WHEN 'tab' THEN tab_order ELSE 0 END, module_name"
169
-		)->execute(array(
170
-			'tree_id'      => $tree->getTreeId(),
171
-			'component'    => $component,
172
-			'access_level' => Auth::accessLevel($tree),
173
-		))->fetchOneColumn();
151
+    /**
152
+     * Get a list of modules which (a) provide a specific function and (b) we have permission to see.
153
+     *
154
+     * We cannot currently use auto-loading for modules, as there may be user-defined
155
+     * modules about which the auto-loader knows nothing.
156
+     *
157
+     * @param Tree   $tree
158
+     * @param string $component The type of module, such as "tab", "report" or "menu"
159
+     *
160
+     * @return AbstractModule[]
161
+     */
162
+    private static function getActiveModulesByComponent(Tree $tree, $component) {
163
+        $module_names = Database::prepare(
164
+            "SELECT module_name" .
165
+            " FROM `##module`" .
166
+            " JOIN `##module_privacy` USING (module_name)" .
167
+            " WHERE gedcom_id = :tree_id AND component = :component AND status = 'enabled' AND access_level >= :access_level" .
168
+            " ORDER BY CASE component WHEN 'menu' THEN menu_order WHEN 'sidebar' THEN sidebar_order WHEN 'tab' THEN tab_order ELSE 0 END, module_name"
169
+        )->execute(array(
170
+            'tree_id'      => $tree->getTreeId(),
171
+            'component'    => $component,
172
+            'access_level' => Auth::accessLevel($tree),
173
+        ))->fetchOneColumn();
174 174
 
175
-		$array = array();
176
-		foreach ($module_names as $module_name) {
177
-			$interface = '\Fisharebest\Webtrees\Module\Module' . ucfirst($component) . 'Interface';
178
-			$module    = self::getModuleByName($module_name);
179
-			if ($module instanceof $interface) {
180
-				$array[$module_name] = $module;
181
-			}
182
-		}
175
+        $array = array();
176
+        foreach ($module_names as $module_name) {
177
+            $interface = '\Fisharebest\Webtrees\Module\Module' . ucfirst($component) . 'Interface';
178
+            $module    = self::getModuleByName($module_name);
179
+            if ($module instanceof $interface) {
180
+                $array[$module_name] = $module;
181
+            }
182
+        }
183 183
 
184
-		// The order of menus/sidebars/tabs is defined in the database. Others are sorted by name.
185
-		if ($component !== 'menu' && $component !== 'sidebar' && $component !== 'tab') {
186
-			uasort($array, function (AbstractModule $x, AbstractModule $y) {
187
-				return I18N::strcasecmp($x->getTitle(), $y->getTitle());
188
-			});
189
-		}
184
+        // The order of menus/sidebars/tabs is defined in the database. Others are sorted by name.
185
+        if ($component !== 'menu' && $component !== 'sidebar' && $component !== 'tab') {
186
+            uasort($array, function (AbstractModule $x, AbstractModule $y) {
187
+                return I18N::strcasecmp($x->getTitle(), $y->getTitle());
188
+            });
189
+        }
190 190
 
191
-		return $array;
192
-	}
191
+        return $array;
192
+    }
193 193
 
194
-	/**
195
-	 * Get a list of all modules, enabled or not, which provide a specific function.
196
-	 *
197
-	 * We cannot currently use auto-loading for modules, as there may be user-defined
198
-	 * modules about which the auto-loader knows nothing.
199
-	 *
200
-	 * @param string $component The type of module, such as "tab", "report" or "menu"
201
-	 *
202
-	 * @return AbstractModule[]
203
-	 */
204
-	public static function getAllModulesByComponent($component) {
205
-		$module_names = Database::prepare(
206
-			"SELECT module_name" .
207
-			" FROM `##module`" .
208
-			" ORDER BY CASE :component WHEN 'menu' THEN menu_order WHEN 'sidebar' THEN sidebar_order WHEN 'tab' THEN tab_order ELSE 0 END, module_name"
209
-		)->execute(array(
210
-			'component'    => $component,
211
-		))->fetchOneColumn();
194
+    /**
195
+     * Get a list of all modules, enabled or not, which provide a specific function.
196
+     *
197
+     * We cannot currently use auto-loading for modules, as there may be user-defined
198
+     * modules about which the auto-loader knows nothing.
199
+     *
200
+     * @param string $component The type of module, such as "tab", "report" or "menu"
201
+     *
202
+     * @return AbstractModule[]
203
+     */
204
+    public static function getAllModulesByComponent($component) {
205
+        $module_names = Database::prepare(
206
+            "SELECT module_name" .
207
+            " FROM `##module`" .
208
+            " ORDER BY CASE :component WHEN 'menu' THEN menu_order WHEN 'sidebar' THEN sidebar_order WHEN 'tab' THEN tab_order ELSE 0 END, module_name"
209
+        )->execute(array(
210
+            'component'    => $component,
211
+        ))->fetchOneColumn();
212 212
 
213
-		$array = array();
214
-		foreach ($module_names as $module_name) {
215
-			$interface = '\Fisharebest\Webtrees\Module\Module' . ucfirst($component) . 'Interface';
216
-			$module    = self::getModuleByName($module_name);
217
-			if ($module instanceof $interface) {
218
-				$array[$module_name] = $module;
219
-			}
220
-		}
213
+        $array = array();
214
+        foreach ($module_names as $module_name) {
215
+            $interface = '\Fisharebest\Webtrees\Module\Module' . ucfirst($component) . 'Interface';
216
+            $module    = self::getModuleByName($module_name);
217
+            if ($module instanceof $interface) {
218
+                $array[$module_name] = $module;
219
+            }
220
+        }
221 221
 
222
-		// The order of menus/sidebars/tabs is defined in the database. Others are sorted by name.
223
-		if ($component !== 'menu' && $component !== 'sidebar' && $component !== 'tab') {
224
-			uasort($array, function (AbstractModule $x, AbstractModule $y) {
225
-				return I18N::strcasecmp($x->getTitle(), $y->getTitle());
226
-			});
227
-		}
222
+        // The order of menus/sidebars/tabs is defined in the database. Others are sorted by name.
223
+        if ($component !== 'menu' && $component !== 'sidebar' && $component !== 'tab') {
224
+            uasort($array, function (AbstractModule $x, AbstractModule $y) {
225
+                return I18N::strcasecmp($x->getTitle(), $y->getTitle());
226
+            });
227
+        }
228 228
 
229
-		return $array;
230
-	}
229
+        return $array;
230
+    }
231 231
 
232
-	/**
233
-	 * Get a list of modules which (a) provide a block and (b) we have permission to see.
234
-	 *
235
-	 * @param Tree $tree
236
-	 *
237
-	 * @return ModuleBlockInterface[]
238
-	 */
239
-	public static function getActiveBlocks(Tree $tree) {
240
-		return self::getActiveModulesByComponent($tree, 'block');
241
-	}
232
+    /**
233
+     * Get a list of modules which (a) provide a block and (b) we have permission to see.
234
+     *
235
+     * @param Tree $tree
236
+     *
237
+     * @return ModuleBlockInterface[]
238
+     */
239
+    public static function getActiveBlocks(Tree $tree) {
240
+        return self::getActiveModulesByComponent($tree, 'block');
241
+    }
242 242
 
243
-	/**
244
-	 * Get a list of modules which (a) provide a chart and (b) we have permission to see.
245
-	 *
246
-	 * @param Tree $tree
247
-	 *
248
-	 * @return ModuleChartInterface[]
249
-	 */
250
-	public static function getActiveCharts(Tree $tree) {
251
-		return self::getActiveModulesByComponent($tree, 'chart');
252
-	}
243
+    /**
244
+     * Get a list of modules which (a) provide a chart and (b) we have permission to see.
245
+     *
246
+     * @param Tree $tree
247
+     *
248
+     * @return ModuleChartInterface[]
249
+     */
250
+    public static function getActiveCharts(Tree $tree) {
251
+        return self::getActiveModulesByComponent($tree, 'chart');
252
+    }
253 253
 
254
-	/**
255
-	 * Get a list of modules which (a) provide a chart and (b) we have permission to see.
256
-	 *
257
-	 * @param Tree   $tree
258
-	 * @param string $module
259
-	 *
260
-	 * @return bool
261
-	 */
262
-	public static function isActiveChart(Tree $tree, $module) {
263
-		return array_key_exists($module, self::getActiveModulesByComponent($tree, 'chart'));
264
-	}
254
+    /**
255
+     * Get a list of modules which (a) provide a chart and (b) we have permission to see.
256
+     *
257
+     * @param Tree   $tree
258
+     * @param string $module
259
+     *
260
+     * @return bool
261
+     */
262
+    public static function isActiveChart(Tree $tree, $module) {
263
+        return array_key_exists($module, self::getActiveModulesByComponent($tree, 'chart'));
264
+    }
265 265
 
266
-	/**
267
-	 * Get a list of modules which (a) provide a menu and (b) we have permission to see.
268
-	 *
269
-	 * @param Tree $tree
270
-	 *
271
-	 * @return ModuleMenuInterface[]
272
-	 */
273
-	public static function getActiveMenus(Tree $tree) {
274
-		return self::getActiveModulesByComponent($tree, 'menu');
275
-	}
266
+    /**
267
+     * Get a list of modules which (a) provide a menu and (b) we have permission to see.
268
+     *
269
+     * @param Tree $tree
270
+     *
271
+     * @return ModuleMenuInterface[]
272
+     */
273
+    public static function getActiveMenus(Tree $tree) {
274
+        return self::getActiveModulesByComponent($tree, 'menu');
275
+    }
276 276
 
277
-	/**
278
-	 * Get a list of modules which (a) provide a report and (b) we have permission to see.
279
-	 *
280
-	 * @param Tree $tree
281
-	 *
282
-	 * @return ModuleReportInterface[]
283
-	 */
284
-	public static function getActiveReports(Tree $tree) {
285
-		return self::getActiveModulesByComponent($tree, 'report');
286
-	}
277
+    /**
278
+     * Get a list of modules which (a) provide a report and (b) we have permission to see.
279
+     *
280
+     * @param Tree $tree
281
+     *
282
+     * @return ModuleReportInterface[]
283
+     */
284
+    public static function getActiveReports(Tree $tree) {
285
+        return self::getActiveModulesByComponent($tree, 'report');
286
+    }
287 287
 
288
-	/**
289
-	 * Get a list of modules which (a) provide a sidebar and (b) we have permission to see.
290
-	 *
291
-	 * @param Tree $tree
292
-	 *
293
-	 * @return ModuleSidebarInterface[]
294
-	 */
295
-	public static function getActiveSidebars(Tree $tree) {
296
-		return self::getActiveModulesByComponent($tree, 'sidebar');
297
-	}
288
+    /**
289
+     * Get a list of modules which (a) provide a sidebar and (b) we have permission to see.
290
+     *
291
+     * @param Tree $tree
292
+     *
293
+     * @return ModuleSidebarInterface[]
294
+     */
295
+    public static function getActiveSidebars(Tree $tree) {
296
+        return self::getActiveModulesByComponent($tree, 'sidebar');
297
+    }
298 298
 
299
-	/**
300
-	 * Get a list of modules which (a) provide a tab and (b) we have permission to see.
301
-	 *
302
-	 * @param Tree $tree
303
-	 *
304
-	 * @return ModuleTabInterface[]
305
-	 */
306
-	public static function getActiveTabs(Tree $tree) {
307
-		return self::getActiveModulesByComponent($tree, 'tab');
308
-	}
299
+    /**
300
+     * Get a list of modules which (a) provide a tab and (b) we have permission to see.
301
+     *
302
+     * @param Tree $tree
303
+     *
304
+     * @return ModuleTabInterface[]
305
+     */
306
+    public static function getActiveTabs(Tree $tree) {
307
+        return self::getActiveModulesByComponent($tree, 'tab');
308
+    }
309 309
 
310
-	/**
311
-	 * Get a list of modules which (a) provide a theme and (b) we have permission to see.
312
-	 *
313
-	 * @param Tree $tree
314
-	 *
315
-	 * @return ModuleThemeInterface[]
316
-	 */
317
-	public static function getActiveThemes(Tree $tree) {
318
-		return self::getActiveModulesByComponent($tree, 'theme');
319
-	}
310
+    /**
311
+     * Get a list of modules which (a) provide a theme and (b) we have permission to see.
312
+     *
313
+     * @param Tree $tree
314
+     *
315
+     * @return ModuleThemeInterface[]
316
+     */
317
+    public static function getActiveThemes(Tree $tree) {
318
+        return self::getActiveModulesByComponent($tree, 'theme');
319
+    }
320 320
 
321
-	/**
322
-	 * Find a specified module, if it is currently active.
323
-	 *
324
-	 * @param string $module_name
325
-	 *
326
-	 * @return AbstractModule|null
327
-	 */
328
-	public static function getModuleByName($module_name) {
329
-		$modules = self::getActiveModules();
330
-		if (array_key_exists($module_name, $modules)) {
331
-			return $modules[$module_name];
332
-		} else {
333
-			return null;
334
-		}
335
-	}
321
+    /**
322
+     * Find a specified module, if it is currently active.
323
+     *
324
+     * @param string $module_name
325
+     *
326
+     * @return AbstractModule|null
327
+     */
328
+    public static function getModuleByName($module_name) {
329
+        $modules = self::getActiveModules();
330
+        if (array_key_exists($module_name, $modules)) {
331
+            return $modules[$module_name];
332
+        } else {
333
+            return null;
334
+        }
335
+    }
336 336
 
337
-	/**
338
-	 * Scan the source code to find a list of all installed modules.
339
-	 *
340
-	 * During setup, new modules need a status of “enabled”.
341
-	 * In admin->modules, new modules need status of “disabled”.
342
-	 *
343
-	 * @param string $default_status
344
-	 *
345
-	 * @return AbstractModule[]
346
-	 */
347
-	public static function getInstalledModules($default_status) {
348
-		$modules = array();
337
+    /**
338
+     * Scan the source code to find a list of all installed modules.
339
+     *
340
+     * During setup, new modules need a status of “enabled”.
341
+     * In admin->modules, new modules need status of “disabled”.
342
+     *
343
+     * @param string $default_status
344
+     *
345
+     * @return AbstractModule[]
346
+     */
347
+    public static function getInstalledModules($default_status) {
348
+        $modules = array();
349 349
 
350
-		foreach (glob(WT_ROOT . WT_MODULES_DIR . '*/module.php') as $file) {
351
-			try {
352
-				$module = include $file;
353
-				if ($module instanceof AbstractModule) {
354
-					$modules[$module->getName()] = $module;
355
-					Database::prepare("INSERT IGNORE INTO `##module` (module_name, status, menu_order, sidebar_order, tab_order) VALUES (?, ?, ?, ?, ?)")->execute(array(
356
-						$module->getName(),
357
-						$default_status,
358
-						$module instanceof ModuleMenuInterface ? $module->defaultMenuOrder() : null,
359
-						$module instanceof ModuleSidebarInterface ? $module->defaultSidebarOrder() : null,
360
-						$module instanceof ModuleTabInterface ? $module->defaultTabOrder() : null,
361
-					));
362
-					// Set the default privcy for this module. Note that this also sets it for the
363
-					// default family tree, with a gedcom_id of -1
364
-					if ($module instanceof ModuleMenuInterface) {
365
-						Database::prepare(
366
-							"INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
367
-							" SELECT ?, gedcom_id, 'menu', ?" .
368
-							" FROM `##gedcom`"
369
-						)->execute(array($module->getName(), $module->defaultAccessLevel()));
370
-					}
371
-					if ($module instanceof ModuleSidebarInterface) {
372
-						Database::prepare(
373
-							"INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
374
-							" SELECT ?, gedcom_id, 'sidebar', ?" .
375
-							" FROM `##gedcom`"
376
-						)->execute(array($module->getName(), $module->defaultAccessLevel()));
377
-					}
378
-					if ($module instanceof ModuleTabInterface) {
379
-						Database::prepare(
380
-							"INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
381
-							" SELECT ?, gedcom_id, 'tab', ?" .
382
-							" FROM `##gedcom`"
383
-						)->execute(array($module->getName(), $module->defaultAccessLevel()));
384
-					}
385
-					if ($module instanceof ModuleBlockInterface) {
386
-						Database::prepare(
387
-							"INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
388
-							" SELECT ?, gedcom_id, 'block', ?" .
389
-							" FROM `##gedcom`"
390
-						)->execute(array($module->getName(), $module->defaultAccessLevel()));
391
-					}
392
-					if ($module instanceof ModuleChartInterface) {
393
-						Database::prepare(
394
-							"INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
395
-							" SELECT ?, gedcom_id, 'chart', ?" .
396
-							" FROM `##gedcom`"
397
-						)->execute(array($module->getName(), $module->defaultAccessLevel()));
398
-					}
399
-					if ($module instanceof ModuleReportInterface) {
400
-						Database::prepare(
401
-							"INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
402
-							" SELECT ?, gedcom_id, 'report', ?" .
403
-							" FROM `##gedcom`"
404
-						)->execute(array($module->getName(), $module->defaultAccessLevel()));
405
-					}
406
-					if ($module instanceof ModuleThemeInterface) {
407
-						Database::prepare(
408
-							"INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
409
-							" SELECT ?, gedcom_id, 'theme', ?" .
410
-							" FROM `##gedcom`"
411
-						)->execute(array($module->getName(), $module->defaultAccessLevel()));
412
-					}
413
-				}
414
-			} catch (\Exception $ex) {
415
-				// Old or invalid module?
416
-			}
417
-		}
350
+        foreach (glob(WT_ROOT . WT_MODULES_DIR . '*/module.php') as $file) {
351
+            try {
352
+                $module = include $file;
353
+                if ($module instanceof AbstractModule) {
354
+                    $modules[$module->getName()] = $module;
355
+                    Database::prepare("INSERT IGNORE INTO `##module` (module_name, status, menu_order, sidebar_order, tab_order) VALUES (?, ?, ?, ?, ?)")->execute(array(
356
+                        $module->getName(),
357
+                        $default_status,
358
+                        $module instanceof ModuleMenuInterface ? $module->defaultMenuOrder() : null,
359
+                        $module instanceof ModuleSidebarInterface ? $module->defaultSidebarOrder() : null,
360
+                        $module instanceof ModuleTabInterface ? $module->defaultTabOrder() : null,
361
+                    ));
362
+                    // Set the default privcy for this module. Note that this also sets it for the
363
+                    // default family tree, with a gedcom_id of -1
364
+                    if ($module instanceof ModuleMenuInterface) {
365
+                        Database::prepare(
366
+                            "INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
367
+                            " SELECT ?, gedcom_id, 'menu', ?" .
368
+                            " FROM `##gedcom`"
369
+                        )->execute(array($module->getName(), $module->defaultAccessLevel()));
370
+                    }
371
+                    if ($module instanceof ModuleSidebarInterface) {
372
+                        Database::prepare(
373
+                            "INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
374
+                            " SELECT ?, gedcom_id, 'sidebar', ?" .
375
+                            " FROM `##gedcom`"
376
+                        )->execute(array($module->getName(), $module->defaultAccessLevel()));
377
+                    }
378
+                    if ($module instanceof ModuleTabInterface) {
379
+                        Database::prepare(
380
+                            "INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
381
+                            " SELECT ?, gedcom_id, 'tab', ?" .
382
+                            " FROM `##gedcom`"
383
+                        )->execute(array($module->getName(), $module->defaultAccessLevel()));
384
+                    }
385
+                    if ($module instanceof ModuleBlockInterface) {
386
+                        Database::prepare(
387
+                            "INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
388
+                            " SELECT ?, gedcom_id, 'block', ?" .
389
+                            " FROM `##gedcom`"
390
+                        )->execute(array($module->getName(), $module->defaultAccessLevel()));
391
+                    }
392
+                    if ($module instanceof ModuleChartInterface) {
393
+                        Database::prepare(
394
+                            "INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
395
+                            " SELECT ?, gedcom_id, 'chart', ?" .
396
+                            " FROM `##gedcom`"
397
+                        )->execute(array($module->getName(), $module->defaultAccessLevel()));
398
+                    }
399
+                    if ($module instanceof ModuleReportInterface) {
400
+                        Database::prepare(
401
+                            "INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
402
+                            " SELECT ?, gedcom_id, 'report', ?" .
403
+                            " FROM `##gedcom`"
404
+                        )->execute(array($module->getName(), $module->defaultAccessLevel()));
405
+                    }
406
+                    if ($module instanceof ModuleThemeInterface) {
407
+                        Database::prepare(
408
+                            "INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" .
409
+                            " SELECT ?, gedcom_id, 'theme', ?" .
410
+                            " FROM `##gedcom`"
411
+                        )->execute(array($module->getName(), $module->defaultAccessLevel()));
412
+                    }
413
+                }
414
+            } catch (\Exception $ex) {
415
+                // Old or invalid module?
416
+            }
417
+        }
418 418
 
419
-		return $modules;
420
-	}
419
+        return $modules;
420
+    }
421 421
 
422
-	/**
423
-	 * After creating a new family tree, we need to assign the default access
424
-	 * rights for each module.
425
-	 *
426
-	 * @param int $tree_id
427
-	 */
428
-	public static function setDefaultAccess($tree_id) {
429
-		foreach (self::getInstalledModules('disabled') as $module) {
430
-			if ($module instanceof ModuleMenuInterface) {
431
-				Database::prepare(
432
-					"INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'menu', ?)"
433
-				)->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
434
-			}
435
-			if ($module instanceof ModuleSidebarInterface) {
436
-				Database::prepare(
437
-					"INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'sidebar', ?)"
438
-				)->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
439
-			}
440
-			if ($module instanceof ModuleTabInterface) {
441
-				Database::prepare(
442
-					"INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'tab', ?)"
443
-				)->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
444
-			}
445
-			if ($module instanceof ModuleBlockInterface) {
446
-				Database::prepare(
447
-					"INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'block', ?)"
448
-				)->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
449
-			}
450
-			if ($module instanceof ModuleChartInterface) {
451
-				Database::prepare(
452
-					"INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'chart', ?)"
453
-				)->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
454
-			}
455
-			if ($module instanceof ModuleReportInterface) {
456
-				Database::prepare(
457
-					"INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'report', ?)"
458
-				)->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
459
-			}
460
-			if ($module instanceof ModuleThemeInterface) {
461
-				Database::prepare(
462
-					"INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'theme', ?)"
463
-				)->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
464
-			}
465
-		}
466
-	}
422
+    /**
423
+     * After creating a new family tree, we need to assign the default access
424
+     * rights for each module.
425
+     *
426
+     * @param int $tree_id
427
+     */
428
+    public static function setDefaultAccess($tree_id) {
429
+        foreach (self::getInstalledModules('disabled') as $module) {
430
+            if ($module instanceof ModuleMenuInterface) {
431
+                Database::prepare(
432
+                    "INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'menu', ?)"
433
+                )->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
434
+            }
435
+            if ($module instanceof ModuleSidebarInterface) {
436
+                Database::prepare(
437
+                    "INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'sidebar', ?)"
438
+                )->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
439
+            }
440
+            if ($module instanceof ModuleTabInterface) {
441
+                Database::prepare(
442
+                    "INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'tab', ?)"
443
+                )->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
444
+            }
445
+            if ($module instanceof ModuleBlockInterface) {
446
+                Database::prepare(
447
+                    "INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'block', ?)"
448
+                )->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
449
+            }
450
+            if ($module instanceof ModuleChartInterface) {
451
+                Database::prepare(
452
+                    "INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'chart', ?)"
453
+                )->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
454
+            }
455
+            if ($module instanceof ModuleReportInterface) {
456
+                Database::prepare(
457
+                    "INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'report', ?)"
458
+                )->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
459
+            }
460
+            if ($module instanceof ModuleThemeInterface) {
461
+                Database::prepare(
462
+                    "INSERT IGNORE `##module_privacy` (module_name, gedcom_id, component, access_level) VALUES (?, ?, 'theme', ?)"
463
+                )->execute(array($module->getName(), $tree_id, $module->defaultAccessLevel()));
464
+            }
465
+        }
466
+    }
467 467
 }
Please login to merge, or discard this patch.
Braces   +32 added lines, -16 removed lines patch added patch discarded remove patch
@@ -27,14 +27,16 @@  discard block
 block discarded – undo
27 27
 /**
28 28
  * Functions for managing and maintaining modules.
29 29
  */
30
-class Module {
30
+class Module
31
+{
31 32
 	/**
32 33
 	 * Get a list of all core modules.  We need to identify
33 34
 	 * third-party during upgrade and on the module admin page.
34 35
 	 *
35 36
 	 * @return string[]
36 37
 	 */
37
-	public static function getCoreModuleNames() {
38
+	public static function getCoreModuleNames()
39
+	{
38 40
 		return array(
39 41
 			'GEDFact_assistant',
40 42
 			'ahnentafel_report',
@@ -115,7 +117,8 @@  discard block
 block discarded – undo
115 117
 	 *
116 118
 	 * @return AbstractModule[]
117 119
 	 */
118
-	private static function getActiveModules() {
120
+	private static function getActiveModules()
121
+	{
119 122
 		/** @var AbstractModule[] - Only query the database once. */
120 123
 		static $modules;
121 124
 
@@ -159,7 +162,8 @@  discard block
 block discarded – undo
159 162
 	 *
160 163
 	 * @return AbstractModule[]
161 164
 	 */
162
-	private static function getActiveModulesByComponent(Tree $tree, $component) {
165
+	private static function getActiveModulesByComponent(Tree $tree, $component)
166
+	{
163 167
 		$module_names = Database::prepare(
164 168
 			"SELECT module_name" .
165 169
 			" FROM `##module`" .
@@ -201,7 +205,8 @@  discard block
 block discarded – undo
201 205
 	 *
202 206
 	 * @return AbstractModule[]
203 207
 	 */
204
-	public static function getAllModulesByComponent($component) {
208
+	public static function getAllModulesByComponent($component)
209
+	{
205 210
 		$module_names = Database::prepare(
206 211
 			"SELECT module_name" .
207 212
 			" FROM `##module`" .
@@ -236,7 +241,8 @@  discard block
 block discarded – undo
236 241
 	 *
237 242
 	 * @return ModuleBlockInterface[]
238 243
 	 */
239
-	public static function getActiveBlocks(Tree $tree) {
244
+	public static function getActiveBlocks(Tree $tree)
245
+	{
240 246
 		return self::getActiveModulesByComponent($tree, 'block');
241 247
 	}
242 248
 
@@ -247,7 +253,8 @@  discard block
 block discarded – undo
247 253
 	 *
248 254
 	 * @return ModuleChartInterface[]
249 255
 	 */
250
-	public static function getActiveCharts(Tree $tree) {
256
+	public static function getActiveCharts(Tree $tree)
257
+	{
251 258
 		return self::getActiveModulesByComponent($tree, 'chart');
252 259
 	}
253 260
 
@@ -259,7 +266,8 @@  discard block
 block discarded – undo
259 266
 	 *
260 267
 	 * @return bool
261 268
 	 */
262
-	public static function isActiveChart(Tree $tree, $module) {
269
+	public static function isActiveChart(Tree $tree, $module)
270
+	{
263 271
 		return array_key_exists($module, self::getActiveModulesByComponent($tree, 'chart'));
264 272
 	}
265 273
 
@@ -270,7 +278,8 @@  discard block
 block discarded – undo
270 278
 	 *
271 279
 	 * @return ModuleMenuInterface[]
272 280
 	 */
273
-	public static function getActiveMenus(Tree $tree) {
281
+	public static function getActiveMenus(Tree $tree)
282
+	{
274 283
 		return self::getActiveModulesByComponent($tree, 'menu');
275 284
 	}
276 285
 
@@ -281,7 +290,8 @@  discard block
 block discarded – undo
281 290
 	 *
282 291
 	 * @return ModuleReportInterface[]
283 292
 	 */
284
-	public static function getActiveReports(Tree $tree) {
293
+	public static function getActiveReports(Tree $tree)
294
+	{
285 295
 		return self::getActiveModulesByComponent($tree, 'report');
286 296
 	}
287 297
 
@@ -292,7 +302,8 @@  discard block
 block discarded – undo
292 302
 	 *
293 303
 	 * @return ModuleSidebarInterface[]
294 304
 	 */
295
-	public static function getActiveSidebars(Tree $tree) {
305
+	public static function getActiveSidebars(Tree $tree)
306
+	{
296 307
 		return self::getActiveModulesByComponent($tree, 'sidebar');
297 308
 	}
298 309
 
@@ -303,7 +314,8 @@  discard block
 block discarded – undo
303 314
 	 *
304 315
 	 * @return ModuleTabInterface[]
305 316
 	 */
306
-	public static function getActiveTabs(Tree $tree) {
317
+	public static function getActiveTabs(Tree $tree)
318
+	{
307 319
 		return self::getActiveModulesByComponent($tree, 'tab');
308 320
 	}
309 321
 
@@ -314,7 +326,8 @@  discard block
 block discarded – undo
314 326
 	 *
315 327
 	 * @return ModuleThemeInterface[]
316 328
 	 */
317
-	public static function getActiveThemes(Tree $tree) {
329
+	public static function getActiveThemes(Tree $tree)
330
+	{
318 331
 		return self::getActiveModulesByComponent($tree, 'theme');
319 332
 	}
320 333
 
@@ -325,7 +338,8 @@  discard block
 block discarded – undo
325 338
 	 *
326 339
 	 * @return AbstractModule|null
327 340
 	 */
328
-	public static function getModuleByName($module_name) {
341
+	public static function getModuleByName($module_name)
342
+	{
329 343
 		$modules = self::getActiveModules();
330 344
 		if (array_key_exists($module_name, $modules)) {
331 345
 			return $modules[$module_name];
@@ -344,7 +358,8 @@  discard block
 block discarded – undo
344 358
 	 *
345 359
 	 * @return AbstractModule[]
346 360
 	 */
347
-	public static function getInstalledModules($default_status) {
361
+	public static function getInstalledModules($default_status)
362
+	{
348 363
 		$modules = array();
349 364
 
350 365
 		foreach (glob(WT_ROOT . WT_MODULES_DIR . '*/module.php') as $file) {
@@ -425,7 +440,8 @@  discard block
 block discarded – undo
425 440
 	 *
426 441
 	 * @param int $tree_id
427 442
 	 */
428
-	public static function setDefaultAccess($tree_id) {
443
+	public static function setDefaultAccess($tree_id)
444
+	{
429 445
 		foreach (self::getInstalledModules('disabled') as $module) {
430 446
 			if ($module instanceof ModuleMenuInterface) {
431 447
 				Database::prepare(
Please login to merge, or discard this patch.
app/Family.php 3 patches
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -343,7 +343,7 @@  discard block
 block discarded – undo
343 343
 			// Check the script used by each name, so we can match cyrillic with cyrillic, greek with greek, etc.
344 344
 			$husb_names = array();
345 345
 			if ($this->husb) {
346
-				$husb_names = array_filter($this->husb->getAllNames(), function(array $x) { return $x['type'] !== '_MARNM'; } );
346
+				$husb_names = array_filter($this->husb->getAllNames(), function (array $x) { return $x['type'] !== '_MARNM'; } );
347 347
 			}
348 348
 			// If the individual only has married names, create a dummy birth name.
349 349
 			if (empty($husb_names)) {
@@ -359,7 +359,7 @@  discard block
 block discarded – undo
359 359
 
360 360
 			$wife_names = array();
361 361
 			if ($this->wife) {
362
-				$wife_names = array_filter($this->wife->getAllNames(), function(array $x) { return $x['type'] !== '_MARNM'; } );
362
+				$wife_names = array_filter($this->wife->getAllNames(), function (array $x) { return $x['type'] !== '_MARNM'; } );
363 363
 			}
364 364
 			// If the individual only has married names, create a dummy birth name.
365 365
 			if (empty($wife_names)) {
Please login to merge, or discard this patch.
Indentation   +397 added lines, -397 removed lines patch added patch discarded remove patch
@@ -19,401 +19,401 @@
 block discarded – undo
19 19
  * A GEDCOM family (FAM) object.
20 20
  */
21 21
 class Family extends GedcomRecord {
22
-	const RECORD_TYPE = 'FAM';
23
-	const URL_PREFIX  = 'family.php?famid=';
24
-
25
-	/** @var Individual|null The husband (or first spouse for same-sex couples) */
26
-	private $husb;
27
-
28
-	/** @var Individual|null The wife (or second spouse for same-sex couples) */
29
-	private $wife;
30
-
31
-	/**
32
-	 * Create a GedcomRecord object from raw GEDCOM data.
33
-	 *
34
-	 * @param string      $xref
35
-	 * @param string      $gedcom  an empty string for new/pending records
36
-	 * @param string|null $pending null for a record with no pending edits,
37
-	 *                             empty string for records with pending deletions
38
-	 * @param Tree        $tree
39
-	 */
40
-	public function __construct($xref, $gedcom, $pending, $tree) {
41
-		parent::__construct($xref, $gedcom, $pending, $tree);
42
-
43
-		// Fetch family members
44
-		if (preg_match_all('/^1 (?:HUSB|WIFE|CHIL) @(.+)@/m', $gedcom . $pending, $match)) {
45
-			Individual::load($tree, $match[1]);
46
-		}
47
-
48
-		if (preg_match('/^1 HUSB @(.+)@/m', $gedcom . $pending, $match)) {
49
-			$this->husb = Individual::getInstance($match[1], $tree);
50
-		}
51
-		if (preg_match('/^1 WIFE @(.+)@/m', $gedcom . $pending, $match)) {
52
-			$this->wife = Individual::getInstance($match[1], $tree);
53
-		}
54
-
55
-		// Make sure husb/wife are the right way round.
56
-		if ($this->husb && $this->husb->getSex() === 'F' || $this->wife && $this->wife->getSex() === 'M') {
57
-			list($this->husb, $this->wife) = array($this->wife, $this->husb);
58
-		}
59
-	}
60
-
61
-	/**
62
-	 * Generate a private version of this record
63
-	 *
64
-	 * @param int $access_level
65
-	 *
66
-	 * @return string
67
-	 */
68
-	protected function createPrivateGedcomRecord($access_level) {
69
-		$SHOW_PRIVATE_RELATIONSHIPS = $this->tree->getPreference('SHOW_PRIVATE_RELATIONSHIPS');
70
-
71
-		$rec = '0 @' . $this->xref . '@ FAM';
72
-		// Just show the 1 CHIL/HUSB/WIFE tag, not any subtags, which may contain private data
73
-		preg_match_all('/\n1 (?:CHIL|HUSB|WIFE) @(' . WT_REGEX_XREF . ')@/', $this->gedcom, $matches, PREG_SET_ORDER);
74
-		foreach ($matches as $match) {
75
-			$rela = Individual::getInstance($match[1], $this->tree);
76
-			if ($rela && ($SHOW_PRIVATE_RELATIONSHIPS || $rela->canShow($access_level))) {
77
-				$rec .= $match[0];
78
-			}
79
-		}
80
-
81
-		return $rec;
82
-	}
83
-
84
-	/**
85
-	 * Fetch data from the database
86
-	 *
87
-	 * @param string $xref
88
-	 * @param int    $tree_id
89
-	 *
90
-	 * @return null|string
91
-	 */
92
-	protected static function fetchGedcomRecord($xref, $tree_id) {
93
-		return Database::prepare(
94
-			"SELECT f_gedcom FROM `##families` WHERE f_id = :xref AND f_file = :tree_id"
95
-		)->execute(array(
96
-			'xref'    => $xref,
97
-			'tree_id' => $tree_id,
98
-		))->fetchOne();
99
-	}
100
-
101
-	/**
102
-	 * Get the male (or first female) partner of the family
103
-	 *
104
-	 * @param $access_level int|null
105
-	 *
106
-	 * @return Individual|null
107
-	 */
108
-	public function getHusband($access_level = null) {
109
-		$SHOW_PRIVATE_RELATIONSHIPS = $this->tree->getPreference('SHOW_PRIVATE_RELATIONSHIPS');
110
-
111
-		if ($this->husb && ($SHOW_PRIVATE_RELATIONSHIPS || $this->husb->canShowName($access_level))) {
112
-			return $this->husb;
113
-		} else {
114
-			return null;
115
-		}
116
-	}
117
-
118
-	/**
119
-	 * Get the female (or second male) partner of the family
120
-	 *
121
-	 * @param $access_level int|null
122
-	 *
123
-	 * @return Individual|null
124
-	 */
125
-	public function getWife($access_level = null) {
126
-		$SHOW_PRIVATE_RELATIONSHIPS = $this->tree->getPreference('SHOW_PRIVATE_RELATIONSHIPS');
127
-
128
-		if ($this->wife && ($SHOW_PRIVATE_RELATIONSHIPS || $this->wife->canShowName($access_level))) {
129
-			return $this->wife;
130
-		} else {
131
-			return null;
132
-		}
133
-	}
134
-
135
-	/**
136
-	 * Each object type may have its own special rules, and re-implement this function.
137
-	 *
138
-	 * @param int $access_level
139
-	 *
140
-	 * @return bool
141
-	 */
142
-	protected function canShowByType($access_level) {
143
-		// Hide a family if any member is private
144
-		preg_match_all('/\n1 (?:CHIL|HUSB|WIFE) @(' . WT_REGEX_XREF . ')@/', $this->gedcom, $matches);
145
-		foreach ($matches[1] as $match) {
146
-			$person = Individual::getInstance($match, $this->tree);
147
-			if ($person && !$person->canShow($access_level)) {
148
-				return false;
149
-			}
150
-		}
151
-
152
-		return true;
153
-	}
154
-
155
-	/**
156
-	 * Can the name of this record be shown?
157
-	 *
158
-	 * @param int|null $access_level
159
-	 *
160
-	 * @return bool
161
-	 */
162
-	public function canShowName($access_level = null) {
163
-		// We can always see the name (Husband-name + Wife-name), however,
164
-		// the name will often be "private + private"
165
-		return true;
166
-	}
167
-
168
-	/**
169
-	 * Find the spouse of a person.
170
-	 *
171
-	 * @param Individual $person
172
-	 * @param int|null   $access_level
173
-	 *
174
-	 * @return Individual|null
175
-	 */
176
-	public function getSpouse(Individual $person, $access_level = null) {
177
-		if ($person === $this->wife) {
178
-			return $this->getHusband($access_level);
179
-		} else {
180
-			return $this->getWife($access_level);
181
-		}
182
-	}
183
-
184
-	/**
185
-	 * Get the (zero, one or two) spouses from this family.
186
-	 *
187
-	 * @param int|null $access_level
188
-	 *
189
-	 * @return Individual[]
190
-	 */
191
-	public function getSpouses($access_level = null) {
192
-		return array_filter(array(
193
-			$this->getHusband($access_level),
194
-			$this->getWife($access_level),
195
-		));
196
-	}
197
-
198
-	/**
199
-	 * Get a list of this family’s children.
200
-	 *
201
-	 * @param int|null $access_level
202
-	 *
203
-	 * @return Individual[]
204
-	 */
205
-	public function getChildren($access_level = null) {
206
-		if ($access_level === null) {
207
-			$access_level = Auth::accessLevel($this->tree);
208
-		}
209
-
210
-		$SHOW_PRIVATE_RELATIONSHIPS = $this->tree->getPreference('SHOW_PRIVATE_RELATIONSHIPS');
211
-
212
-		$children = array();
213
-		foreach ($this->getFacts('CHIL', false, $access_level, $SHOW_PRIVATE_RELATIONSHIPS) as $fact) {
214
-			$child = $fact->getTarget();
215
-			if ($child && ($SHOW_PRIVATE_RELATIONSHIPS || $child->canShowName($access_level))) {
216
-				$children[] = $child;
217
-			}
218
-		}
219
-
220
-		return $children;
221
-	}
222
-
223
-	/**
224
-	 * Static helper function to sort an array of families by marriage date
225
-	 *
226
-	 * @param Family $x
227
-	 * @param Family $y
228
-	 *
229
-	 * @return int
230
-	 */
231
-	public static function compareMarrDate(Family $x, Family $y) {
232
-		return Date::compare($x->getMarriageDate(), $y->getMarriageDate());
233
-	}
234
-
235
-	/**
236
-	 * Number of children - for the individual list
237
-	 *
238
-	 * @return int
239
-	 */
240
-	public function getNumberOfChildren() {
241
-		$nchi = count($this->getChildren());
242
-		foreach ($this->getFacts('NCHI') as $fact) {
243
-			$nchi = max($nchi, (int) $fact->getValue());
244
-		}
245
-
246
-		return $nchi;
247
-	}
248
-
249
-	/**
250
-	 * get the marriage event
251
-	 *
252
-	 * @return Fact
253
-	 */
254
-	public function getMarriage() {
255
-		return $this->getFirstFact('MARR');
256
-	}
257
-
258
-	/**
259
-	 * Get marriage date
260
-	 *
261
-	 * @return Date
262
-	 */
263
-	public function getMarriageDate() {
264
-		$marriage = $this->getMarriage();
265
-		if ($marriage) {
266
-			return $marriage->getDate();
267
-		} else {
268
-			return new Date('');
269
-		}
270
-	}
271
-
272
-	/**
273
-	 * Get the marriage year - displayed on lists of families
274
-	 *
275
-	 * @return int
276
-	 */
277
-	public function getMarriageYear() {
278
-		return $this->getMarriageDate()->minimumDate()->y;
279
-	}
280
-
281
-	/**
282
-	 * Get the type for this marriage
283
-	 *
284
-	 * @return string|null
285
-	 */
286
-	public function getMarriageType() {
287
-		$marriage = $this->getMarriage();
288
-		if ($marriage) {
289
-			return $marriage->getAttribute('TYPE');
290
-		} else {
291
-			return null;
292
-		}
293
-	}
294
-
295
-	/**
296
-	 * Get the marriage place
297
-	 *
298
-	 * @return Place
299
-	 */
300
-	public function getMarriagePlace() {
301
-		$marriage = $this->getMarriage();
302
-
303
-		return $marriage->getPlace();
304
-	}
305
-
306
-	/**
307
-	 * Get a list of all marriage dates - for the family lists.
308
-	 *
309
-	 * @return Date[]
310
-	 */
311
-	public function getAllMarriageDates() {
312
-		foreach (explode('|', WT_EVENTS_MARR) as $event) {
313
-			if ($array = $this->getAllEventDates($event)) {
314
-				return $array;
315
-			}
316
-		}
317
-
318
-		return array();
319
-	}
320
-
321
-	/**
322
-	 * Get a list of all marriage places - for the family lists.
323
-	 *
324
-	 * @return string[]
325
-	 */
326
-	public function getAllMarriagePlaces() {
327
-		foreach (explode('|', WT_EVENTS_MARR) as $event) {
328
-			if ($array = $this->getAllEventPlaces($event)) {
329
-				return $array;
330
-			}
331
-		}
332
-
333
-		return array();
334
-	}
335
-
336
-	/**
337
-	 * Derived classes should redefine this function, otherwise the object will have no name
338
-	 *
339
-	 * @return string[][]
340
-	 */
341
-	public function getAllNames() {
342
-		if (is_null($this->_getAllNames)) {
343
-			// Check the script used by each name, so we can match cyrillic with cyrillic, greek with greek, etc.
344
-			$husb_names = array();
345
-			if ($this->husb) {
346
-				$husb_names = array_filter($this->husb->getAllNames(), function(array $x) { return $x['type'] !== '_MARNM'; } );
347
-			}
348
-			// If the individual only has married names, create a dummy birth name.
349
-			if (empty($husb_names)) {
350
-				$husb_names[] = array(
351
-					'type' => 'BIRT',
352
-					'sort' => '@N.N.',
353
-					'full' => I18N::translateContext('Unknown given name', '…') . ' ' . I18N::translateContext('Unknown surname', '…'),
354
-				);
355
-			}
356
-			foreach ($husb_names as $n => $husb_name) {
357
-				$husb_names[$n]['script'] = I18N::textScript($husb_name['full']);
358
-			}
359
-
360
-			$wife_names = array();
361
-			if ($this->wife) {
362
-				$wife_names = array_filter($this->wife->getAllNames(), function(array $x) { return $x['type'] !== '_MARNM'; } );
363
-			}
364
-			// If the individual only has married names, create a dummy birth name.
365
-			if (empty($wife_names)) {
366
-				$wife_names[] = array(
367
-					'type' => 'BIRT',
368
-					'sort' => '@N.N.',
369
-					'full' => I18N::translateContext('Unknown given name', '…') . ' ' . I18N::translateContext('Unknown surname', '…'),
370
-				);
371
-			}
372
-			foreach ($wife_names as $n => $wife_name) {
373
-				$wife_names[$n]['script'] = I18N::textScript($wife_name['full']);
374
-			}
375
-
376
-			// Add the matched names first
377
-			foreach ($husb_names as $husb_name) {
378
-				foreach ($wife_names as $wife_name) {
379
-					if ($husb_name['script'] == $wife_name['script']) {
380
-						$this->_getAllNames[] = array(
381
-							'type' => $husb_name['type'],
382
-							'sort' => $husb_name['sort'] . ' + ' . $wife_name['sort'],
383
-							'full' => $husb_name['full'] . ' + ' . $wife_name['full'],
384
-							// No need for a fullNN entry - we do not currently store FAM names in the database
385
-						);
386
-					}
387
-				}
388
-			}
389
-
390
-			// Add the unmatched names second (there may be no matched names)
391
-			foreach ($husb_names as $husb_name) {
392
-				foreach ($wife_names as $wife_name) {
393
-					if ($husb_name['script'] != $wife_name['script']) {
394
-						$this->_getAllNames[] = array(
395
-							'type' => $husb_name['type'],
396
-							'sort' => $husb_name['sort'] . ' + ' . $wife_name['sort'],
397
-							'full' => $husb_name['full'] . ' + ' . $wife_name['full'],
398
-							// No need for a fullNN entry - we do not currently store FAM names in the database
399
-						);
400
-					}
401
-				}
402
-			}
403
-		}
404
-
405
-		return $this->_getAllNames;
406
-	}
407
-
408
-	/**
409
-	 * This function should be redefined in derived classes to show any major
410
-	 * identifying characteristics of this record.
411
-	 *
412
-	 * @return string
413
-	 */
414
-	public function formatListDetails() {
415
-		return
416
-			$this->formatFirstMajorFact(WT_EVENTS_MARR, 1) .
417
-			$this->formatFirstMajorFact(WT_EVENTS_DIV, 1);
418
-	}
22
+    const RECORD_TYPE = 'FAM';
23
+    const URL_PREFIX  = 'family.php?famid=';
24
+
25
+    /** @var Individual|null The husband (or first spouse for same-sex couples) */
26
+    private $husb;
27
+
28
+    /** @var Individual|null The wife (or second spouse for same-sex couples) */
29
+    private $wife;
30
+
31
+    /**
32
+     * Create a GedcomRecord object from raw GEDCOM data.
33
+     *
34
+     * @param string      $xref
35
+     * @param string      $gedcom  an empty string for new/pending records
36
+     * @param string|null $pending null for a record with no pending edits,
37
+     *                             empty string for records with pending deletions
38
+     * @param Tree        $tree
39
+     */
40
+    public function __construct($xref, $gedcom, $pending, $tree) {
41
+        parent::__construct($xref, $gedcom, $pending, $tree);
42
+
43
+        // Fetch family members
44
+        if (preg_match_all('/^1 (?:HUSB|WIFE|CHIL) @(.+)@/m', $gedcom . $pending, $match)) {
45
+            Individual::load($tree, $match[1]);
46
+        }
47
+
48
+        if (preg_match('/^1 HUSB @(.+)@/m', $gedcom . $pending, $match)) {
49
+            $this->husb = Individual::getInstance($match[1], $tree);
50
+        }
51
+        if (preg_match('/^1 WIFE @(.+)@/m', $gedcom . $pending, $match)) {
52
+            $this->wife = Individual::getInstance($match[1], $tree);
53
+        }
54
+
55
+        // Make sure husb/wife are the right way round.
56
+        if ($this->husb && $this->husb->getSex() === 'F' || $this->wife && $this->wife->getSex() === 'M') {
57
+            list($this->husb, $this->wife) = array($this->wife, $this->husb);
58
+        }
59
+    }
60
+
61
+    /**
62
+     * Generate a private version of this record
63
+     *
64
+     * @param int $access_level
65
+     *
66
+     * @return string
67
+     */
68
+    protected function createPrivateGedcomRecord($access_level) {
69
+        $SHOW_PRIVATE_RELATIONSHIPS = $this->tree->getPreference('SHOW_PRIVATE_RELATIONSHIPS');
70
+
71
+        $rec = '0 @' . $this->xref . '@ FAM';
72
+        // Just show the 1 CHIL/HUSB/WIFE tag, not any subtags, which may contain private data
73
+        preg_match_all('/\n1 (?:CHIL|HUSB|WIFE) @(' . WT_REGEX_XREF . ')@/', $this->gedcom, $matches, PREG_SET_ORDER);
74
+        foreach ($matches as $match) {
75
+            $rela = Individual::getInstance($match[1], $this->tree);
76
+            if ($rela && ($SHOW_PRIVATE_RELATIONSHIPS || $rela->canShow($access_level))) {
77
+                $rec .= $match[0];
78
+            }
79
+        }
80
+
81
+        return $rec;
82
+    }
83
+
84
+    /**
85
+     * Fetch data from the database
86
+     *
87
+     * @param string $xref
88
+     * @param int    $tree_id
89
+     *
90
+     * @return null|string
91
+     */
92
+    protected static function fetchGedcomRecord($xref, $tree_id) {
93
+        return Database::prepare(
94
+            "SELECT f_gedcom FROM `##families` WHERE f_id = :xref AND f_file = :tree_id"
95
+        )->execute(array(
96
+            'xref'    => $xref,
97
+            'tree_id' => $tree_id,
98
+        ))->fetchOne();
99
+    }
100
+
101
+    /**
102
+     * Get the male (or first female) partner of the family
103
+     *
104
+     * @param $access_level int|null
105
+     *
106
+     * @return Individual|null
107
+     */
108
+    public function getHusband($access_level = null) {
109
+        $SHOW_PRIVATE_RELATIONSHIPS = $this->tree->getPreference('SHOW_PRIVATE_RELATIONSHIPS');
110
+
111
+        if ($this->husb && ($SHOW_PRIVATE_RELATIONSHIPS || $this->husb->canShowName($access_level))) {
112
+            return $this->husb;
113
+        } else {
114
+            return null;
115
+        }
116
+    }
117
+
118
+    /**
119
+     * Get the female (or second male) partner of the family
120
+     *
121
+     * @param $access_level int|null
122
+     *
123
+     * @return Individual|null
124
+     */
125
+    public function getWife($access_level = null) {
126
+        $SHOW_PRIVATE_RELATIONSHIPS = $this->tree->getPreference('SHOW_PRIVATE_RELATIONSHIPS');
127
+
128
+        if ($this->wife && ($SHOW_PRIVATE_RELATIONSHIPS || $this->wife->canShowName($access_level))) {
129
+            return $this->wife;
130
+        } else {
131
+            return null;
132
+        }
133
+    }
134
+
135
+    /**
136
+     * Each object type may have its own special rules, and re-implement this function.
137
+     *
138
+     * @param int $access_level
139
+     *
140
+     * @return bool
141
+     */
142
+    protected function canShowByType($access_level) {
143
+        // Hide a family if any member is private
144
+        preg_match_all('/\n1 (?:CHIL|HUSB|WIFE) @(' . WT_REGEX_XREF . ')@/', $this->gedcom, $matches);
145
+        foreach ($matches[1] as $match) {
146
+            $person = Individual::getInstance($match, $this->tree);
147
+            if ($person && !$person->canShow($access_level)) {
148
+                return false;
149
+            }
150
+        }
151
+
152
+        return true;
153
+    }
154
+
155
+    /**
156
+     * Can the name of this record be shown?
157
+     *
158
+     * @param int|null $access_level
159
+     *
160
+     * @return bool
161
+     */
162
+    public function canShowName($access_level = null) {
163
+        // We can always see the name (Husband-name + Wife-name), however,
164
+        // the name will often be "private + private"
165
+        return true;
166
+    }
167
+
168
+    /**
169
+     * Find the spouse of a person.
170
+     *
171
+     * @param Individual $person
172
+     * @param int|null   $access_level
173
+     *
174
+     * @return Individual|null
175
+     */
176
+    public function getSpouse(Individual $person, $access_level = null) {
177
+        if ($person === $this->wife) {
178
+            return $this->getHusband($access_level);
179
+        } else {
180
+            return $this->getWife($access_level);
181
+        }
182
+    }
183
+
184
+    /**
185
+     * Get the (zero, one or two) spouses from this family.
186
+     *
187
+     * @param int|null $access_level
188
+     *
189
+     * @return Individual[]
190
+     */
191
+    public function getSpouses($access_level = null) {
192
+        return array_filter(array(
193
+            $this->getHusband($access_level),
194
+            $this->getWife($access_level),
195
+        ));
196
+    }
197
+
198
+    /**
199
+     * Get a list of this family’s children.
200
+     *
201
+     * @param int|null $access_level
202
+     *
203
+     * @return Individual[]
204
+     */
205
+    public function getChildren($access_level = null) {
206
+        if ($access_level === null) {
207
+            $access_level = Auth::accessLevel($this->tree);
208
+        }
209
+
210
+        $SHOW_PRIVATE_RELATIONSHIPS = $this->tree->getPreference('SHOW_PRIVATE_RELATIONSHIPS');
211
+
212
+        $children = array();
213
+        foreach ($this->getFacts('CHIL', false, $access_level, $SHOW_PRIVATE_RELATIONSHIPS) as $fact) {
214
+            $child = $fact->getTarget();
215
+            if ($child && ($SHOW_PRIVATE_RELATIONSHIPS || $child->canShowName($access_level))) {
216
+                $children[] = $child;
217
+            }
218
+        }
219
+
220
+        return $children;
221
+    }
222
+
223
+    /**
224
+     * Static helper function to sort an array of families by marriage date
225
+     *
226
+     * @param Family $x
227
+     * @param Family $y
228
+     *
229
+     * @return int
230
+     */
231
+    public static function compareMarrDate(Family $x, Family $y) {
232
+        return Date::compare($x->getMarriageDate(), $y->getMarriageDate());
233
+    }
234
+
235
+    /**
236
+     * Number of children - for the individual list
237
+     *
238
+     * @return int
239
+     */
240
+    public function getNumberOfChildren() {
241
+        $nchi = count($this->getChildren());
242
+        foreach ($this->getFacts('NCHI') as $fact) {
243
+            $nchi = max($nchi, (int) $fact->getValue());
244
+        }
245
+
246
+        return $nchi;
247
+    }
248
+
249
+    /**
250
+     * get the marriage event
251
+     *
252
+     * @return Fact
253
+     */
254
+    public function getMarriage() {
255
+        return $this->getFirstFact('MARR');
256
+    }
257
+
258
+    /**
259
+     * Get marriage date
260
+     *
261
+     * @return Date
262
+     */
263
+    public function getMarriageDate() {
264
+        $marriage = $this->getMarriage();
265
+        if ($marriage) {
266
+            return $marriage->getDate();
267
+        } else {
268
+            return new Date('');
269
+        }
270
+    }
271
+
272
+    /**
273
+     * Get the marriage year - displayed on lists of families
274
+     *
275
+     * @return int
276
+     */
277
+    public function getMarriageYear() {
278
+        return $this->getMarriageDate()->minimumDate()->y;
279
+    }
280
+
281
+    /**
282
+     * Get the type for this marriage
283
+     *
284
+     * @return string|null
285
+     */
286
+    public function getMarriageType() {
287
+        $marriage = $this->getMarriage();
288
+        if ($marriage) {
289
+            return $marriage->getAttribute('TYPE');
290
+        } else {
291
+            return null;
292
+        }
293
+    }
294
+
295
+    /**
296
+     * Get the marriage place
297
+     *
298
+     * @return Place
299
+     */
300
+    public function getMarriagePlace() {
301
+        $marriage = $this->getMarriage();
302
+
303
+        return $marriage->getPlace();
304
+    }
305
+
306
+    /**
307
+     * Get a list of all marriage dates - for the family lists.
308
+     *
309
+     * @return Date[]
310
+     */
311
+    public function getAllMarriageDates() {
312
+        foreach (explode('|', WT_EVENTS_MARR) as $event) {
313
+            if ($array = $this->getAllEventDates($event)) {
314
+                return $array;
315
+            }
316
+        }
317
+
318
+        return array();
319
+    }
320
+
321
+    /**
322
+     * Get a list of all marriage places - for the family lists.
323
+     *
324
+     * @return string[]
325
+     */
326
+    public function getAllMarriagePlaces() {
327
+        foreach (explode('|', WT_EVENTS_MARR) as $event) {
328
+            if ($array = $this->getAllEventPlaces($event)) {
329
+                return $array;
330
+            }
331
+        }
332
+
333
+        return array();
334
+    }
335
+
336
+    /**
337
+     * Derived classes should redefine this function, otherwise the object will have no name
338
+     *
339
+     * @return string[][]
340
+     */
341
+    public function getAllNames() {
342
+        if (is_null($this->_getAllNames)) {
343
+            // Check the script used by each name, so we can match cyrillic with cyrillic, greek with greek, etc.
344
+            $husb_names = array();
345
+            if ($this->husb) {
346
+                $husb_names = array_filter($this->husb->getAllNames(), function(array $x) { return $x['type'] !== '_MARNM'; } );
347
+            }
348
+            // If the individual only has married names, create a dummy birth name.
349
+            if (empty($husb_names)) {
350
+                $husb_names[] = array(
351
+                    'type' => 'BIRT',
352
+                    'sort' => '@N.N.',
353
+                    'full' => I18N::translateContext('Unknown given name', '…') . ' ' . I18N::translateContext('Unknown surname', '…'),
354
+                );
355
+            }
356
+            foreach ($husb_names as $n => $husb_name) {
357
+                $husb_names[$n]['script'] = I18N::textScript($husb_name['full']);
358
+            }
359
+
360
+            $wife_names = array();
361
+            if ($this->wife) {
362
+                $wife_names = array_filter($this->wife->getAllNames(), function(array $x) { return $x['type'] !== '_MARNM'; } );
363
+            }
364
+            // If the individual only has married names, create a dummy birth name.
365
+            if (empty($wife_names)) {
366
+                $wife_names[] = array(
367
+                    'type' => 'BIRT',
368
+                    'sort' => '@N.N.',
369
+                    'full' => I18N::translateContext('Unknown given name', '…') . ' ' . I18N::translateContext('Unknown surname', '…'),
370
+                );
371
+            }
372
+            foreach ($wife_names as $n => $wife_name) {
373
+                $wife_names[$n]['script'] = I18N::textScript($wife_name['full']);
374
+            }
375
+
376
+            // Add the matched names first
377
+            foreach ($husb_names as $husb_name) {
378
+                foreach ($wife_names as $wife_name) {
379
+                    if ($husb_name['script'] == $wife_name['script']) {
380
+                        $this->_getAllNames[] = array(
381
+                            'type' => $husb_name['type'],
382
+                            'sort' => $husb_name['sort'] . ' + ' . $wife_name['sort'],
383
+                            'full' => $husb_name['full'] . ' + ' . $wife_name['full'],
384
+                            // No need for a fullNN entry - we do not currently store FAM names in the database
385
+                        );
386
+                    }
387
+                }
388
+            }
389
+
390
+            // Add the unmatched names second (there may be no matched names)
391
+            foreach ($husb_names as $husb_name) {
392
+                foreach ($wife_names as $wife_name) {
393
+                    if ($husb_name['script'] != $wife_name['script']) {
394
+                        $this->_getAllNames[] = array(
395
+                            'type' => $husb_name['type'],
396
+                            'sort' => $husb_name['sort'] . ' + ' . $wife_name['sort'],
397
+                            'full' => $husb_name['full'] . ' + ' . $wife_name['full'],
398
+                            // No need for a fullNN entry - we do not currently store FAM names in the database
399
+                        );
400
+                    }
401
+                }
402
+            }
403
+        }
404
+
405
+        return $this->_getAllNames;
406
+    }
407
+
408
+    /**
409
+     * This function should be redefined in derived classes to show any major
410
+     * identifying characteristics of this record.
411
+     *
412
+     * @return string
413
+     */
414
+    public function formatListDetails() {
415
+        return
416
+            $this->formatFirstMajorFact(WT_EVENTS_MARR, 1) .
417
+            $this->formatFirstMajorFact(WT_EVENTS_DIV, 1);
418
+    }
419 419
 }
Please login to merge, or discard this patch.
Braces   +44 added lines, -22 removed lines patch added patch discarded remove patch
@@ -18,7 +18,8 @@  discard block
 block discarded – undo
18 18
 /**
19 19
  * A GEDCOM family (FAM) object.
20 20
  */
21
-class Family extends GedcomRecord {
21
+class Family extends GedcomRecord
22
+{
22 23
 	const RECORD_TYPE = 'FAM';
23 24
 	const URL_PREFIX  = 'family.php?famid=';
24 25
 
@@ -37,7 +38,8 @@  discard block
 block discarded – undo
37 38
 	 *                             empty string for records with pending deletions
38 39
 	 * @param Tree        $tree
39 40
 	 */
40
-	public function __construct($xref, $gedcom, $pending, $tree) {
41
+	public function __construct($xref, $gedcom, $pending, $tree)
42
+	{
41 43
 		parent::__construct($xref, $gedcom, $pending, $tree);
42 44
 
43 45
 		// Fetch family members
@@ -65,7 +67,8 @@  discard block
 block discarded – undo
65 67
 	 *
66 68
 	 * @return string
67 69
 	 */
68
-	protected function createPrivateGedcomRecord($access_level) {
70
+	protected function createPrivateGedcomRecord($access_level)
71
+	{
69 72
 		$SHOW_PRIVATE_RELATIONSHIPS = $this->tree->getPreference('SHOW_PRIVATE_RELATIONSHIPS');
70 73
 
71 74
 		$rec = '0 @' . $this->xref . '@ FAM';
@@ -89,7 +92,8 @@  discard block
 block discarded – undo
89 92
 	 *
90 93
 	 * @return null|string
91 94
 	 */
92
-	protected static function fetchGedcomRecord($xref, $tree_id) {
95
+	protected static function fetchGedcomRecord($xref, $tree_id)
96
+	{
93 97
 		return Database::prepare(
94 98
 			"SELECT f_gedcom FROM `##families` WHERE f_id = :xref AND f_file = :tree_id"
95 99
 		)->execute(array(
@@ -105,7 +109,8 @@  discard block
 block discarded – undo
105 109
 	 *
106 110
 	 * @return Individual|null
107 111
 	 */
108
-	public function getHusband($access_level = null) {
112
+	public function getHusband($access_level = null)
113
+	{
109 114
 		$SHOW_PRIVATE_RELATIONSHIPS = $this->tree->getPreference('SHOW_PRIVATE_RELATIONSHIPS');
110 115
 
111 116
 		if ($this->husb && ($SHOW_PRIVATE_RELATIONSHIPS || $this->husb->canShowName($access_level))) {
@@ -122,7 +127,8 @@  discard block
 block discarded – undo
122 127
 	 *
123 128
 	 * @return Individual|null
124 129
 	 */
125
-	public function getWife($access_level = null) {
130
+	public function getWife($access_level = null)
131
+	{
126 132
 		$SHOW_PRIVATE_RELATIONSHIPS = $this->tree->getPreference('SHOW_PRIVATE_RELATIONSHIPS');
127 133
 
128 134
 		if ($this->wife && ($SHOW_PRIVATE_RELATIONSHIPS || $this->wife->canShowName($access_level))) {
@@ -139,7 +145,8 @@  discard block
 block discarded – undo
139 145
 	 *
140 146
 	 * @return bool
141 147
 	 */
142
-	protected function canShowByType($access_level) {
148
+	protected function canShowByType($access_level)
149
+	{
143 150
 		// Hide a family if any member is private
144 151
 		preg_match_all('/\n1 (?:CHIL|HUSB|WIFE) @(' . WT_REGEX_XREF . ')@/', $this->gedcom, $matches);
145 152
 		foreach ($matches[1] as $match) {
@@ -159,7 +166,8 @@  discard block
 block discarded – undo
159 166
 	 *
160 167
 	 * @return bool
161 168
 	 */
162
-	public function canShowName($access_level = null) {
169
+	public function canShowName($access_level = null)
170
+	{
163 171
 		// We can always see the name (Husband-name + Wife-name), however,
164 172
 		// the name will often be "private + private"
165 173
 		return true;
@@ -173,7 +181,8 @@  discard block
 block discarded – undo
173 181
 	 *
174 182
 	 * @return Individual|null
175 183
 	 */
176
-	public function getSpouse(Individual $person, $access_level = null) {
184
+	public function getSpouse(Individual $person, $access_level = null)
185
+	{
177 186
 		if ($person === $this->wife) {
178 187
 			return $this->getHusband($access_level);
179 188
 		} else {
@@ -188,7 +197,8 @@  discard block
 block discarded – undo
188 197
 	 *
189 198
 	 * @return Individual[]
190 199
 	 */
191
-	public function getSpouses($access_level = null) {
200
+	public function getSpouses($access_level = null)
201
+	{
192 202
 		return array_filter(array(
193 203
 			$this->getHusband($access_level),
194 204
 			$this->getWife($access_level),
@@ -202,7 +212,8 @@  discard block
 block discarded – undo
202 212
 	 *
203 213
 	 * @return Individual[]
204 214
 	 */
205
-	public function getChildren($access_level = null) {
215
+	public function getChildren($access_level = null)
216
+	{
206 217
 		if ($access_level === null) {
207 218
 			$access_level = Auth::accessLevel($this->tree);
208 219
 		}
@@ -228,7 +239,8 @@  discard block
 block discarded – undo
228 239
 	 *
229 240
 	 * @return int
230 241
 	 */
231
-	public static function compareMarrDate(Family $x, Family $y) {
242
+	public static function compareMarrDate(Family $x, Family $y)
243
+	{
232 244
 		return Date::compare($x->getMarriageDate(), $y->getMarriageDate());
233 245
 	}
234 246
 
@@ -237,7 +249,8 @@  discard block
 block discarded – undo
237 249
 	 *
238 250
 	 * @return int
239 251
 	 */
240
-	public function getNumberOfChildren() {
252
+	public function getNumberOfChildren()
253
+	{
241 254
 		$nchi = count($this->getChildren());
242 255
 		foreach ($this->getFacts('NCHI') as $fact) {
243 256
 			$nchi = max($nchi, (int) $fact->getValue());
@@ -251,7 +264,8 @@  discard block
 block discarded – undo
251 264
 	 *
252 265
 	 * @return Fact
253 266
 	 */
254
-	public function getMarriage() {
267
+	public function getMarriage()
268
+	{
255 269
 		return $this->getFirstFact('MARR');
256 270
 	}
257 271
 
@@ -260,7 +274,8 @@  discard block
 block discarded – undo
260 274
 	 *
261 275
 	 * @return Date
262 276
 	 */
263
-	public function getMarriageDate() {
277
+	public function getMarriageDate()
278
+	{
264 279
 		$marriage = $this->getMarriage();
265 280
 		if ($marriage) {
266 281
 			return $marriage->getDate();
@@ -274,7 +289,8 @@  discard block
 block discarded – undo
274 289
 	 *
275 290
 	 * @return int
276 291
 	 */
277
-	public function getMarriageYear() {
292
+	public function getMarriageYear()
293
+	{
278 294
 		return $this->getMarriageDate()->minimumDate()->y;
279 295
 	}
280 296
 
@@ -283,7 +299,8 @@  discard block
 block discarded – undo
283 299
 	 *
284 300
 	 * @return string|null
285 301
 	 */
286
-	public function getMarriageType() {
302
+	public function getMarriageType()
303
+	{
287 304
 		$marriage = $this->getMarriage();
288 305
 		if ($marriage) {
289 306
 			return $marriage->getAttribute('TYPE');
@@ -297,7 +314,8 @@  discard block
 block discarded – undo
297 314
 	 *
298 315
 	 * @return Place
299 316
 	 */
300
-	public function getMarriagePlace() {
317
+	public function getMarriagePlace()
318
+	{
301 319
 		$marriage = $this->getMarriage();
302 320
 
303 321
 		return $marriage->getPlace();
@@ -308,7 +326,8 @@  discard block
 block discarded – undo
308 326
 	 *
309 327
 	 * @return Date[]
310 328
 	 */
311
-	public function getAllMarriageDates() {
329
+	public function getAllMarriageDates()
330
+	{
312 331
 		foreach (explode('|', WT_EVENTS_MARR) as $event) {
313 332
 			if ($array = $this->getAllEventDates($event)) {
314 333
 				return $array;
@@ -323,7 +342,8 @@  discard block
 block discarded – undo
323 342
 	 *
324 343
 	 * @return string[]
325 344
 	 */
326
-	public function getAllMarriagePlaces() {
345
+	public function getAllMarriagePlaces()
346
+	{
327 347
 		foreach (explode('|', WT_EVENTS_MARR) as $event) {
328 348
 			if ($array = $this->getAllEventPlaces($event)) {
329 349
 				return $array;
@@ -338,7 +358,8 @@  discard block
 block discarded – undo
338 358
 	 *
339 359
 	 * @return string[][]
340 360
 	 */
341
-	public function getAllNames() {
361
+	public function getAllNames()
362
+	{
342 363
 		if (is_null($this->_getAllNames)) {
343 364
 			// Check the script used by each name, so we can match cyrillic with cyrillic, greek with greek, etc.
344 365
 			$husb_names = array();
@@ -411,7 +432,8 @@  discard block
 block discarded – undo
411 432
 	 *
412 433
 	 * @return string
413 434
 	 */
414
-	public function formatListDetails() {
435
+	public function formatListDetails()
436
+	{
415 437
 		return
416 438
 			$this->formatFirstMajorFact(WT_EVENTS_MARR, 1) .
417 439
 			$this->formatFirstMajorFact(WT_EVENTS_DIV, 1);
Please login to merge, or discard this patch.