/**
  * Create the chart controller
  *
  * @param int $show_full needed for use by charts module
  */
 public function __construct($show_full = 1)
 {
     global $WT_TREE;
     parent::__construct();
     $rootid = Filter::get('rootid', WT_REGEX_XREF);
     $this->root = Individual::getInstance($rootid, $WT_TREE);
     if (!$this->root) {
         // Missing root individual? Show the chart for someone.
         $this->root = $this->getSignificantIndividual();
     }
     if (!$this->root || !$this->root->canShowName()) {
         http_response_code(404);
         $this->error_message = I18N::translate('This individual does not exist or you do not have permission to view it.');
     }
     // Extract parameter from form
     if ($show_full) {
         $this->show_full = Filter::getInteger('show_full', 0, 1, $WT_TREE->getPreference('PEDIGREE_FULL_DETAILS'));
     } else {
         $this->show_full = 0;
     }
     $this->box = new \stdClass();
     if ($this->showFull()) {
         $this->box->width = Theme::theme()->parameter('chart-box-x');
         $this->box->height = Theme::theme()->parameter('chart-box-y');
     } else {
         $this->box->width = Theme::theme()->parameter('compact-chart-box-x');
         $this->box->height = Theme::theme()->parameter('compact-chart-box-y');
     }
 }
Exemple #2
0
 /**
  * Extend \Fisharebest\Webtrees\Individual getInstance, in order to retrieve directly a  object 
  * 
  * @param string $xref
  * @param Tree $tree
  * @param null|string $gedcom
  * @return null|Individual
  */
 public static function getIntance($xref, Tree $tree, $gedcom = null)
 {
     $indi = \Fisharebest\Webtrees\Individual::getInstance($xref, $tree, $gedcom);
     if ($indi) {
         return new Individual($indi);
     }
     return null;
 }
 /**
  * Generate the HTML content of this block.
  *
  * @param int      $block_id
  * @param bool     $template
  * @param string[] $cfg
  *
  * @return string
  */
 public function getBlock($block_id, $template = true, $cfg = array())
 {
     global $WT_TREE;
     $id = $this->getName() . $block_id;
     $class = $this->getName() . '_block';
     $title = $this->getTitle();
     $anonymous = 0;
     $logged_in = array();
     $content = '';
     foreach (User::allLoggedIn() as $user) {
         if (Auth::isAdmin() || $user->getPreference('visibleonline')) {
             $logged_in[] = $user;
         } else {
             $anonymous++;
         }
     }
     $count_logged_in = count($logged_in);
     $content .= '<div class="logged_in_count">';
     if ($anonymous) {
         $content .= I18N::plural('%s anonymous signed-in user', '%s anonymous signed-in users', $anonymous, I18N::number($anonymous));
         if ($count_logged_in) {
             $content .= '&nbsp;|&nbsp;';
         }
     }
     if ($count_logged_in) {
         $content .= I18N::plural('%s signed-in user', '%s signed-in users', $count_logged_in, I18N::number($count_logged_in));
     }
     $content .= '</div>';
     $content .= '<div class="logged_in_list">';
     if (Auth::check()) {
         foreach ($logged_in as $user) {
             $individual = Individual::getInstance($WT_TREE->getUserPreference($user, 'gedcomid'), $WT_TREE);
             $content .= '<div class="logged_in_name">';
             if ($individual) {
                 $content .= '<a href="' . $individual->getHtmlUrl() . '">' . $user->getRealNameHtml() . '</a>';
             } else {
                 $content .= $user->getRealNameHtml();
             }
             $content .= ' - ' . Filter::escapeHtml($user->getUserName());
             if (Auth::id() != $user->getUserId() && $user->getPreference('contactmethod') != 'none') {
                 $content .= ' <a class="icon-email" href="#" onclick="return message(\'' . Filter::escapeHtml($user->getUserName()) . '\', \'\', \'' . Filter::escapeHtml(Functions::getQueryUrl()) . '\');" title="' . I18N::translate('Send a message') . '"></a>';
             }
             $content .= '</div>';
         }
     }
     $content .= '</div>';
     if ($anonymous === 0 && $count_logged_in === 0) {
         return '';
     }
     if ($template) {
         return Theme::theme()->formatBlock($id, $title, $class, $content);
     } else {
         return $content;
     }
 }
 /**
  * Compute all Sosa ancestors from the user's root individual.
  * @return bool Result of the computation
  */
 public function computeAll()
 {
     $root_id = $this->tree->getUserPreference($this->user, 'MAJ_SOSA_ROOT_ID');
     $indi = Individual::getInstance($root_id, $this->tree);
     if ($indi) {
         $this->sosa_provider->deleteAll();
         $this->addNode($indi, 1);
         $this->flushTmpSosaTable(true);
         return true;
     }
     return false;
 }
 /**
  * Startup activity
  */
 public function __construct()
 {
     global $WT_TREE;
     $xref = Filter::get('pid', WT_REGEX_XREF);
     $this->record = Individual::getInstance($xref, $WT_TREE);
     if (!$this->record && $WT_TREE->getPreference('USE_RIN')) {
         $rin = FunctionsDb::findRin($xref);
         $this->record = Individual::getInstance($rin, $WT_TREE);
     }
     parent::__construct();
     // If we can display the details, add them to the page header
     if ($this->record && $this->record->canShow()) {
         $this->setPageTitle($this->record->getFullName() . ' ' . $this->record->getLifespan());
         $this->tabs = Module::getActiveTabs($this->record->getTree());
     }
 }
 /**
  * Startup activity
  */
 public function __construct()
 {
     global $WT_TREE;
     parent::__construct();
     $this->setPageTitle(I18N::translate('Lifespans'));
     $this->facts = explode('|', WT_EVENTS_BIRT . '|' . WT_EVENTS_DEAT . '|' . WT_EVENTS_MARR . '|' . WT_EVENTS_DIV);
     $tmp = explode('\\', get_class(I18N::defaultCalendar()));
     $cal = strtolower(array_pop($tmp));
     $this->defaultCalendar = str_replace('calendar', '', $cal);
     $filterPids = false;
     // Request parameters
     $clear = Filter::getBool('clear');
     $newpid = Filter::get('newpid', WT_REGEX_XREF);
     $addfam = Filter::getBool('addFamily');
     $this->place = Filter::get('place');
     $this->beginYear = Filter::getInteger('beginYear', 0, PHP_INT_MAX, null);
     $this->endYear = Filter::getInteger('endYear', 0, PHP_INT_MAX, null);
     $this->calendar = Filter::get('calendar', null, $this->defaultCalendar);
     $this->strictDate = Filter::getBool('strictDate');
     // Set up base color parameters
     $this->colors['M'] = new ColorGenerator(240, self::SATURATION, self::LIGHTNESS, self::ALPHA, self::RANGE * -1);
     $this->colors['F'] = new ColorGenerator(00, self::SATURATION, self::LIGHTNESS, self::ALPHA, self::RANGE);
     // Build a list of people based on the input parameters
     if ($clear) {
         // Empty list & reset form
         $xrefs = array();
         $this->place = null;
         $this->beginYear = null;
         $this->endYear = null;
         $this->calendar = $this->defaultCalendar;
     } elseif ($this->place) {
         // Get all individual & family records found for a place
         $this->place_obj = new Place($this->place, $WT_TREE);
         $xrefs = Database::prepare("SELECT DISTINCT `i_id` FROM `##placelinks`" . " JOIN `##individuals` ON `pl_gid`=`i_id` AND `pl_file`=`i_file`" . " WHERE `i_file`=:tree_id" . " AND `pl_p_id`=:place_id" . " UNION" . " SELECT DISTINCT `f_id` FROM `##placelinks`" . " JOIN `##families` ON `pl_gid`=`f_id` AND `pl_file`=`f_file`" . " WHERE `f_file`=:tree_id" . " AND `pl_p_id`=:place_id")->execute(array('tree_id' => $WT_TREE->getTreeId(), 'place_id' => $this->place_obj->getPlaceId()))->fetchOneColumn();
     } else {
         // Modify an existing list of records
         $xrefs = Session::get(self::SESSION_DATA, array());
         if ($newpid) {
             $xrefs = array_merge($xrefs, $this->addFamily(Individual::getInstance($newpid, $WT_TREE), $addfam));
             $xrefs = array_unique($xrefs);
         } elseif (!$xrefs) {
             $xrefs = $this->addFamily($this->getSignificantIndividual(), false);
         }
     }
     $tmp = $this->getCalendarDate(unixtojd());
     $this->currentYear = $tmp->today()->y;
     $tmp = strtoupper(strtr($this->calendar, array('jewish' => 'hebrew', 'french' => 'french r')));
     $this->calendarEscape = sprintf('@#D%s@', $tmp);
     if ($xrefs) {
         // ensure date ranges are valid in preparation for filtering list
         if ($this->beginYear || $this->endYear) {
             $filterPids = true;
             if (!$this->beginYear) {
                 $tmp = new Date($this->calendarEscape . ' 1');
                 $this->beginYear = $tmp->minimumDate()->y;
             }
             if (!$this->endYear) {
                 $this->endYear = $this->currentYear;
             }
             $this->startDate = new Date($this->calendarEscape . $this->beginYear);
             $this->endDate = new Date($this->calendarEscape . $this->endYear);
         }
         // Test each xref to see if the search criteria are met
         foreach ($xrefs as $key => $xref) {
             $valid = false;
             $person = Individual::getInstance($xref, $WT_TREE);
             if ($person) {
                 if ($person->canShow()) {
                     foreach ($person->getFacts() as $fact) {
                         if ($this->checkFact($fact)) {
                             $this->people[] = $person;
                             $valid = true;
                             break;
                         }
                     }
                 }
             } else {
                 $family = Family::getInstance($xref, $WT_TREE);
                 if ($family && $family->canShow() && $this->checkFact($family->getMarriage())) {
                     $valid = true;
                     $this->people[] = $family->getHusband();
                     $this->people[] = $family->getWife();
                 }
             }
             if (!$valid) {
                 unset($xrefs[$key]);
                 // no point in storing a xref if we can't use it
             }
         }
         Session::put(self::SESSION_DATA, $xrefs);
     } else {
         Session::forget(self::SESSION_DATA);
     }
     $this->people = array_filter(array_unique($this->people));
     $count = count($this->people);
     if ($count) {
         // Build the subtitle
         if ($this->place && $filterPids) {
             $this->subtitle = I18N::plural('%s individual with events in %s between %s and %s', '%s individuals with events in %s between %s and %s', $count, I18N::number($count), $this->place, $this->startDate->display(false, '%Y'), $this->endDate->display(false, '%Y'));
         } elseif ($this->place) {
             $this->subtitle = I18N::plural('%s individual with events in %s', '%s individuals with events in %s', $count, I18N::number($count), $this->place);
         } elseif ($filterPids) {
             $this->subtitle = I18N::plural('%s individual with events between %s and %s', '%s individuals with events between %s and %s', $count, I18N::number($count), $this->startDate->display(false, '%Y'), $this->endDate->display(false, '%Y'));
         } else {
             $this->subtitle = I18N::plural('%s individual', '%s individuals', $count, I18N::number($count));
         }
         // Sort the array in order of birth year
         usort($this->people, function (Individual $a, Individual $b) {
             return Date::compare($a->getEstimatedBirthDate(), $b->getEstimatedBirthDate());
         });
         //Find the mimimum birth year and maximum death year from the individuals in the array.
         $bdate = $this->getCalendarDate($this->people[0]->getEstimatedBirthDate()->minimumJulianDay());
         $minyear = $bdate->y;
         $that = $this;
         // PHP5.3 cannot access $this inside a closure
         $maxyear = array_reduce($this->people, function ($carry, Individual $item) use($that) {
             $date = $that->getCalendarDate($item->getEstimatedDeathDate()->maximumJulianDay());
             return max($carry, $date->y);
         }, 0);
     } elseif ($filterPids) {
         $minyear = $this->endYear;
         $maxyear = $this->endYear;
     } else {
         $minyear = $this->currentYear;
         $maxyear = $this->currentYear;
     }
     $maxyear = min($maxyear, $this->currentYear);
     // Limit maximum year to current year as we can't forecast the future
     $minyear = min($minyear, $maxyear - $WT_TREE->getPreference('MAX_ALIVE_AGE'));
     // Set default minimum chart length
     $this->timelineMinYear = (int) floor($minyear / 10) * 10;
     // round down to start of the decade
     $this->timelineMaxYear = (int) ceil($maxyear / 10) * 10;
     // round up to start of next decade
 }
Exemple #7
0
         http_response_code(406);
     }
     break;
 case 'masquerade':
     $user = User::find(Filter::postInteger('user_id'));
     if ($user && Auth::isAdmin() && Auth::user() !== $user) {
         Log::addAuthenticationLog('Masquerade as user: '******'unlink-media':
     // Remove links from an individual and their spouse-family records to a media object.
     // Used by the "unlink" option on the album (lightbox) tab.
     $source = Individual::getInstance(Filter::post('source', WT_REGEX_XREF), $WT_TREE);
     $target = Filter::post('target', WT_REGEX_XREF);
     if ($source && $source->canShow() && $source->canEdit() && $target) {
         // Consider the individual and their spouse-family records
         $sources = $source->getSpouseFamilies();
         $sources[] = $source;
         foreach ($sources as $source) {
             foreach ($source->getFacts() as $fact) {
                 if (!$fact->isPendingDeletion()) {
                     if ($fact->getValue() == '@' . $target . '@') {
                         // Level 1 links
                         $source->deleteFact($fact->getFactId(), true);
                     } elseif (strpos($fact->getGedcom(), ' @' . $target . '@')) {
                         // Level 2-3 links
                         $source->updateFact($fact->getFactId(), preg_replace(array('/\\n2 OBJE @' . $target . '@(\\n[3-9].*)*/', '/\\n3 OBJE @' . $target . '@(\\n[4-9].*)*/'), '', $fact->getGedcom()), true);
                     }
Exemple #8
0
 /**
  * Print fact DATE/TIME
  *
  * @param Fact $event event containing the date/age
  * @param GedcomRecord $record the person (or couple) whose ages should be printed
  * @param bool $anchor option to print a link to calendar
  * @param bool $time option to print TIME value
  *
  * @return string
  */
 public static function formatFactDate(Fact $event, GedcomRecord $record, $anchor, $time)
 {
     global $pid;
     $factrec = $event->getGedcom();
     $html = '';
     // Recorded age
     if (preg_match('/\\n2 AGE (.+)/', $factrec, $match)) {
         $fact_age = $match[1];
     } else {
         $fact_age = '';
     }
     if (preg_match('/\\n2 HUSB\\n3 AGE (.+)/', $factrec, $match)) {
         $husb_age = $match[1];
     } else {
         $husb_age = '';
     }
     if (preg_match('/\\n2 WIFE\\n3 AGE (.+)/', $factrec, $match)) {
         $wife_age = $match[1];
     } else {
         $wife_age = '';
     }
     // Calculated age
     if (preg_match('/\\n2 DATE (.+)/', $factrec, $match)) {
         $date = new Date($match[1]);
         $html .= ' ' . $date->display($anchor);
         // time
         if ($time && preg_match('/\\n3 TIME (.+)/', $factrec, $match)) {
             $html .= ' – <span class="date">' . $match[1] . '</span>';
         }
         $fact = $event->getTag();
         if ($record instanceof Individual) {
             if ($fact === 'BIRT' && $record->getTree()->getPreference('SHOW_PARENTS_AGE')) {
                 // age of parents at child birth
                 $html .= self::formatParentsAges($record, $date);
             } elseif ($fact !== 'CHAN' && $fact !== '_TODO') {
                 // age at event
                 $birth_date = $record->getBirthDate();
                 // Can't use getDeathDate(), as this also gives BURI/CREM events, which
                 // wouldn't give the correct "days after death" result for people with
                 // no DEAT.
                 $death_event = $record->getFirstFact('DEAT');
                 if ($death_event) {
                     $death_date = $death_event->getDate();
                 } else {
                     $death_date = new Date('');
                 }
                 $ageText = '';
                 if (Date::compare($date, $death_date) <= 0 || !$record->isDead() || $fact == 'DEAT') {
                     // Before death, print age
                     $age = Date::getAgeGedcom($birth_date, $date);
                     // Only show calculated age if it differs from recorded age
                     if ($age != '') {
                         if ($fact_age != '' && $fact_age != $age || $fact_age == '' && $husb_age == '' && $wife_age == '' || $husb_age != '' && $record->getSex() == 'M' && $husb_age != $age || $wife_age != '' && $record->getSex() == 'F' && $wife_age != $age) {
                             if ($age != "0d") {
                                 $ageText = '(' . I18N::translate('Age') . ' ' . FunctionsDate::getAgeAtEvent($age, false) . ')';
                             }
                         }
                     }
                 }
                 if ($fact != 'DEAT' && Date::compare($date, $death_date) >= 0) {
                     // After death, print time since death
                     $age = FunctionsDate::getAgeAtEvent(Date::getAgeGedcom($death_date, $date), true);
                     if ($age != '') {
                         if (Date::getAgeGedcom($death_date, $date) == "0d") {
                             $ageText = '(' . I18N::translate('on the date of death') . ')';
                         } else {
                             $ageText = '(' . $age . ' ' . I18N::translate('after death') . ')';
                             // Family events which occur after death are probably errors
                             if ($event->getParent() instanceof Family) {
                                 $ageText .= '<i class="icon-warning"></i>';
                             }
                         }
                     }
                 }
                 if ($ageText) {
                     $html .= ' <span class="age">' . $ageText . '</span>';
                 }
             }
         } elseif ($record instanceof Family) {
             $indi = Individual::getInstance($pid, $record->getTree());
             if ($indi) {
                 $birth_date = $indi->getBirthDate();
                 $death_date = $indi->getDeathDate();
                 $ageText = '';
                 if (Date::compare($date, $death_date) <= 0) {
                     $age = Date::getAgeGedcom($birth_date, $date);
                     // Only show calculated age if it differs from recorded age
                     if ($age != '' && $age > 0) {
                         if ($fact_age != '' && $fact_age != $age || $fact_age == '' && $husb_age == '' && $wife_age == '' || $husb_age != '' && $indi->getSex() == 'M' && $husb_age != $age || $wife_age != '' && $indi->getSex() == 'F' && $wife_age != $age) {
                             $ageText = '(' . I18N::translate('Age') . ' ' . FunctionsDate::getAgeAtEvent($age, false) . ')';
                         }
                     }
                 }
                 if ($ageText) {
                     $html .= ' <span class="age">' . $ageText . '</span>';
                 }
             }
         }
     } else {
         // 1 DEAT Y with no DATE => print YES
         // 1 BIRT 2 SOUR @S1@ => print YES
         // 1 DEAT N is not allowed
         // It is not proper GEDCOM form to use a N(o) value with an event tag to infer that it did not happen.
         $factdetail = explode(' ', trim($factrec));
         if (isset($factdetail) && (count($factdetail) == 3 && strtoupper($factdetail[2]) == 'Y') || count($factdetail) == 4 && $factdetail[2] == 'SOUR') {
             $html .= I18N::translate('yes');
         }
     }
     // print gedcom ages
     foreach (array(GedcomTag::getLabel('AGE') => $fact_age, GedcomTag::getLabel('HUSB') => $husb_age, GedcomTag::getLabel('WIFE') => $wife_age) as $label => $age) {
         if ($age != '') {
             $html .= ' <span class="label">' . $label . ':</span> <span class="age">' . FunctionsDate::getAgeAtEvent($age, false) . '</span>';
         }
     }
     return $html;
 }
Exemple #9
0
    private function pageBody(PageController $controller)
    {
        $FTV_SETTINGS = unserialize($this->getSetting('FTV_SETTINGS'));
        ?>
		<!-- ADMIN PAGE CONTENT -->
		<ol class="breadcrumb small">
			<li><a href="admin.php"><?php 
        echo I18N::translate('Control panel');
        ?>
</a></li>
			<li><a href="admin_modules.php"><?php 
        echo I18N::translate('Module administration');
        ?>
</a></li>
			<li class="active"><?php 
        echo $controller->getPageTitle();
        ?>
</li>
		</ol>
		<h2><?php 
        echo $controller->getPageTitle();
        ?>
</h2>

		<!-- *** FORM 1 *** -->
		<form class="form-horizontal" method="post" name="form1">
			<?php 
        echo Filter::getCsrf();
        ?>
			<!-- SELECT TREE -->
			<div class="form-group">
				<label class="control-label col-sm-2" for="tree">
					<?php 
        echo I18N::translate('Family tree');
        ?>
				</label>
				<div class="col-sm-4">
					<select id="tree" name="NEW_FIB_TREE" class="form-control">
						<?php 
        foreach (Tree::getAll() as $tree) {
            ?>
							<?php 
            if ($tree->getTreeId() == $this->tree_id) {
                ?>
								<option value="<?php 
                echo $tree->getTreeId();
                ?>
" data-ged="<?php 
                echo $tree->getNameHtml();
                ?>
" selected="selected">
									<?php 
                echo $tree->getTitleHtml();
                ?>
								</option>
							<?php 
            } else {
                ?>
								<option value="<?php 
                echo $tree->getTreeId();
                ?>
" data-ged="<?php 
                echo $tree->getNameHtml();
                ?>
">
									<?php 
                echo $tree->getTitleHtml();
                ?>
								</option>
							<?php 
            }
            ?>
						<?php 
        }
        ?>
					</select>
				</div>
			</div>
		</form>

		<!-- PANEL GROUP ACCORDION -->
		<div class="panel-group" id="accordion">
			<!-- PANEL 1 -->
			<div class="panel panel-default" id="panel1">
				<div class="panel-heading">
					<h4 class="panel-title">
						<a data-toggle="collapse" data-target="#collapseOne" href="#">
							<?php 
        echo I18N::translate('Pages');
        ?>
						</a>
					</h4>
				</div>
				<div id="collapseOne" class="panel-collapse collapse in">
					<div class="panel-body">
						<?php 
        if (empty($FTV_SETTINGS) || !empty($FTV_SETTINGS) && !$this->searchArray($FTV_SETTINGS, 'TREE', $this->tree_id)) {
            ?>
							<div class="alert alert-info alert-dismissible" role="alert">
								<button type="button" class="close" data-dismiss="alert" aria-label="' . I18N::translate('close') . '">
									<span aria-hidden="true">&times;</span>
								</button>
								<p class="small text-muted">
									<?php 
            echo I18N::translate('Use the search form below to search for a root person. After a successful search the Fancy Treeview page will be automatically created. You can add as many root persons as you want.');
            ?>
								</p>
							</div>
						<?php 
        }
        ?>
						<!-- *** FORM 2 *** -->
						<div id="ftv-search-form" class="form-group alert alert-info">
							<form class="form-inline" method="post" name="form2">
								<!-- SURNAME SEARCH FIELD -->
								<div class="form-group">
									<label class="control-label">
										<?php 
        echo I18N::translate('Search root person');
        ?>
									</label>
									<input
										class="form-control"
										data-autocomplete-type="SURN"
										id="surname-search"
										name="SURNAME"
										placeholder="<?php 
        echo I18N::translate('Surname');
        ?>
"
										type="text"
										>
									<label class="checkbox-inline">
										<?php 
        echo FunctionsEdit::checkbox('soundex_std') . I18N::translate('Russell');
        ?>
									</label>
									<label class="checkbox-inline">
										<?php 
        echo FunctionsEdit::checkbox('soudex_dm') . I18N::translate('Daitch-Mokotoff');
        ?>
									</label>
									<button name="search" class="btn btn-primary" type="submit">
										<i class="fa fa-search"></i>
										<?php 
        echo I18N::translate('search');
        ?>
									</button>
								</div>
								<!-- PID SEARCH FIELD -->
								<?php 
        $class = I18N::direction() === 'rtl' ? 'pull-left' : 'pull-right';
        ?>
								<div class="form-group <?php 
        echo $class;
        ?>
">
									<label class="control-label" for="pid-search">
										<?php 
        echo I18N::translate('Or enter an ID');
        ?>
									</label>
									<input
										class="form-control"
										data-autocomplete-type="INDI"
										id="pid-search"
										name="PID"
										placeholder="<?php 
        echo I18N::translate('Search ID by name');
        ?>
"
										type="text"
										value=""
										>
									<button name="Ok" class="btn btn-primary" type="submit">
										<i class="fa fa-check"></i>
										<?php 
        echo I18N::translate('ok');
        ?>
									</button>
								</div>
							</form>
							<!-- *** FORM 3 *** -->
							<form class="form-horizontal" method="post" name="form3">
								<!-- TABLE -->
								<table id="search-result-table" class="table" style="display: none">
									<thead>
										<tr>
											<th><?php 
        echo I18N::translate('Root person');
        ?>
</th>
											<?php 
        if (!$this->options('use_fullname')) {
            ?>
												<th><?php 
            echo I18N::translate('Surname in page title');
            ?>
</th>
											<?php 
        }
        ?>
											<th><?php 
        echo I18N::translate('Page title');
        ?>
</th>
											<th><?php 
        echo I18N::translate('Access level');
        ?>
</th>
											<th><?php 
        echo I18N::translate('Add');
        ?>
</th>
										</tr>
									</thead>
									<tbody>
										<tr>
											<!-- ROOT PERSONS FULL NAME -->
											<td id="root">
												<?php 
        if ($this->options('use_fullname')) {
            ?>
													<input
														name="surname"
														type="hidden"
														value=""
														>
													<?php 
        }
        ?>
												<input
													name="pid"
													type="hidden"
													value=""
													>
												<input
													name="sort"
													type="hidden"
													value=""
													>
												<span></span>
											</td>
											<?php 
        if (!$this->options('use_fullname')) {
            ?>
												<!-- SURNAME IN PAGE TITLE -->
												<td id="surn">
													<label class="showname"></label>
													<input
														class="form-control editname"
														name="surname"
														type="text"
														value=""
														>
												</td>
											<?php 
        }
        ?>
											<!-- PAGE TITLE -->
											<td id="title"></td>
											<!-- ACCESS LEVEL -->
											<td>
												<?php 
        echo FunctionsEdit::editFieldAccessLevel('access_level', 2, 'class="form-control"');
        ?>
											</td>
											<!-- ADD BUTTON -->
											<td>
												<button	type="submit" name="add" class="btn btn-success btn-sm" title="<?php 
        I18N::translate('Add');
        ?>
">
													<i class="fa fa-plus"></i>
												</button>
											</td>
										</tr>
									</tbody>
								</table>
							</form>
						</div>
						<?php 
        echo $this->addMessage("error", "danger", true);
        ?>
						<?php 
        echo $this->addMessage('update-settings', 'success', true, I18N::translate('The settings for this tree are succesfully updated'));
        ?>
						<div id="fancy-treeview-form" class="form-group">
							<?php 
        if (!empty($FTV_SETTINGS) && $this->searchArray($FTV_SETTINGS, 'TREE', $this->tree_id)) {
            ?>
								<form class="form-horizontal" method="post" name="form4">
									<!-- TABLE -->
									<table id="fancy-treeview-table" class="table table-hover">
										<thead>
											<tr>
												<th><?php 
            echo I18N::translate('Root person');
            ?>
</th>
												<?php 
            if (!$this->options('use_fullname')) {
                ?>
													<th><?php 
                echo I18N::translate('Surname in page title');
                ?>
</th>
												<?php 
            }
            ?>
												<th><?php 
            echo I18N::translate('Page title');
            ?>
</th>
												<th><?php 
            echo I18N::translate('Access level');
            ?>
</th>
												<th><?php 
            echo I18N::translate('Delete');
            ?>
</th>
											</tr>
										</thead>
										<tbody>
											<?php 
            foreach ($FTV_SETTINGS as $key => $this_ITEM) {
                ?>
												<?php 
                if ($this_ITEM['TREE'] == $this->tree_id) {
                    ?>
													<?php 
                    if (Individual::getInstance($this_ITEM['PID'], $this->tree)) {
                        ?>
														<tr class="sortme">
															<!-- ROOT PERSONS FULL NAME -->
															<td>
																<input
																	name="pid[<?php 
                        echo $key;
                        ?>
]"
																	type="hidden"
																	value="<?php 
                        echo $this_ITEM['PID'];
                        ?>
"
																	>
																<input
																	name="sort[<?php 
                        echo $key;
                        ?>
]"
																	type="hidden"
																	value="<?php 
                        echo $this_ITEM['SORT'];
                        ?>
"
																	>
																	<?php 
                        echo Individual::getInstance($this_ITEM['PID'], $this->tree)->getFullName() . '';
                        ?>
																(<?php 
                        echo Individual::getInstance($this_ITEM['PID'], $this->tree)->getLifeSpan();
                        ?>
)
															</td>
															<?php 
                        if (!$this->options('use_fullname')) {
                            ?>
																<!-- SURNAME IN PAGE TITLE -->
																<td>
																	<label class="showname">
																		<?php 
                            echo $this_ITEM['SURNAME'];
                            ?>
																	</label>
																	<input
																		class="form-control editname"
																		name="surname[<?php 
                            echo $key;
                            ?>
]"
																		type="text"
																		value="<?php 
                            echo $this_ITEM['SURNAME'];
                            ?>
"
																		>
																</td>
															<?php 
                        }
                        ?>
															<!-- PAGE TITLE -->
															<td>
																<a href="module.php?mod=<?php 
                        echo $this->getName();
                        ?>
&amp;mod_action=page&amp;ged=<?php 
                        echo $this->tree->getNameHtml();
                        ?>
&amp;rootid=<?php 
                        echo $this_ITEM['PID'];
                        ?>
" target="_blank">
																	<?php 
                        if ($this->options('use_fullname') == true) {
                            echo I18N::translate('Descendants of %s', Individual::getInstance($this_ITEM['PID'], $this->tree)->getFullName());
                        } else {
                            echo I18N::translate('Descendants of the %s family', $this_ITEM['SURNAME']);
                        }
                        ?>
																</a>
															</td>
															<!-- ACCESS LEVEL -->
															<td>
																<?php 
                        echo FunctionsEdit::editFieldAccessLevel('access_level[' . $key . ']', $this_ITEM['ACCESS_LEVEL'], 'class="form-control"');
                        ?>
															</td>
															<!-- DELETE BUTTON -->
															<td>
																<button	type="button" name="delete" class="btn btn-danger btn-sm" data-key="<?php 
                        echo $key;
                        ?>
" title="<?php 
                        I18N::translate('Delete');
                        ?>
">
																	<i class="fa fa-trash-o"></i>
																</button>
															</td>
														</tr>
													<?php 
                    } else {
                        ?>
														<tr>
															<!-- SURNAME -->
															<td class="error">
																<input
																	name="pid[<?php 
                        echo $key;
                        ?>
]"
																	type="hidden"
																	value="<?php 
                        echo $this_ITEM['PID'];
                        ?>
"
																	>
																	<?php 
                        echo $this_ITEM['SURNAME'];
                        ?>
															</td>
															<!-- ERROR MESSAGE -->
															<td colspan="4" class="error">
																<?php 
                        echo I18N::translate('The person with root id %s doesn’t exist anymore in this tree', $this_ITEM['PID']);
                        ?>
															</td>
															<!-- DELETE BUTTON -->
															<td>
																<button name="delete" type="button" class="btn btn-danger btn-sm" title="<?php 
                        I18N::translate('Delete');
                        ?>
">
																	<i class="fa fa-trash-o"></i>
																</button>
															</td>
														</tr>
													<?php 
                    }
                    ?>
												<?php 
                }
                ?>
											<?php 
            }
            ?>
										</tbody>
									</table>
									<!-- BUTTONS -->
									<button name="update" class="btn btn-primary" type="submit">
										<i class="fa fa-check"></i>
										<?php 
            echo I18N::translate('update');
            ?>
									</button>
								</form>
							<?php 
        }
        ?>
						</div>
					</div>
				</div>
			</div>

			<!-- PANEL 2 -->
			<div class="panel panel-default" id="panel2">
				<div class="panel-heading">
					<h4 class="panel-title">
						<a data-toggle="collapse" data-target="#collapseTwo" href="#" class="collapsed">
							<?php 
        echo I18N::translate('Options for %s', $this->tree->getTitleHtml());
        ?>
						</a>
					</h4>
				</div>
				<div id="collapseTwo" class="panel-collapse collapse">
					<div class="panel-body">
						<?php 
        echo $this->addMessage('save-options', 'success', true, I18N::translate('The options for this tree are succesfully saved'));
        ?>
						<?php 
        echo $this->addMessage('reset-options', 'success', true, I18N::translate('The options for this tree are succesfully reset to the default settings'));
        ?>
						<div id="ftv-options-form" class="form-group">
							<form class="form-horizontal" method="post" name="form5">
								<!-- USE FULLNAME IN MENU -->
								<div class="form-group fullname">
									<label class="control-label col-sm-4">
										<?php 
        echo I18N::translate('Use fullname in menu');
        ?>
									</label>
									<div class="col-sm-8">
										<?php 
        echo FunctionsEdit::editFieldYesNo('NEW_FTV_OPTIONS[USE_FULLNAME]', $this->options('use_fullname'), 'class="radio-inline"');
        ?>
									</div>
								</div>
								<!-- GENERATION BLOCKS -->
								<div class="form-group">
									<label class="control-label col-sm-4">
										<?php 
        echo I18N::translate('Number of generation blocks to show');
        ?>
									</label>
									<div class="col-sm-4">
										<?php 
        echo FunctionsEdit::selectEditControl('NEW_FTV_OPTIONS[NUMBLOCKS]', array(I18N::translate('All'), '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'), null, $this->options('numblocks'), 'class="form-control"');
        ?>
									</div>
									<p class="col-sm-8 col-sm-offset-4 small text-muted">
										<?php 
        echo I18N::translate('This option is especially usefull for large trees. When you notice a slow page load, here you can set the number of generation blocks to load at once to a lower level. Below the last generation block a button will appear to add the next set of generation blocks. The new blocks will be added to the blocks already loaded. Clicking on a “follow” link in the last visible generation block, will also load the next set of generation blocks.');
        ?>
									</p>
								</div>
								<!-- SHOW SINGLES -->
								<div class="form-group">
									<label class="control-label col-sm-4">
										<?php 
        echo I18N::translate('Show single persons');
        ?>
									</label>
									<div class="col-sm-8">
										<?php 
        echo FunctionsEdit::editFieldYesNo('NEW_FTV_OPTIONS[SHOW_SINGLES]', $this->options('show_singles'), 'class="radio-inline"');
        ?>
									</div>
									<p class="col-sm-8 col-sm-offset-4 small text-muted">
										<?php 
        echo I18N::translate('Turn this option on if you want to show single persons in the generation blocks. Single persons are persons without partner and children. With this option turned on, every child of a family will be shown in a detailed way in the next generation block.');
        ?>
									</p>
								</div>
								<!-- CHECK RELATIONSHIP -->
								<div class="form-group">
									<label class="control-label col-sm-4">
										<?php 
        echo I18N::translate('Check relationship between partners');
        ?>
									</label>
									<div class="col-sm-8">
										<?php 
        echo FunctionsEdit::editFieldYesNo('NEW_FTV_OPTIONS[CHECK_RELATIONSHIP]', $this->options('check_relationship'), 'class="radio-inline"');
        ?>
									</div>
									<p class="col-sm-8 col-sm-offset-4 small text-muted">
										<?php 
        echo I18N::translate('With this option turned on, the script checks if a (married) couple has the same ancestors. If a relationship between the partners is found, a text will appear between brackets after the spouses’ name to indicate the blood relationship.');
        ?>
</p>
									<p class="col-sm-8 col-sm-offset-4 small text-muted">
										<?php 
        echo I18N::translate('<strong>Note</strong>: this option can be time and/or memory consuming, especially on large trees. It can cause very slow page loading or an ’execution time out error’ on your server. If you notice such a behavior, reduce the number of generation blocks to load at once or don’t use it in combination with the option to show singles (see the previous options). If you still experience any problems, don’t use this option at all.');
        ?>
									</p>
								</div>
								<!-- SHOW PLACES -->
								<div id="places" class="form-group">
									<label class="control-label col-sm-4">
										<?php 
        echo I18N::translate('Show places?');
        ?>
									</label>
									<div class="col-sm-8">
										<?php 
        echo FunctionsEdit::editFieldYesNo('NEW_FTV_OPTIONS[SHOW_PLACES]', $this->options('show_places'), 'class="radio-inline"');
        ?>
									</div>
								</div>
								<!-- USE GEDCOM PLACE SETTING -->
								<div id="gedcom_places" class="form-group<?php 
        if (!$this->options('show_places')) {
            echo ' collapse';
        }
        ?>
">
									<label class="control-label col-sm-4">
										<?php 
        echo I18N::translate('Use default GEDCOM settings to abbreviate place names?');
        ?>
									</label>
									<div class="col-sm-8">
										<?php 
        echo FunctionsEdit::editFieldYesNo('NEW_FTV_OPTIONS[USE_GEDCOM_PLACES]', $this->options('use_gedcom_places'), 'class="radio-inline"');
        ?>
									</div>
									<p class="col-sm-8 col-sm-offset-4 small text-muted">
										<?php 
        echo I18N::translate('If you have ticked the “Show places” option, you can choose to use the default GEDCOM settings to abbreviate placenames. If you don’t set this option, full place names will be shown.');
        ?>
									</p>
								</div>
								<!-- GET COUNTRYLIST -->
								<?php 
        if ($this->getCountrylist()) {
            ?>
									<div id="country_list" class="form-group<?php 
            if (!$this->options('show_places') || $this->options('use_gedcom_places')) {
                echo ' collapse';
            }
            ?>
">
										<label class="control-label col-sm-4">
											<?php 
            echo I18N::translate('Select your country');
            ?>
										</label>
										<div class="col-sm-8">
											<?php 
            echo FunctionsEdit::selectEditControl('NEW_FTV_OPTIONS[COUNTRY]', $this->getCountryList(), '', $this->options('country'), 'class="form-control"');
            ?>
										</div>
										<p class="col-sm-8 col-sm-offset-4 small text-muted">
											<?php 
            echo I18N::translate('If you have ticked the “Show places” option but NOT the option to abbreviate placenames, you can set your own country here. Full places will be listed on the Fancy Treeview pages, but when a place includes the name of your own country, this name will be left out. If you don’t select a country then all countries will be shown, including your own.');
            ?>
										</p>
									</div>
								<?php 
        }
        ?>
								<!-- SHOW OCCUPATIONS -->
								<div class="form-group">
									<label class="control-label col-sm-4">
										<?php 
        echo I18N::translate('Show occupations');
        ?>
									</label>
									<div class="col-sm-8">
										<?php 
        echo FunctionsEdit::editFieldYesNo('NEW_FTV_OPTIONS[SHOW_OCCU]', $this->options('show_occu'), 'class="radio-inline"');
        ?>
									</div>
								</div>
								<!-- RESIZE THUMBS -->
								<div id="resize_thumbs" class="form-group">
									<label class="control-label col-sm-4">
										<?php 
        echo I18N::translate('Resize thumbnails');
        ?>
									</label>
									<div class="col-sm-8">
										<?php 
        echo FunctionsEdit::editFieldYesNo('NEW_FTV_OPTIONS[RESIZE_THUMBS]', $this->options('resize_thumbs'), 'class="radio-inline"');
        ?>
									</div>
									<p class="col-sm-8 col-sm-offset-4 small text-muted">
										<?php 
        echo I18N::translate('Here you can choose to resize the default webtrees thumbnails especially for the Fancy Treeview pages. You can set a custom size in percentage or in pixels. If you choose “no” the default webtrees thumbnails will be used with the formats you have set on the tree configuration page.');
        ?>
									</p>
								</div>
								<!-- THUMB SIZE -->
								<div id="thumb_size" class="form-group<?php 
        if (!$this->options('resize_thumbs')) {
            echo ' collapse';
        }
        ?>
">
									<label class="control-label col-sm-4">
										<?php 
        echo I18N::translate('Thumbnail size');
        ?>
									</label>
									<div class="row">
										<div class="col-sm-1">
											<input
												class="form-control"
												id="NEW_FTV_OPTIONS[THUMB_SIZE]"
												name="NEW_FTV_OPTIONS[THUMB_SIZE]"
												type="text"
												value="<?php 
        echo $this->options('thumb_size');
        ?>
"
												>
										</div>
										<div class="col-sm-2">
											<?php 
        echo FunctionsEdit::selectEditControl('NEW_FTV_OPTIONS[THUMB_RESIZE_FORMAT]', array('1' => I18N::translate('percent'), '2' => I18N::translate('pixels')), null, $this->options('thumb_resize_format'), 'class="form-control"');
        ?>
										</div>
									</div>
								</div>
								<!-- SQUARE THUMBS -->
								<div id="square_thumbs" class="form-group<?php 
        if (!$this->options('resize_thumbs')) {
            echo ' collapse';
        }
        ?>
">
									<label class="control-label col-sm-4">
										<?php 
        echo I18N::translate('Use square thumbnails');
        ?>
									</label>
									<div class="col-sm-8">
										<?php 
        echo FunctionsEdit::editFieldYesNo('NEW_FTV_OPTIONS[USE_SQUARE_THUMBS]', $this->options('use_square_thumbs'), 'class="radio-inline"');
        ?>
									</div>
								</div>
								<!-- SHOW USERFORM -->
								<div class="form-group">
									<label class="control-label col-sm-4">
										<?php 
        echo I18N::translate('Show form to change start person');
        ?>
									</label>
									<div class="col-sm-4">
										<?php 
        echo FunctionsEdit::editFieldAccessLevel('NEW_FTV_OPTIONS[SHOW_USERFORM]', $this->options('show_userform'), 'class="form-control"');
        ?>
									</div>
								</div>
								<!-- SHOW PDF -->
								<div class="form-group">
									<label class="control-label col-sm-4">
										<?php 
        echo I18N::translate('Show PDF icon?');
        ?>
									</label>
									<div class="col-sm-4">
										<?php 
        echo FunctionsEdit::editFieldAccessLevel('NEW_FTV_OPTIONS[SHOW_PDF_ICON]', $this->options('show_pdf_icon'), 'class="form-control"');
        ?>
									</div>
								</div>
								<!-- SHOW FANCY TREEVIEW ON INDI PAGE -->
								<div class="form-group fullname">
									<label class="control-label col-sm-4">
										<?php 
        echo I18N::translate('Show a Fancy Treeview tab on the individual page');
        ?>
									</label>
									<div class="col-sm-8">
										<?php 
        echo FunctionsEdit::editFieldYesNo('NEW_FTV_OPTIONS[FTV_TAB]', $this->options('ftv_tab'), 'class="radio-inline"');
        ?>
									</div>
								</div>
								<!-- BUTTONS -->
								<button name="save-options" class="btn btn-primary" type="submit">
									<i class="fa fa-check"></i>
									<?php 
        echo I18N::translate('save');
        ?>
								</button>
								<button name="reset-options" class="btn btn-primary" type="reset">
									<i class="fa fa-recycle"></i>
									<?php 
        echo I18N::translate('reset');
        ?>
								</button>
							</form>
						</div>
					</div>
				</div>
			</div>
		</div>
		<?php 
    }
Exemple #10
0
            if (!Auth::user()->getPreference('canadmin')) {
                // Keep a reference to the currently logged in user because after logging out this user,
                // a call to Auth::user() will not return this user anymore
                $currentUser = Auth::user();
                Auth::logout();
                $currentUser->delete();
            }
            break;
    }
    header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME);
    return;
}
$controller = new PageController();
$controller->setPageTitle(I18N::translate('My account'))->pageHeader()->addExternalJavascript(WT_AUTOCOMPLETE_JS_URL)->addInlineJavascript('autocomplete();');
$my_individual_record = Individual::getInstance($WT_TREE->getUserPreference(Auth::user(), 'gedcomid'), $WT_TREE);
$default_individual = Individual::getInstance($WT_TREE->getUserPreference(Auth::user(), 'rootid'), $WT_TREE);
// Form validation
?>
<script>
function checkform(frm) {
	if (frm.form_username.value=="") {
		alert("<?php 
echo I18N::translate('You must enter a user name.');
?>
");
		frm.form_username.focus();
		return false;
	}
	if (frm.form_realname.value=="") {
		alert("<?php 
echo I18N::translate('You must enter a real name.');
Exemple #11
0
 /**
  * Get the record to which this fact links
  *
  * @return Individual|Family|Source|Repository|Media|Note|null
  */
 public function getTarget()
 {
     $xref = trim($this->getValue(), '@');
     switch ($this->tag) {
         case 'FAMC':
         case 'FAMS':
             return Family::getInstance($xref, $this->getParent()->getTree());
         case 'HUSB':
         case 'WIFE':
         case 'CHIL':
             return Individual::getInstance($xref, $this->getParent()->getTree());
         case 'SOUR':
             return Source::getInstance($xref, $this->getParent()->getTree());
         case 'OBJE':
             return Media::getInstance($xref, $this->getParent()->getTree());
         case 'REPO':
             return Repository::getInstance($xref, $this->getParent()->getTree());
         case 'NOTE':
             return Note::getInstance($xref, $this->getParent()->getTree());
         default:
             return GedcomRecord::getInstance($xref, $this->getParent()->getTree());
     }
 }
 /**
  * A list for the side bar.
  *
  * @return string
  */
 public function getCartList()
 {
     global $WT_TREE;
     $cart = Session::get('cart', array());
     if (!array_key_exists($WT_TREE->getTreeId(), $cart)) {
         $cart[$WT_TREE->getTreeId()] = array();
     }
     $pid = Filter::get('pid', WT_REGEX_XREF);
     if (!$cart[$WT_TREE->getTreeId()]) {
         $out = I18N::translate('Your clippings cart is empty.');
     } else {
         $out = '<ul>';
         foreach (array_keys($cart[$WT_TREE->getTreeId()]) as $xref) {
             $record = GedcomRecord::getInstance($xref, $WT_TREE);
             if ($record instanceof Individual || $record instanceof Family) {
                 switch ($record::RECORD_TYPE) {
                     case 'INDI':
                         $icon = 'icon-indis';
                         break;
                     case 'FAM':
                         $icon = 'icon-sfamily';
                         break;
                 }
                 $out .= '<li>';
                 if (!empty($icon)) {
                     $out .= '<i class="' . $icon . '"></i>';
                 }
                 $out .= '<a href="' . $record->getHtmlUrl() . '">';
                 if ($record instanceof Individual) {
                     $out .= $record->getSexImage();
                 }
                 $out .= ' ' . $record->getFullName() . ' ';
                 if ($record instanceof Individual && $record->canShow()) {
                     $out .= ' (' . $record->getLifeSpan() . ')';
                 }
                 $out .= '</a>';
                 $out .= '<a class="icon-remove remove_cart" href="module.php?mod=' . $this->getName() . '&amp;mod_action=ajax&amp;sb_action=clippings&amp;remove=' . $xref . '&amp;pid=' . $pid . '" title="' . I18N::translate('Remove') . '"></a>';
                 $out .= '</li>';
             }
         }
         $out .= '</ul>';
     }
     if ($cart[$WT_TREE->getTreeId()]) {
         $out .= '<br><a href="module.php?mod=' . $this->getName() . '&amp;mod_action=ajax&amp;sb_action=clippings&amp;empty=true&amp;pid=' . $pid . '" class="remove_cart">' . I18N::translate('Empty the clippings cart') . '</a>' . '<br>' . '<a href="module.php?mod=' . $this->getName() . '&amp;mod_action=ajax&amp;sb_action=clippings&amp;download=true&amp;pid=' . $pid . '" class="add_cart">' . I18N::translate('Download') . '</a>';
     }
     $record = Individual::getInstance($pid, $WT_TREE);
     if ($record && !array_key_exists($record->getXref(), $cart[$WT_TREE->getTreeId()])) {
         $out .= '<br><a href="module.php?mod=' . $this->getName() . '&amp;mod_action=ajax&amp;sb_action=clippings&amp;add=' . $pid . '&amp;pid=' . $pid . '" class="add_cart"><i class="icon-clippings"></i> ' . I18N::translate('Add %s to the clippings cart', $record->getFullName()) . '</a>';
     }
     return $out;
 }
 /**
  * Perform the search
  */
 private function advancedSearch()
 {
     global $WT_TREE;
     $this->myindilist = array();
     $fct = count($this->fields);
     if (!array_filter($this->values)) {
         return;
     }
     // Dynamic SQL query, plus bind variables
     $sql = 'SELECT DISTINCT ind.i_id AS xref, ind.i_gedcom AS gedcom FROM `##individuals` ind';
     $bind = array();
     // Join the following tables
     $father_name = false;
     $mother_name = false;
     $spouse_family = false;
     $indi_name = false;
     $indi_date = false;
     $fam_date = false;
     $indi_plac = false;
     $fam_plac = false;
     foreach ($this->fields as $n => $field) {
         if ($this->values[$n]) {
             if (substr($field, 0, 14) == 'FAMC:HUSB:NAME') {
                 $father_name = true;
             } elseif (substr($field, 0, 14) == 'FAMC:WIFE:NAME') {
                 $mother_name = true;
             } elseif (substr($field, 0, 4) == 'NAME') {
                 $indi_name = true;
             } elseif (strpos($field, ':DATE') !== false) {
                 if (substr($field, 0, 4) == 'FAMS') {
                     $fam_date = true;
                     $spouse_family = true;
                 } else {
                     $indi_date = true;
                 }
             } elseif (strpos($field, ':PLAC') !== false) {
                 if (substr($field, 0, 4) == 'FAMS') {
                     $fam_plac = true;
                     $spouse_family = true;
                 } else {
                     $indi_plac = true;
                 }
             } elseif ($field == 'FAMS:NOTE') {
                 $spouse_family = true;
             }
         }
     }
     if ($father_name || $mother_name) {
         $sql .= " JOIN `##link`   l_1 ON (l_1.l_file=ind.i_file AND l_1.l_from=ind.i_id AND l_1.l_type='FAMC')";
     }
     if ($father_name) {
         $sql .= " JOIN `##link`   l_2 ON (l_2.l_file=ind.i_file AND l_2.l_from=l_1.l_to AND l_2.l_type='HUSB')";
         $sql .= " JOIN `##name`   f_n ON (f_n.n_file=ind.i_file AND f_n.n_id  =l_2.l_to)";
     }
     if ($mother_name) {
         $sql .= " JOIN `##link`   l_3 ON (l_3.l_file=ind.i_file AND l_3.l_from=l_1.l_to AND l_3.l_type='WIFE')";
         $sql .= " JOIN `##name`   m_n ON (m_n.n_file=ind.i_file AND m_n.n_id  =l_3.l_to)";
     }
     if ($spouse_family) {
         $sql .= " JOIN `##link`     l_4 ON (l_4.l_file=ind.i_file AND l_4.l_from=ind.i_id AND l_4.l_type='FAMS')";
         $sql .= " JOIN `##families` fam ON (fam.f_file=ind.i_file AND fam.f_id  =l_4.l_to)";
     }
     if ($indi_name) {
         $sql .= " JOIN `##name`   i_n ON (i_n.n_file=ind.i_file AND i_n.n_id=ind.i_id)";
     }
     if ($indi_date) {
         $sql .= " JOIN `##dates`  i_d ON (i_d.d_file=ind.i_file AND i_d.d_gid=ind.i_id)";
     }
     if ($fam_date) {
         $sql .= " JOIN `##dates`  f_d ON (f_d.d_file=ind.i_file AND f_d.d_gid=fam.f_id)";
     }
     if ($indi_plac) {
         $sql .= " JOIN `##placelinks`   i_pl ON (i_pl.pl_file=ind.i_file AND i_pl.pl_gid =ind.i_id)";
         $sql .= " JOIN (" . "SELECT CONCAT_WS(', ', p1.p_place, p2.p_place, p3.p_place, p4.p_place, p5.p_place, p6.p_place, p7.p_place, p8.p_place, p9.p_place) AS place, p1.p_id AS id, p1.p_file AS file" . " FROM      `##places` AS p1" . " LEFT JOIN `##places` AS p2 ON (p1.p_parent_id=p2.p_id)" . " LEFT JOIN `##places` AS p3 ON (p2.p_parent_id=p3.p_id)" . " LEFT JOIN `##places` AS p4 ON (p3.p_parent_id=p4.p_id)" . " LEFT JOIN `##places` AS p5 ON (p4.p_parent_id=p5.p_id)" . " LEFT JOIN `##places` AS p6 ON (p5.p_parent_id=p6.p_id)" . " LEFT JOIN `##places` AS p7 ON (p6.p_parent_id=p7.p_id)" . " LEFT JOIN `##places` AS p8 ON (p7.p_parent_id=p8.p_id)" . " LEFT JOIN `##places` AS p9 ON (p8.p_parent_id=p9.p_id)" . ") AS i_p ON (i_p.file  =ind.i_file AND i_pl.pl_p_id= i_p.id)";
     }
     if ($fam_plac) {
         $sql .= " JOIN `##placelinks`   f_pl ON (f_pl.pl_file=ind.i_file AND f_pl.pl_gid =fam.f_id)";
         $sql .= " JOIN (" . "SELECT CONCAT_WS(', ', p1.p_place, p2.p_place, p3.p_place, p4.p_place, p5.p_place, p6.p_place, p7.p_place, p8.p_place, p9.p_place) AS place, p1.p_id AS id, p1.p_file AS file" . " FROM      `##places` AS p1" . " LEFT JOIN `##places` AS p2 ON (p1.p_parent_id=p2.p_id)" . " LEFT JOIN `##places` AS p3 ON (p2.p_parent_id=p3.p_id)" . " LEFT JOIN `##places` AS p4 ON (p3.p_parent_id=p4.p_id)" . " LEFT JOIN `##places` AS p5 ON (p4.p_parent_id=p5.p_id)" . " LEFT JOIN `##places` AS p6 ON (p5.p_parent_id=p6.p_id)" . " LEFT JOIN `##places` AS p7 ON (p6.p_parent_id=p7.p_id)" . " LEFT JOIN `##places` AS p8 ON (p7.p_parent_id=p8.p_id)" . " LEFT JOIN `##places` AS p9 ON (p8.p_parent_id=p9.p_id)" . ") AS f_p ON (f_p.file  =ind.i_file AND f_pl.pl_p_id= f_p.id)";
     }
     // Add the where clause
     $sql .= " WHERE ind.i_file=?";
     $bind[] = $WT_TREE->getTreeId();
     for ($i = 0; $i < $fct; $i++) {
         $field = $this->fields[$i];
         $value = $this->values[$i];
         if ($value === '') {
             continue;
         }
         $parts = preg_split("/:/", $field . '::::');
         if ($parts[0] == 'NAME') {
             // NAME:*
             switch ($parts[1]) {
                 case 'GIVN':
                     switch ($parts[2]) {
                         case 'EXACT':
                             $sql .= " AND i_n.n_givn=?";
                             $bind[] = $value;
                             break;
                         case 'BEGINS':
                             $sql .= " AND i_n.n_givn LIKE CONCAT(?, '%')";
                             $bind[] = $value;
                             break;
                         case 'CONTAINS':
                             $sql .= " AND i_n.n_givn LIKE CONCAT('%', ?, '%')";
                             $bind[] = $value;
                             break;
                         case 'SDX_STD':
                             $sdx = Soundex::russell($value);
                             if ($sdx !== null) {
                                 $sdx = explode(':', $sdx);
                                 foreach ($sdx as $k => $v) {
                                     $sdx[$k] = "i_n.n_soundex_givn_std LIKE CONCAT('%', ?, '%')";
                                     $bind[] = $v;
                                 }
                                 $sql .= ' AND (' . implode(' OR ', $sdx) . ')';
                             } else {
                                 // No phonetic content?  Use a substring match
                                 $sql .= " AND i_n.n_givn LIKE CONCAT('%', ?, '%')";
                                 $bind[] = $value;
                             }
                             break;
                         case 'SDX':
                             // SDX uses DM by default.
                         // SDX uses DM by default.
                         case 'SDX_DM':
                             $sdx = Soundex::daitchMokotoff($value);
                             if ($sdx !== null) {
                                 $sdx = explode(':', $sdx);
                                 foreach ($sdx as $k => $v) {
                                     $sdx[$k] = "i_n.n_soundex_givn_dm LIKE CONCAT('%', ?, '%')";
                                     $bind[] = $v;
                                 }
                                 $sql .= ' AND (' . implode(' OR ', $sdx) . ')';
                             } else {
                                 // No phonetic content?  Use a substring match
                                 $sql .= " AND i_n.n_givn LIKE CONCAT('%', ?, '%')";
                                 $bind[] = $value;
                             }
                             break;
                     }
                     break;
                 case 'SURN':
                     switch ($parts[2]) {
                         case 'EXACT':
                             $sql .= " AND i_n.n_surname=?";
                             $bind[] = $value;
                             break;
                         case 'BEGINS':
                             $sql .= " AND i_n.n_surname LIKE CONCAT(?, '%')";
                             $bind[] = $value;
                             break;
                         case 'CONTAINS':
                             $sql .= " AND i_n.n_surname LIKE CONCAT('%', ?, '%')";
                             $bind[] = $value;
                             break;
                         case 'SDX_STD':
                             $sdx = Soundex::russell($value);
                             if ($sdx !== null) {
                                 $sdx = explode(':', $sdx);
                                 foreach ($sdx as $k => $v) {
                                     $sdx[$k] = "i_n.n_soundex_surn_std LIKE CONCAT('%', ?, '%')";
                                     $bind[] = $v;
                                 }
                                 $sql .= " AND (" . implode(' OR ', $sdx) . ")";
                             } else {
                                 // No phonetic content?  Use a substring match
                                 $sql .= " AND i_n.n_surn LIKE CONCAT('%', ?, '%')";
                                 $bind[] = $value;
                             }
                             break;
                         case 'SDX':
                             // SDX uses DM by default.
                         // SDX uses DM by default.
                         case 'SDX_DM':
                             $sdx = Soundex::daitchMokotoff($value);
                             if ($sdx !== null) {
                                 $sdx = explode(':', $sdx);
                                 foreach ($sdx as $k => $v) {
                                     $sdx[$k] = "i_n.n_soundex_surn_dm LIKE CONCAT('%', ?, '%')";
                                     $bind[] = $v;
                                 }
                                 $sql .= " AND (" . implode(' OR ', $sdx) . ")";
                                 break;
                             } else {
                                 // No phonetic content?  Use a substring match
                                 $sql .= " AND i_n.n_surn LIKE CONCAT('%', ?, '%')";
                                 $bind[] = $value;
                             }
                     }
                     break;
                 case 'NICK':
                 case '_MARNM':
                 case '_HEB':
                 case '_AKA':
                     $sql .= " AND i_n.n_type=? AND i_n.n_full LIKE CONCAT('%', ?, '%')";
                     $bind[] = $parts[1];
                     $bind[] = $value;
                     break;
             }
         } elseif ($parts[1] == 'DATE') {
             // *:DATE
             $date = new Date($value);
             if ($date->isOK()) {
                 $jd1 = $date->minimumJulianDay();
                 $jd2 = $date->maximumJulianDay();
                 if (!empty($this->plusminus[$i])) {
                     $adjd = $this->plusminus[$i] * 365;
                     $jd1 -= $adjd;
                     $jd2 += $adjd;
                 }
                 $sql .= " AND i_d.d_fact=? AND i_d.d_julianday1>=? AND i_d.d_julianday2<=?";
                 $bind[] = $parts[0];
                 $bind[] = $jd1;
                 $bind[] = $jd2;
             }
         } elseif ($parts[0] == 'FAMS' && $parts[2] == 'DATE') {
             // FAMS:*:DATE
             $date = new Date($value);
             if ($date->isOK()) {
                 $jd1 = $date->minimumJulianDay();
                 $jd2 = $date->maximumJulianDay();
                 if (!empty($this->plusminus[$i])) {
                     $adjd = $this->plusminus[$i] * 365;
                     $jd1 -= $adjd;
                     $jd2 += $adjd;
                 }
                 $sql .= " AND f_d.d_fact=? AND f_d.d_julianday1>=? AND f_d.d_julianday2<=?";
                 $bind[] = $parts[1];
                 $bind[] = $jd1;
                 $bind[] = $jd2;
             }
         } elseif ($parts[1] == 'PLAC') {
             // *:PLAC
             // SQL can only link a place to a person/family, not to an event.
             $sql .= " AND i_p.place LIKE CONCAT('%', ?, '%')";
             $bind[] = $value;
         } elseif ($parts[0] == 'FAMS' && $parts[2] == 'PLAC') {
             // FAMS:*:PLAC
             // SQL can only link a place to a person/family, not to an event.
             $sql .= " AND f_p.place LIKE CONCAT('%', ?, '%')";
             $bind[] = $value;
         } elseif ($parts[0] == 'FAMC' && $parts[2] == 'NAME') {
             $table = $parts[1] == 'HUSB' ? 'f_n' : 'm_n';
             // NAME:*
             switch ($parts[3]) {
                 case 'GIVN':
                     switch ($parts[4]) {
                         case 'EXACT':
                             $sql .= " AND {$table}.n_givn=?";
                             $bind[] = $value;
                             break;
                         case 'BEGINS':
                             $sql .= " AND {$table}.n_givn LIKE CONCAT(?, '%')";
                             $bind[] = $value;
                             break;
                         case 'CONTAINS':
                             $sql .= " AND {$table}.n_givn LIKE CONCAT('%', ?, '%')";
                             $bind[] = $value;
                             break;
                         case 'SDX_STD':
                             $sdx = Soundex::russell($value);
                             if ($sdx !== null) {
                                 $sdx = explode(':', $sdx);
                                 foreach ($sdx as $k => $v) {
                                     $sdx[$k] = "{$table}.n_soundex_givn_std LIKE CONCAT('%', ?, '%')";
                                     $bind[] = $v;
                                 }
                                 $sql .= ' AND (' . implode(' OR ', $sdx) . ')';
                             } else {
                                 // No phonetic content?  Use a substring match
                                 $sql .= " AND {$table}.n_givn = LIKE CONCAT('%', ?, '%')";
                                 $bind[] = $value;
                             }
                             break;
                         case 'SDX':
                             // SDX uses DM by default.
                         // SDX uses DM by default.
                         case 'SDX_DM':
                             $sdx = Soundex::daitchMokotoff($value);
                             if ($sdx !== null) {
                                 $sdx = explode(':', $sdx);
                                 foreach ($sdx as $k => $v) {
                                     $sdx[$k] = "{$table}.n_soundex_givn_dm LIKE CONCAT('%', ?, '%')";
                                     $bind[] = $v;
                                 }
                                 $sql .= ' AND (' . implode(' OR ', $sdx) . ')';
                                 break;
                             } else {
                                 // No phonetic content?  Use a substring match
                                 $sql .= " AND {$table}.n_givn = LIKE CONCAT('%', ?, '%')";
                                 $bind[] = $value;
                             }
                     }
                     break;
                 case 'SURN':
                     switch ($parts[4]) {
                         case 'EXACT':
                             $sql .= " AND {$table}.n_surname=?";
                             $bind[] = $value;
                             break;
                         case 'BEGINS':
                             $sql .= " AND {$table}.n_surname LIKE CONCAT(?, '%')";
                             $bind[] = $value;
                             break;
                         case 'CONTAINS':
                             $sql .= " AND {$table}.n_surname LIKE CONCAT('%', ?, '%')";
                             $bind[] = $value;
                             break;
                         case 'SDX_STD':
                             $sdx = Soundex::russell($value);
                             if ($sdx !== null) {
                                 $sdx = explode(':', $sdx);
                                 foreach ($sdx as $k => $v) {
                                     $sdx[$k] = "{$table}.n_soundex_surn_std LIKE CONCAT('%', ?, '%')";
                                     $bind[] = $v;
                                 }
                                 $sql .= ' AND (' . implode(' OR ', $sdx) . ')';
                             } else {
                                 // No phonetic content?  Use a substring match
                                 $sql .= " AND {$table}.n_surn = LIKE CONCAT('%', ?, '%')";
                                 $bind[] = $value;
                             }
                             break;
                         case 'SDX':
                             // SDX uses DM by default.
                         // SDX uses DM by default.
                         case 'SDX_DM':
                             $sdx = Soundex::daitchMokotoff($value);
                             if ($sdx !== null) {
                                 $sdx = explode(':', $sdx);
                                 foreach ($sdx as $k => $v) {
                                     $sdx[$k] = "{$table}.n_soundex_surn_dm LIKE CONCAT('%', ?, '%')";
                                     $bind[] = $v;
                                 }
                                 $sql .= ' AND (' . implode(' OR ', $sdx) . ')';
                             } else {
                                 // No phonetic content?  Use a substring match
                                 $sql .= " AND {$table}.n_surn = LIKE CONCAT('%', ?, '%')";
                                 $bind[] = $value;
                             }
                             break;
                     }
                     break;
             }
         } elseif ($parts[0] == 'FAMS') {
             // e.g. searches for occupation, religion, note, etc.
             $sql .= " AND fam.f_gedcom REGEXP CONCAT('\n[0-9] ', ?, '(.*\n[0-9] CONT)* [^\n]*', ?)";
             $bind[] = $parts[1];
             $bind[] = $value;
         } else {
             // e.g. searches for occupation, religion, note, etc.
             $sql .= " AND ind.i_gedcom REGEXP CONCAT('\n[0-9] ', ?, '(.*\n[0-9] CONT)* [^\n]*', ?)";
             $bind[] = $parts[0];
             $bind[] = $value;
         }
     }
     $rows = Database::prepare($sql)->execute($bind)->fetchAll();
     foreach ($rows as $row) {
         $person = Individual::getInstance($row->xref, $WT_TREE, $row->gedcom);
         // Check for XXXX:PLAC fields, which were only partially matched by SQL
         foreach ($this->fields as $n => $field) {
             if ($this->values[$n] && preg_match('/^(' . WT_REGEX_TAG . '):PLAC$/', $field, $match)) {
                 if (!preg_match('/\\n1 ' . $match[1] . '(\\n[2-9].*)*\\n2 PLAC .*' . preg_quote($this->values[$n], '/') . '/i', $person->getGedcom())) {
                     continue 2;
                 }
             }
         }
         $this->myindilist[] = $person;
     }
 }
 /**
  * Get significant information from this page, to allow other pages such as
  * charts and reports to initialise with the same records
  *
  * @return Individual
  */
 public function getSignificantIndividual()
 {
     global $WT_TREE;
     static $individual;
     // Only query the DB once.
     if (!$individual && $WT_TREE->getUserPreference(Auth::user(), 'rootid')) {
         $individual = Individual::getInstance($WT_TREE->getUserPreference(Auth::user(), 'rootid'), $WT_TREE);
     }
     if (!$individual && $WT_TREE->getUserPreference(Auth::user(), 'gedcomid')) {
         $individual = Individual::getInstance($WT_TREE->getUserPreference(Auth::user(), 'gedcomid'), $WT_TREE);
     }
     if (!$individual) {
         $individual = Individual::getInstance($WT_TREE->getPreference('PEDIGREE_ROOT_ID'), $WT_TREE);
     }
     if (!$individual) {
         $individual = Individual::getInstance(Database::prepare("SELECT MIN(i_id) FROM `##individuals` WHERE i_file=?")->execute(array($WT_TREE->getTreeId()))->fetchOne(), $WT_TREE);
     }
     if (!$individual) {
         // always return a record
         $individual = new Individual('I', '0 @I@ INDI', null, $WT_TREE);
     }
     return $individual;
 }
				<input
					class="form-control"
					data-autocomplete-type="INDI"
					dir="ltr"
					id="PEDIGREE_ROOT_ID"
					maxlength="20"
					name="PEDIGREE_ROOT_ID"
					type="text"
					value="<?php 
    echo $WT_TREE->getPreference('PEDIGREE_ROOT_ID');
    ?>
"
				>
				<span class="input-group-addon">
					<?php 
    $person = Individual::getInstance($WT_TREE->getPreference('PEDIGREE_ROOT_ID'), $WT_TREE);
    if ($person) {
        echo $person->getFullName(), ' ', $person->getLifeSpan();
    } else {
        echo I18N::translate('Unable to find record with ID');
    }
    ?>
				</span>
			</div>
			<p class="small text-muted">
				<?php 
    echo I18N::translate('This individual will be selected by default when viewing charts and reports.');
    ?>
			</p>
		</div>
	</div>
 /**
  * Get the current view of a record, allowing for pending changes
  *
  * @param string $xref
  * @param string $type
  *
  * @return string
  */
 public static function getLatestRecord($xref, $type)
 {
     global $WT_TREE;
     switch ($type) {
         case 'INDI':
             return Individual::getInstance($xref, $WT_TREE)->getGedcom();
         case 'FAM':
             return Family::getInstance($xref, $WT_TREE)->getGedcom();
         case 'SOUR':
             return Source::getInstance($xref, $WT_TREE)->getGedcom();
         case 'REPO':
             return Repository::getInstance($xref, $WT_TREE)->getGedcom();
         case 'OBJE':
             return Media::getInstance($xref, $WT_TREE)->getGedcom();
         case 'NOTE':
             return Note::getInstance($xref, $WT_TREE)->getGedcom();
         default:
             return GedcomRecord::getInstance($xref, $WT_TREE)->getGedcom();
     }
 }
 /**
  * Startup activity
  */
 public function __construct()
 {
     global $WT_TREE;
     parent::__construct();
     $this->setPageTitle(I18N::translate('Timeline'));
     $this->baseyear = (int) date('Y');
     $pids = Filter::getArray('pids', WT_REGEX_XREF);
     $remove = Filter::get('remove', WT_REGEX_XREF);
     foreach (array_unique($pids) as $pid) {
         if ($pid !== $remove) {
             $person = Individual::getInstance($pid, $WT_TREE);
             if ($person && $person->canShow()) {
                 $this->people[] = $person;
             }
         }
     }
     $this->pidlinks = '';
     foreach ($this->people as $indi) {
         // setup string of valid pids for links
         $this->pidlinks .= 'pids%5B%5D=' . $indi->getXref() . '&amp;';
         $bdate = $indi->getBirthDate();
         if ($bdate->isOK()) {
             $date = new GregorianDate($bdate->minimumJulianDay());
             $this->birthyears[$indi->getXref()] = $date->y;
             $this->birthmonths[$indi->getXref()] = max(1, $date->m);
             $this->birthdays[$indi->getXref()] = max(1, $date->d);
         }
         // find all the fact information
         $facts = $indi->getFacts();
         foreach ($indi->getSpouseFamilies() as $family) {
             foreach ($family->getFacts() as $fact) {
                 $facts[] = $fact;
             }
         }
         foreach ($facts as $event) {
             // get the fact type
             $fact = $event->getTag();
             if (!in_array($fact, $this->nonfacts)) {
                 // check for a date
                 $date = $event->getDate();
                 if ($date->isOK()) {
                     $date = new GregorianDate($date->minimumJulianDay());
                     $this->baseyear = min($this->baseyear, $date->y);
                     $this->topyear = max($this->topyear, $date->y);
                     if (!$indi->isDead()) {
                         $this->topyear = max($this->topyear, (int) date('Y'));
                     }
                     // do not add the same fact twice (prevents marriages from being added multiple times)
                     if (!in_array($event, $this->indifacts, true)) {
                         $this->indifacts[] = $event;
                     }
                 }
             }
         }
     }
     $scale = Filter::getInteger('scale', 0, 200);
     if ($scale === 0) {
         $this->scale = (int) (($this->topyear - $this->baseyear) / 20 * count($this->indifacts) / 4);
         if ($this->scale < 6) {
             $this->scale = 6;
         }
     } else {
         $this->scale = $scale;
     }
     if ($this->scale < 2) {
         $this->scale = 2;
     }
     $this->baseyear -= 5;
     $this->topyear += 5;
 }
 /**
  * Export the database in GEDCOM format
  *
  * @param Tree $tree Which tree to export
  * @param resource $gedout Handle to a writable stream
  * @param string[] $exportOptions Export options are as follows:
  *                                'privatize':    which Privacy rules apply? (none, visitor, user, manager)
  *                                'toANSI':       should the output be produced in ISO-8859-1 instead of UTF-8? (yes, no)
  *                                'path':         what constant should prefix all media file paths? (eg: media/  or c:\my pictures\my family
  *                                'slashes':      what folder separators apply to media file paths? (forward, backward)
  */
 public static function exportGedcom(Tree $tree, $gedout, $exportOptions)
 {
     switch ($exportOptions['privatize']) {
         case 'gedadmin':
             $access_level = Auth::PRIV_NONE;
             break;
         case 'user':
             $access_level = Auth::PRIV_USER;
             break;
         case 'visitor':
             $access_level = Auth::PRIV_PRIVATE;
             break;
         case 'none':
             $access_level = Auth::PRIV_HIDE;
             break;
     }
     $head = self::gedcomHeader($tree);
     if ($exportOptions['toANSI'] == 'yes') {
         $head = str_replace('UTF-8', 'ANSI', $head);
         $head = utf8_decode($head);
     }
     $head = self::reformatRecord($head);
     fwrite($gedout, $head);
     // Buffer the output. Lots of small fwrite() calls can be very slow when writing large gedcoms.
     $buffer = '';
     // Generate the OBJE/SOUR/REPO/NOTE records first, as their privacy calcualations involve
     // database queries, and we wish to avoid large gaps between queries due to MySQL connection timeouts.
     $tmp_gedcom = '';
     $rows = Database::prepare("SELECT m_id AS xref, m_gedcom AS gedcom" . " FROM `##media` WHERE m_file = :tree_id ORDER BY m_id")->execute(array('tree_id' => $tree->getTreeId()))->fetchAll();
     foreach ($rows as $row) {
         $rec = Media::getInstance($row->xref, $tree, $row->gedcom)->privatizeGedcom($access_level);
         $rec = self::convertMediaPath($rec, $exportOptions['path']);
         if ($exportOptions['toANSI'] === 'yes') {
             $rec = utf8_decode($rec);
         }
         $tmp_gedcom .= self::reformatRecord($rec);
     }
     $rows = Database::prepare("SELECT s_id AS xref, s_file AS gedcom_id, s_gedcom AS gedcom" . " FROM `##sources` WHERE s_file = :tree_id ORDER BY s_id")->execute(array('tree_id' => $tree->getTreeId()))->fetchAll();
     foreach ($rows as $row) {
         $rec = Source::getInstance($row->xref, $tree, $row->gedcom)->privatizeGedcom($access_level);
         if ($exportOptions['toANSI'] === 'yes') {
             $rec = utf8_decode($rec);
         }
         $tmp_gedcom .= self::reformatRecord($rec);
     }
     $rows = Database::prepare("SELECT o_type AS type, o_id AS xref, o_gedcom AS gedcom" . " FROM `##other` WHERE o_file = :tree_id AND o_type NOT IN ('HEAD', 'TRLR') ORDER BY o_id")->execute(array('tree_id' => $tree->getTreeId()))->fetchAll();
     foreach ($rows as $row) {
         switch ($row->type) {
             case 'NOTE':
                 $record = Note::getInstance($row->xref, $tree, $row->gedcom);
                 break;
             case 'REPO':
                 $record = Repository::getInstance($row->xref, $tree, $row->gedcom);
                 break;
             default:
                 $record = GedcomRecord::getInstance($row->xref, $tree, $row->gedcom);
                 break;
         }
         $rec = $record->privatizeGedcom($access_level);
         if ($exportOptions['toANSI'] === 'yes') {
             $rec = utf8_decode($rec);
         }
         $tmp_gedcom .= self::reformatRecord($rec);
     }
     $rows = Database::prepare("SELECT i_id AS xref, i_gedcom AS gedcom" . " FROM `##individuals` WHERE i_file = :tree_id ORDER BY i_id")->execute(array('tree_id' => $tree->getTreeId()))->fetchAll();
     foreach ($rows as $row) {
         $rec = Individual::getInstance($row->xref, $tree, $row->gedcom)->privatizeGedcom($access_level);
         if ($exportOptions['toANSI'] === 'yes') {
             $rec = utf8_decode($rec);
         }
         $buffer .= self::reformatRecord($rec);
         if (strlen($buffer) > 65536) {
             fwrite($gedout, $buffer);
             $buffer = '';
         }
     }
     $rows = Database::prepare("SELECT f_id AS xref, f_gedcom AS gedcom" . " FROM `##families` WHERE f_file = :tree_id ORDER BY f_id")->execute(array('tree_id' => $tree->getTreeId()))->fetchAll();
     foreach ($rows as $row) {
         $rec = Family::getInstance($row->xref, $tree, $row->gedcom)->privatizeGedcom($access_level);
         if ($exportOptions['toANSI'] === 'yes') {
             $rec = utf8_decode($rec);
         }
         $buffer .= self::reformatRecord($rec);
         if (strlen($buffer) > 65536) {
             fwrite($gedout, $buffer);
             $buffer = '';
         }
     }
     fwrite($gedout, $buffer);
     fwrite($gedout, $tmp_gedcom);
     fwrite($gedout, '0 TRLR' . WT_EOL);
 }
 /**
  * Fetch all individuals with a matching surname
  */
 private function loadIndividuals()
 {
     global $WT_TREE;
     $sql = "SELECT DISTINCT i_id AS xref, i_gedcom AS gedcom" . " FROM `##individuals`" . " JOIN `##name` ON (i_id=n_id AND i_file=n_file)" . " WHERE n_file = ?" . " AND n_type != ?" . " AND (n_surn = ? OR n_surname = ?";
     $args = array($WT_TREE->getTreeId(), '_MARNM', $this->surname, $this->surname);
     if ($this->soundex_std) {
         $sdx = Soundex::russell($this->surname);
         if ($sdx !== null) {
             foreach (explode(':', $sdx) as $value) {
                 $sql .= " OR n_soundex_surn_std LIKE CONCAT('%', ?, '%')";
                 $args[] = $value;
             }
         }
     }
     if ($this->soundex_dm) {
         $sdx = Soundex::daitchMokotoff($this->surname);
         if ($sdx !== null) {
             foreach (explode(':', $sdx) as $value) {
                 $sql .= " OR n_soundex_surn_dm LIKE CONCAT('%', ?, '%')";
                 $args[] = $value;
             }
         }
     }
     $sql .= ')';
     $rows = Database::prepare($sql)->execute($args)->fetchAll();
     $this->individuals = array();
     foreach ($rows as $row) {
         $this->individuals[] = Individual::getInstance($row->xref, $WT_TREE, $row->gedcom);
     }
     // Sort by birth date, oldest first
     usort($this->individuals, '\\Fisharebest\\Webtrees\\Individual::compareBirthDate');
 }
Exemple #20
0
        ?>
" onclick="window.close();">
		</p>
	</form>
	</div>
	<?php 
        break;
    case 'reorder_fams_update':
        $xref = Filter::post('xref', WT_REGEX_XREF);
        $order = Filter::post('order');
        $keep_chan = Filter::postBool('keep_chan');
        if (!Filter::checkCsrf()) {
            header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME . '?action=reorder_fams&xref=' . $xref);
            return;
        }
        $person = Individual::getInstance($xref, $WT_TREE);
        check_record_access($person);
        $controller->setPageTitle(I18N::translate('Re-order families'))->pageHeader();
        if (is_array($order)) {
            $gedcom = array('0 @' . $person->getXref() . '@ INDI');
            $facts = $person->getFacts();
            // Move families to the end of the record
            foreach ($order as $family => $num) {
                foreach ($facts as $n => $fact) {
                    if ($fact->getValue() === '@' . $family . '@') {
                        $facts[] = $fact;
                        unset($facts[$n]);
                        break;
                    }
                }
            }
 /**
  * Calculates number of generations a person has
  *
  * @param string $pid
  * @param int    $depth
  *
  * @return int
  */
 private function maxDescendencyGenerations($pid, $depth)
 {
     global $WT_TREE;
     if ($depth > $this->generations) {
         return $depth;
     }
     $person = Individual::getInstance($pid, $WT_TREE);
     if (is_null($person)) {
         return $depth;
     }
     $maxdc = $depth;
     foreach ($person->getSpouseFamilies() as $family) {
         foreach ($family->getChildren() as $child) {
             $dc = $this->maxDescendencyGenerations($child->getXref(), $depth + 1);
             if ($dc >= $this->generations) {
                 return $dc;
             }
             if ($dc > $maxdc) {
                 $maxdc = $dc;
             }
         }
     }
     $maxdc++;
     if ($maxdc == 1) {
         $maxdc++;
     }
     return $maxdc;
 }
    return array_map(function ($y) use($WT_TREE) {
        return Repository::getInstance($y, $WT_TREE);
    }, $tmp);
}, $repositories);
$sources = Database::prepare("SELECT GROUP_CONCAT(n_id) AS xrefs " . " FROM `##sources`" . " JOIN `##name` ON s_id = n_id AND s_file = n_file" . " WHERE s_file = :tree_id" . " GROUP BY n_full" . " HAVING COUNT(n_id) > 1")->execute(array('tree_id' => $WT_TREE->getTreeId()))->fetchAll();
$sources = array_map(function (\stdClass $x) use($WT_TREE) {
    $tmp = explode(',', $x->xrefs);
    return array_map(function ($y) use($WT_TREE) {
        return Source::getInstance($y, $WT_TREE);
    }, $tmp);
}, $sources);
$individuals = Database::prepare("SELECT DISTINCT GROUP_CONCAT(d_gid ORDER BY d_gid) AS xrefs" . " FROM `##dates` AS d" . " JOIN `##name` ON d_file = n_file AND d_gid = n_id" . " WHERE d_file = :tree_id AND d_fact IN ('BIRT', 'CHR', 'BAPM', 'DEAT', 'BURI')" . " GROUP BY d_day, d_month, d_year, d_type, d_fact, n_type, n_full" . " HAVING COUNT(DISTINCT d_gid) > 1")->execute(array('tree_id' => $WT_TREE->getTreeId()))->fetchAll();
$individuals = array_map(function (\stdClass $x) use($WT_TREE) {
    $tmp = explode(',', $x->xrefs);
    return array_map(function ($y) use($WT_TREE) {
        return Individual::getInstance($y, $WT_TREE);
    }, $tmp);
}, $individuals);
$families = Database::prepare("SELECT GROUP_CONCAT(f_id) AS xrefs " . " FROM `##families`" . " WHERE f_file = :tree_id" . " GROUP BY LEAST(f_husb, f_wife), GREATEST(f_husb, f_wife)" . " HAVING COUNT(f_id) > 1")->execute(array('tree_id' => $WT_TREE->getTreeId()))->fetchAll();
$families = array_map(function (\stdClass $x) use($WT_TREE) {
    $tmp = explode(',', $x->xrefs);
    return array_map(function ($y) use($WT_TREE) {
        return Family::getInstance($y, $WT_TREE);
    }, $tmp);
}, $families);
$media = Database::prepare("SELECT GROUP_CONCAT(m_id) AS xrefs " . " FROM `##media`" . " WHERE m_file = :tree_id" . " GROUP BY m_titl" . " HAVING COUNT(m_id) > 1")->execute(array('tree_id' => $WT_TREE->getTreeId()))->fetchAll();
$media = array_map(function (\stdClass $x) use($WT_TREE) {
    $tmp = explode(',', $x->xrefs);
    return array_map(function ($y) use($WT_TREE) {
        return Media::getInstance($y, $WT_TREE);
    }, $tmp);
Exemple #23
0
 /**
  * Find the ages between siblings.
  *
  * @param string   $type
  * @param string[] $params
  *
  * @return string
  */
 private function ageBetweenSiblingsQuery($type = 'list', $params = array())
 {
     if (isset($params[0])) {
         $total = (int) $params[0];
     } else {
         $total = 10;
     }
     if (isset($params[1])) {
         $one = $params[1];
     } else {
         $one = false;
     }
     // each family only once if true
     $rows = $this->runSql(" SELECT SQL_CACHE DISTINCT" . " link1.l_from AS family," . " link1.l_to AS ch1," . " link2.l_to AS ch2," . " child1.d_julianday2-child2.d_julianday2 AS age" . " FROM `##link` AS link1" . " LEFT JOIN `##dates` AS child1 ON child1.d_file = {$this->tree->getTreeId()}" . " LEFT JOIN `##dates` AS child2 ON child2.d_file = {$this->tree->getTreeId()}" . " LEFT JOIN `##link` AS link2 ON link2.l_file = {$this->tree->getTreeId()}" . " WHERE" . " link1.l_file = {$this->tree->getTreeId()} AND" . " link1.l_from = link2.l_from AND" . " link1.l_type = 'CHIL' AND" . " child1.d_gid = link1.l_to AND" . " child1.d_fact = 'BIRT' AND" . " link2.l_type = 'CHIL' AND" . " child2.d_gid = link2.l_to AND" . " child2.d_fact = 'BIRT' AND" . " child1.d_julianday2 > child2.d_julianday2 AND" . " child2.d_julianday2 <> 0 AND" . " child1.d_gid <> child2.d_gid" . " ORDER BY age DESC" . " LIMIT " . $total);
     if (!isset($rows[0])) {
         return '';
     }
     $top10 = array();
     $dist = array();
     foreach ($rows as $fam) {
         $family = Family::getInstance($fam['family'], $this->tree);
         $child1 = Individual::getInstance($fam['ch1'], $this->tree);
         $child2 = Individual::getInstance($fam['ch2'], $this->tree);
         if ($type == 'name') {
             if ($child1->canShow() && $child2->canShow()) {
                 $return = '<a href="' . $child2->getHtmlUrl() . '">' . $child2->getFullName() . '</a> ';
                 $return .= I18N::translate('and') . ' ';
                 $return .= '<a href="' . $child1->getHtmlUrl() . '">' . $child1->getFullName() . '</a>';
                 $return .= ' <a href="' . $family->getHtmlUrl() . '">[' . I18N::translate('View family') . ']</a>';
             } else {
                 $return = I18N::translate('This information is private and cannot be shown.');
             }
             return $return;
         }
         $age = $fam['age'];
         if ((int) ($age / 365.25) > 0) {
             $age = (int) ($age / 365.25) . 'y';
         } elseif ((int) ($age / 30.4375) > 0) {
             $age = (int) ($age / 30.4375) . 'm';
         } else {
             $age = $age . 'd';
         }
         $age = FunctionsDate::getAgeAtEvent($age, true);
         if ($type == 'age') {
             return $age;
         }
         if ($type == 'list') {
             if ($one && !in_array($fam['family'], $dist)) {
                 if ($child1->canShow() && $child2->canShow()) {
                     $return = "<li>";
                     $return .= "<a href=\"" . $child2->getHtmlUrl() . "\">" . $child2->getFullName() . "</a> ";
                     $return .= I18N::translate('and') . " ";
                     $return .= "<a href=\"" . $child1->getHtmlUrl() . "\">" . $child1->getFullName() . "</a>";
                     $return .= " (" . $age . ")";
                     $return .= " <a href=\"" . $family->getHtmlUrl() . "\">[" . I18N::translate('View family') . "]</a>";
                     $return .= '</li>';
                     $top10[] = $return;
                     $dist[] = $fam['family'];
                 }
             } elseif (!$one && $child1->canShow() && $child2->canShow()) {
                 $return = "<li>";
                 $return .= "<a href=\"" . $child2->getHtmlUrl() . "\">" . $child2->getFullName() . "</a> ";
                 $return .= I18N::translate('and') . " ";
                 $return .= "<a href=\"" . $child1->getHtmlUrl() . "\">" . $child1->getFullName() . "</a>";
                 $return .= " (" . $age . ")";
                 $return .= " <a href=\"" . $family->getHtmlUrl() . "\">[" . I18N::translate('View family') . "]</a>";
                 $return .= '</li>';
                 $top10[] = $return;
             }
         } else {
             if ($child1->canShow() && $child2->canShow()) {
                 $return = $child2->formatList('span', false, $child2->getFullName());
                 $return .= "<br>" . I18N::translate('and') . "<br>";
                 $return .= $child1->formatList('span', false, $child1->getFullName());
                 $return .= "<br><a href=\"" . $family->getHtmlUrl() . "\">[" . I18N::translate('View family') . "]</a>";
                 return $return;
             } else {
                 return I18N::translate('This information is private and cannot be shown.');
             }
         }
     }
     if ($type === 'list') {
         $top10 = implode('', $top10);
     }
     if (I18N::direction() === 'rtl') {
         $top10 = str_replace(array('[', ']', '(', ')', '+'), array('&rlm;[', '&rlm;]', '&rlm;(', '&rlm;)', '&rlm;+'), $top10);
     }
     if ($type === 'list') {
         return '<ul>' . $top10 . '</ul>';
     }
     return $top10;
 }
Exemple #24
0
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
namespace Fisharebest\Webtrees;

use Fisharebest\Webtrees\Census\CensusInterface;
use Fisharebest\Webtrees\Controller\SimpleController;
use Fisharebest\Webtrees\Module\CensusAssistantModule;
/** @var SimpleController $controller */
global $controller;
/** @var Tree $WT_TREE */
global $WT_TREE;
$xref = Filter::get('xref', WT_REGEX_XREF);
$census = Filter::get('census');
$head = Individual::getInstance($xref, $WT_TREE);
check_record_access($head);
$controller->restrictAccess(class_exists($census));
/** @var CensusInterface */
$census = new $census();
$controller->restrictAccess($census instanceof CensusInterface);
$date = new Date($census->censusDate());
$year = strip_tags($date->display(false, '%Y', false));
$headImg = '<i class="icon-button_head"></i>';
$controller->setPageTitle(I18N::translate('Create a new shared note using assistant'))->addInlineJavascript('jQuery("head").append(\'<link rel="stylesheet" href="' . WT_STATIC_URL . WT_MODULES_DIR . 'GEDFact_assistant/census/style.css" type="text/css">\');' . 'jQuery("#tblSample").on("click", ".icon-remove", function() { jQuery(this).closest("tr").remove(); });')->pageHeader();
?>

<h2>
	<?php 
echo $controller->getPageTitle();
?>
    /**
     * This is a general purpose hook, allowing modules to respond to routes
     * of the form module.php?mod=FOO&mod_action=BAR
     *
     * @param string $mod_action
     */
    public function modAction($mod_action)
    {
        global $controller, $WT_TREE;
        switch ($mod_action) {
            case 'treeview':
                $controller = new ChartController();
                $tv = new TreeView('tv');
                ob_start();
                $person = $controller->getSignificantIndividual();
                list($html, $js) = $tv->drawViewport($person, 4);
                $controller->setPageTitle(I18N::translate('Interactive tree of %s', $person->getFullName()))->pageHeader()->addExternalJavascript($this->js())->addExternalJavascript(WT_JQUERYUI_TOUCH_PUNCH_URL)->addInlineJavascript($js)->addInlineJavascript('
					if (document.createStyleSheet) {
						document.createStyleSheet("' . $this->css() . '"); // For Internet Explorer
					} else {
						jQuery("head").append(\'<link rel="stylesheet" type="text/css" href="' . $this->css() . '">\');
					}
				');
                echo $html;
                break;
            case 'getDetails':
                header('Content-Type: text/html; charset=UTF-8');
                $pid = Filter::get('pid', WT_REGEX_XREF);
                $i = Filter::get('instance');
                $tv = new TreeView($i);
                $individual = Individual::getInstance($pid, $WT_TREE);
                if ($individual) {
                    echo $tv->getDetails($individual);
                }
                break;
            case 'getPersons':
                header('Content-Type: text/html; charset=UTF-8');
                $q = Filter::get('q');
                $i = Filter::get('instance');
                $tv = new TreeView($i);
                echo $tv->getPersons($q);
                break;
            default:
                http_response_code(404);
                break;
        }
    }
 /**
  * Print the links to media objects
  *
  * @param string $factrec
  * @param int $level
  */
 public static function printMediaLinks($factrec, $level)
 {
     global $WT_TREE;
     $nlevel = $level + 1;
     if (preg_match_all("/{$level} OBJE @(.*)@/", $factrec, $omatch, PREG_SET_ORDER) == 0) {
         return;
     }
     $objectNum = 0;
     while ($objectNum < count($omatch)) {
         $media_id = $omatch[$objectNum][1];
         $media = Media::getInstance($media_id, $WT_TREE);
         if ($media) {
             if ($media->canShow()) {
                 if ($objectNum > 0) {
                     echo '<br class="media-separator" style="clear:both;">';
                 }
                 echo '<div class="media-display"><div class="media-display-image">';
                 echo $media->displayImage();
                 echo '</div>';
                 echo '<div class="media-display-title">';
                 echo '<a href="', $media->getHtmlUrl(), '">', $media->getFullName(), '</a>';
                 // NOTE: echo the notes of the media
                 echo '<p>';
                 echo FunctionsPrint::printFactNotes($media->getGedcom(), 1);
                 $ttype = preg_match("/" . ($nlevel + 1) . " TYPE (.*)/", $media->getGedcom(), $match);
                 if ($ttype > 0) {
                     $mediaType = GedcomTag::getFileFormTypeValue($match[1]);
                     echo '<p class="label">', I18N::translate('Type'), ': </span> <span class="field">', $mediaType, '</p>';
                 }
                 echo '</p>';
                 //-- print spouse name for marriage events
                 $ct = preg_match("/WT_SPOUSE: (.*)/", $factrec, $match);
                 if ($ct > 0) {
                     $spouse = Individual::getInstance($match[1], $media->getTree());
                     if ($spouse) {
                         echo '<a href="', $spouse->getHtmlUrl(), '">';
                         echo $spouse->getFullName();
                         echo '</a>';
                     }
                     $ct = preg_match("/WT_FAMILY_ID: (.*)/", $factrec, $match);
                     if ($ct > 0) {
                         $famid = trim($match[1]);
                         $family = Family::getInstance($famid, $spouse->getTree());
                         if ($family) {
                             if ($spouse) {
                                 echo " - ";
                             }
                             echo '<a href="', $family->getHtmlUrl(), '">', I18N::translate('View family'), '</a>';
                         }
                     }
                 }
                 echo FunctionsPrint::printFactNotes($media->getGedcom(), $nlevel);
                 echo self::printFactSources($media->getGedcom(), $nlevel);
                 echo '</div>';
                 //close div "media-display-title"
                 echo '</div>';
                 //close div "media-display"
             }
         } elseif (!$WT_TREE->getPreference('HIDE_GEDCOM_ERRORS')) {
             echo '<p class="ui-state-error">', $media_id, '</p>';
         }
         $objectNum++;
     }
 }
 /**
  * Search for individuals in a tree.
  *
  * @param Tree   $tree  Search this tree
  * @param string $query Search for this text
  *
  * @return string
  */
 private function search(Tree $tree, $query)
 {
     if (strlen($query) < 2) {
         return '';
     }
     $rows = Database::prepare("SELECT i_id AS xref, i_gedcom AS gedcom" . " FROM `##individuals`, `##name`" . " WHERE (i_id LIKE CONCAT('%', :query_1, '%') OR n_sort LIKE CONCAT('%', :query_2, '%'))" . " AND i_id = n_id AND i_file = n_file AND i_file = :tree_id" . " ORDER BY n_sort COLLATE :collation" . " LIMIT 50")->execute(array('query_1' => $query, 'query_2' => $query, 'tree_id' => $tree->getTreeId(), 'collation' => I18N::collation()))->fetchAll();
     $out = '<ul>';
     foreach ($rows as $row) {
         $person = Individual::getInstance($row->xref, $tree, $row->gedcom);
         if ($person->canShowName()) {
             $out .= '<li><a href="' . $person->getHtmlUrl() . '">' . $person->getSexImage() . ' ' . $person->getFullName() . ' ';
             if ($person->canShow()) {
                 $bd = $person->getLifeSpan();
                 if (!empty($bd)) {
                     $out .= ' (' . $bd . ')';
                 }
             }
             $out .= '</a></li>';
         }
     }
     $out .= '</ul>';
     return $out;
 }
 /**
  * Get individual object from PID
  * 
  * @param type $pid
  * @return object
  */
 protected function getPerson($pid)
 {
     return Individual::getInstance($pid, $this->tree);
 }
Exemple #29
0
 /**
  * Fetch a list of individuals with specified names
  *
  * To search for unknown names, use $surn="@N.N.", $salpha="@" or $galpha="@"
  * To search for names with no surnames, use $salpha=","
  *
  * @param Tree   $tree   only fetch individuals from this tree
  * @param string $surn   if set, only fetch people with this surname
  * @param string $salpha if set, only fetch surnames starting with this letter
  * @param string  $galpha if set, only fetch given names starting with this letter
  * @param bool   $marnm  if set, include married names
  * @param bool   $fams   if set, only fetch individuals with FAMS records
  *
  * @return Individual[]
  */
 public static function individuals(Tree $tree, $surn, $salpha, $galpha, $marnm, $fams)
 {
     $sql = "SELECT i_id AS xref, i_gedcom AS gedcom, n_full " . "FROM `##individuals` " . "JOIN `##name` ON n_id = i_id AND n_file = i_file " . ($fams ? "JOIN `##link` ON n_id = l_from AND n_file = l_file AND l_type = 'FAMS' " : "") . "WHERE n_file = :tree_id " . ($marnm ? "" : "AND n_type != '_MARNM'");
     $args = array('tree_id' => $tree->getTreeId());
     if ($surn) {
         $sql .= " AND n_surn COLLATE :collate_1 = :surn";
         $args['collate_1'] = I18N::collation();
         $args['surn'] = $surn;
     } elseif ($salpha === ',') {
         $sql .= " AND n_surn = ''";
     } elseif ($salpha === '@') {
         $sql .= " AND n_surn = '@N.N.'";
     } elseif ($salpha) {
         $sql .= " AND " . self::getInitialSql('n_surn', $salpha);
     } else {
         // All surnames
         $sql .= " AND n_surn NOT IN ('', '@N.N.')";
     }
     if ($galpha) {
         $sql .= " AND " . self::getInitialSql('n_givn', $galpha);
     }
     $sql .= " ORDER BY CASE n_surn WHEN '@N.N.' THEN 1 ELSE 0 END, n_surn COLLATE :collate_2, CASE n_givn WHEN '@P.N.' THEN 1 ELSE 0 END, n_givn COLLATE :collate_3";
     $args['collate_2'] = I18N::collation();
     $args['collate_3'] = I18N::collation();
     $list = array();
     $rows = Database::prepare($sql)->execute($args)->fetchAll();
     foreach ($rows as $row) {
         $person = Individual::getInstance($row->xref, $tree, $row->gedcom);
         // The name from the database may be private - check the filtered list...
         foreach ($person->getAllNames() as $n => $name) {
             if ($name['fullNN'] == $row->n_full) {
                 $person->setPrimaryName($n);
                 // We need to clone $person, as we may have multiple references to the
                 // same person in this list, and the "primary name" would otherwise
                 // be shared amongst all of them.
                 $list[] = clone $person;
                 break;
             }
         }
     }
     return $list;
 }
    /**
     * Render the Ajax response for the sortable table of Sosa individuals
     * @param AjaxController $controller
     */
    protected function renderSosaListIndi(AjaxController $controller)
    {
        global $WT_TREE;
        $listSosa = $this->sosa_provider->getSosaListAtGeneration($this->generation);
        $this->view_bag->set('has_sosa', false);
        if (count($listSosa) > 0) {
            $this->view_bag->set('has_sosa', true);
            $table_id = 'table-sosa-indi-' . Uuid::uuid4();
            $this->view_bag->set('table_id', $table_id);
            $controller->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)->addInlineJavascript('
                jQuery.fn.dataTableExt.oSort["unicode-asc"  ]=function(a,b) {return a.replace(/<[^<]*>/, "").localeCompare(b.replace(/<[^<]*>/, ""))};
				jQuery.fn.dataTableExt.oSort["unicode-desc" ]=function(a,b) {return b.replace(/<[^<]*>/, "").localeCompare(a.replace(/<[^<]*>/, ""))};
				jQuery.fn.dataTableExt.oSort["num-html-asc" ]=function(a,b) {a=parseFloat(a.replace(/<[^<]*>/, "")); b=parseFloat(b.replace(/<[^<]*>/, "")); return (a<b) ? -1 : (a>b ? 1 : 0);};
				jQuery.fn.dataTableExt.oSort["num-html-desc"]=function(a,b) {a=parseFloat(a.replace(/<[^<]*>/, "")); b=parseFloat(b.replace(/<[^<]*>/, "")); return (a>b) ? -1 : (a<b ? 1 : 0);};
            
                jQuery("#' . $table_id . '").dataTable( {
					dom: \'<"H"<"filtersH_' . $table_id . '">T<"dt-clear">pf<"dt-clear">irl>t<"F"pl<"dt-clear"><"filtersF_' . $table_id . '">>\',
					' . I18N::datatablesI18N() . ',
					jQueryUI: true,
					autoWidth: false,
					processing: true,
					retrieve: true,
					columns: [
						/* 0-Sosa */  		{ type: "num", class: "center" },
		                /* 1-ID */ 			{ visible: false },
		                /* 2-givn */ 		{ dataSort: 4,  class: "left"},
						/* 3-surn */ 		{ datasort: 5},
						/* 4-GIVN,SURN */ 	{ type: "unicode", visible: false},
						/* 5-SURN,GIVN */ 	{ type: "unicode", visible: false},
		                /* 6-Birth */		{ dataSort : 7 , class: "center"},
		                /* 7-SORT_BIRT */	{ visible : false},
		                /* 8-BIRT_PLAC */	{ type: "unicode", class: "center"},
		                /* PERSO Modify table to include IsSourced module */
		                /* 9-BIRT_SOUR */   { dataSort : 10, class: "center", visible: ' . (ModuleManager::getInstance()->isOperational(Constants::MODULE_MAJ_ISSOURCED_NAME) ? 'true' : 'false') . ' },
						/* 10-SORT_BIRTSC */{ visible : false},
		                /* 11-Death */		{ dataSort : 12 , class: "center"},
		                /* 12-SORT_DEAT */	{ visible : false},
		                /* 13-Age */		{ dataSort : 14 , class: "center"},
		                /* 14-AGE */		{ type: "num", visible: false},
		                /* 15-DEAT_PLAC */	{ type: "unicode", class: "center" },
		                /* 16-DEAT_SOUR */	{ dataSort : 17, class: "center", visible: ' . (ModuleManager::getInstance()->isOperational(Constants::MODULE_MAJ_ISSOURCED_NAME) ? 'true' : 'false') . ' },
		                /* 17-SORT_DEATSC */{ visible : false},
		                /* 18-SEX */		{ visible : false},
		                /* 19-BIRT */		{ visible : false},
		                /* 20-DEAT */		{ visible : false},
		                /* 21-TREE */		{ visible : false}
		                /* END PERSO */
					],
		            sorting: [[0,"asc"]],
					displayLength: 16,
					pagingType: "full_numbers"
			   });
            
				jQuery("#' . $table_id . '")
				/* Hide/show parents */
				.on("click", ".btn-toggle-parents", function() {
					jQuery(this).toggleClass("ui-state-active");
					jQuery(".parents", jQuery(this).closest("table").DataTable().rows().nodes()).slideToggle();
				})
				/* Hide/show statistics */
				.on("click", ".btn-toggle-statistics", function() {
					jQuery(this).toggleClass("ui-state-active");
					jQuery("#indi_list_table-charts_' . $table_id . '").slideToggle();
				})
				/* Filter buttons in table header */
				.on("click", "button[data-filter-column]", function() {
					var btn = jQuery(this);
					// De-activate the other buttons in this button group
					btn.siblings().removeClass("ui-state-active");
					// Apply (or clear) this filter
					var col = jQuery("#' . $table_id . '").DataTable().column(btn.data("filter-column"));
					if (btn.hasClass("ui-state-active")) {
						btn.removeClass("ui-state-active");
						col.search("").draw();
					} else {
						btn.addClass("ui-state-active");
						col.search(btn.data("filter-value")).draw();
					}
				});
            
				jQuery("#sosa-indi-list").css("visibility", "visible");
		
				jQuery("#btn-toggle-statistics-' . $table_id . '").click();
           ');
            $stats = new Stats($WT_TREE);
            // Bad data can cause "longest life" to be huge, blowing memory limits
            $max_age = min($WT_TREE->getPreference('MAX_ALIVE_AGE'), $stats->LongestLifeAge()) + 1;
            // Inititialise chart data
            $deat_by_age = array();
            for ($age = 0; $age <= $max_age; $age++) {
                $deat_by_age[$age] = '';
            }
            $birt_by_decade = array();
            $deat_by_decade = array();
            for ($year = 1550; $year < 2030; $year += 10) {
                $birt_by_decade[$year] = '';
                $deat_by_decade[$year] = '';
            }
            $unique_indis = array();
            // Don't double-count indis with multiple names.
            $nb_displayed = 0;
            Individual::load($WT_TREE, $listSosa);
            foreach ($listSosa as $sosa => $pid) {
                $person = Individual::getInstance($pid, $WT_TREE);
                if (!$person || !$person->canShowName()) {
                    unset($listSosa[$sosa]);
                    continue;
                }
                $nb_displayed++;
                if ($birth_dates = $person->getAllBirthDates()) {
                    if (FunctionsPrint::isDateWithinChartsRange($birth_dates[0]) && !isset($unique_indis[$person->getXref()])) {
                        $birt_by_decade[(int) ($birth_dates[0]->gregorianYear() / 10) * 10] .= $person->getSex();
                    }
                } else {
                    $birth_dates[0] = new Date('');
                }
                if ($death_dates = $person->getAllDeathDates()) {
                    if (FunctionsPrint::isDateWithinChartsRange($death_dates[0]) && !isset($unique_indis[$person->getXref()])) {
                        $deat_by_decade[(int) ($death_dates[0]->gregorianYear() / 10) * 10] .= $person->getSex();
                    }
                } else {
                    $death_dates[0] = new Date('');
                }
                $age = Date::getAge($birth_dates[0], $death_dates[0], 0);
                if (!isset($unique_indis[$person->getXref()]) && $age >= 0 && $age <= $max_age) {
                    $deat_by_age[$age] .= $person->getSex();
                }
                $listSosa[$sosa] = $person;
                $unique_indis[$person->getXref()] = true;
            }
            $this->view_bag->set('sosa_list', $listSosa);
            $this->view_bag->set('sosa_count', count($listSosa));
            $this->view_bag->set('sosa_theo', pow(2, $this->generation - 1));
            $this->view_bag->set('sosa_ratio', Functions::safeDivision($this->view_bag->get('sosa_count'), $this->view_bag->get('sosa_theo')));
            $this->view_bag->set('sosa_hidden', $this->view_bag->get('sosa_count') - $nb_displayed);
            $this->view_bag->set('chart_births', FunctionsPrintLists::chartByDecade($birt_by_decade, I18N::translate('Decade of birth')));
            $this->view_bag->set('chart_deaths', FunctionsPrintLists::chartByDecade($deat_by_decade, I18N::translate('Decade of death')));
            $this->view_bag->set('chart_ages', FunctionsPrintLists::chartByAge($deat_by_age, I18N::translate('Age related to death year')));
        }
        ViewFactory::make('SosaListIndi', $this, $controller, $this->view_bag)->render();
    }