/** * Computes descendent Lineage from a node. * Uses recursion to build the lineage tree * * @param LineageNode $node * @return LineageNode Computed lineage */ protected function buildLineage(LineageNode $node) { if ($node === null) { return; } $indi_surname = null; $indi_node = $node->getIndividual(); if ($indi_node) { if (count($node->getFamiliesNodes()) == 0) { $indiSpouseFamilies = $indi_node->getSpouseFamilies(); foreach ($indiSpouseFamilies as $indiSpouseFamily) { $node->addFamily($indiSpouseFamily); } } $dindi_node = new \MyArtJaub\Webtrees\Individual($indi_node); $indi_surname = $dindi_node->getUnprotectedPrimarySurname(); //Get the estimated birth place and put it in the place table $place = $dindi_node->getEstimatedBirthPlace(false); if ($place && strlen($place) > 0) { $place = trim($place); $node->getRootNode()->addPlace(new Place($place, $this->tree)); } //Tag the individual as used $this->used_indis[$indi_node->getXref()] = true; } foreach ($node->getFamiliesNodes() as $family) { $spouse_surname = null; if ($indi_node && ($spouse = $family->getSpouse($indi_node))) { $dspouse = new \MyArtJaub\Webtrees\Individual($spouse); $spouse_surname = $dspouse->getUnprotectedPrimarySurname(); } $children = $family->getChildren(); $nbChildren = 0; $nbNatural = 0; foreach ($children as $child) { $dchild = new \MyArtJaub\Webtrees\Individual($child); $child_surname = $dchild->getUnprotectedPrimarySurname(); if (!$dchild->isNewAddition()) { $nbChildren++; //If the root individual is the mother if ($indi_node && I18N::strcasecmp($indi_node->getSex(), 'F') == 0) { //Print only lineages of children with the same surname as their mother (supposing they are natural children) if (!$spouse || $spouse_surname && I18N::strcasecmp($child_surname, $spouse_surname) != 0) { if (I18N::strcasecmp($child_surname, $indi_surname) == 0) { $nbNatural++; $node_child = new LineageNode($child, $node->getRootNode()); $node_child = $this->buildLineage($node_child); if ($node_child) { $node->addChild($family, $node_child); } } } } else { //Print if the children does not bear the same name as his mother (and different from his father) if (strlen($child_surname) == 0 || strlen($indi_surname) == 0 || strlen($spouse_surname) == 0 || I18N::strcasecmp($child_surname, $indi_surname) == 0 || I18N::strcasecmp($child_surname, $spouse_surname) != 0) { $nbNatural++; $node_child = new LineageNode($child, $node->getRootNode()); $node_child = $this->buildLineage($node_child); if ($node_child) { $node->addChild($family, $node_child); } } else { $nbNatural++; $node_child = new LineageNode($child, $node->getRootNode(), $child_surname); if ($node_child) { $node->addChild($family, $node_child); } } } } } //Do not print other children if ($nbChildren - $nbNatural > 0) { $node->addChild($family, null); } } return $node; }
/** * Print a new fact box on details pages * * @param string $id the id of the person, family, source etc the fact will be added to * @param array $usedfacts an array of facts already used in this record * @param string $type the type of record INDI, FAM, SOUR etc */ public static function printAddNewFact($id, $usedfacts, $type) { global $WT_TREE; // -- Add from clipboard if (is_array(Session::get('clipboard'))) { $newRow = true; foreach (array_reverse(Session::get('clipboard'), true) as $fact_id => $fact) { if ($fact["type"] == $type || $fact["type"] == 'all') { if ($newRow) { $newRow = false; echo '<tr><td class="descriptionbox">'; echo I18N::translate('Add from clipboard'), '</td>'; echo '<td class="optionbox wrap"><form method="get" name="newFromClipboard" action="?" onsubmit="return false;">'; echo '<select id="newClipboardFact">'; } echo '<option value="', Filter::escapeHtml($fact_id), '">', GedcomTag::getLabel($fact['fact']); // TODO use the event class to store/parse the clipboard events if (preg_match('/^2 DATE (.+)/m', $fact['factrec'], $match)) { $tmp = new Date($match[1]); echo '; ', $tmp->minimumDate()->format('%Y'); } if (preg_match('/^2 PLAC ([^,\\n]+)/m', $fact['factrec'], $match)) { echo '; ', $match[1]; } echo '</option>'; } } if (!$newRow) { echo '</select>'; echo ' <input type="button" value="', I18N::translate('Add'), "\" onclick=\"return paste_fact('{$id}', '#newClipboardFact');\"> "; echo '</form></td></tr>', "\n"; } } // -- Add from pick list switch ($type) { case "INDI": $addfacts = preg_split("/[, ;:]+/", $WT_TREE->getPreference('INDI_FACTS_ADD'), -1, PREG_SPLIT_NO_EMPTY); $uniquefacts = preg_split("/[, ;:]+/", $WT_TREE->getPreference('INDI_FACTS_UNIQUE'), -1, PREG_SPLIT_NO_EMPTY); $quickfacts = preg_split("/[, ;:]+/", $WT_TREE->getPreference('INDI_FACTS_QUICK'), -1, PREG_SPLIT_NO_EMPTY); break; case "FAM": $addfacts = preg_split("/[, ;:]+/", $WT_TREE->getPreference('FAM_FACTS_ADD'), -1, PREG_SPLIT_NO_EMPTY); $uniquefacts = preg_split("/[, ;:]+/", $WT_TREE->getPreference('FAM_FACTS_UNIQUE'), -1, PREG_SPLIT_NO_EMPTY); $quickfacts = preg_split("/[, ;:]+/", $WT_TREE->getPreference('FAM_FACTS_QUICK'), -1, PREG_SPLIT_NO_EMPTY); break; case "SOUR": $addfacts = preg_split("/[, ;:]+/", $WT_TREE->getPreference('SOUR_FACTS_ADD'), -1, PREG_SPLIT_NO_EMPTY); $uniquefacts = preg_split("/[, ;:]+/", $WT_TREE->getPreference('SOUR_FACTS_UNIQUE'), -1, PREG_SPLIT_NO_EMPTY); $quickfacts = preg_split("/[, ;:]+/", $WT_TREE->getPreference('SOUR_FACTS_QUICK'), -1, PREG_SPLIT_NO_EMPTY); break; case "NOTE": $addfacts = preg_split("/[, ;:]+/", $WT_TREE->getPreference('NOTE_FACTS_ADD'), -1, PREG_SPLIT_NO_EMPTY); $uniquefacts = preg_split("/[, ;:]+/", $WT_TREE->getPreference('NOTE_FACTS_UNIQUE'), -1, PREG_SPLIT_NO_EMPTY); $quickfacts = preg_split("/[, ;:]+/", $WT_TREE->getPreference('NOTE_FACTS_QUICK'), -1, PREG_SPLIT_NO_EMPTY); break; case "REPO": $addfacts = preg_split("/[, ;:]+/", $WT_TREE->getPreference('REPO_FACTS_ADD'), -1, PREG_SPLIT_NO_EMPTY); $uniquefacts = preg_split("/[, ;:]+/", $WT_TREE->getPreference('REPO_FACTS_UNIQUE'), -1, PREG_SPLIT_NO_EMPTY); $quickfacts = preg_split("/[, ;:]+/", $WT_TREE->getPreference('REPO_FACTS_QUICK'), -1, PREG_SPLIT_NO_EMPTY); break; default: return; } $addfacts = array_merge(self::checkFactUnique($uniquefacts, $usedfacts, $type), $addfacts); $quickfacts = array_intersect($quickfacts, $addfacts); $translated_addfacts = array(); foreach ($addfacts as $addfact) { $translated_addfacts[$addfact] = GedcomTag::getLabel($addfact); } uasort($translated_addfacts, function ($x, $y) { return I18N::strcasecmp(I18N::translate($x), I18N::translate($y)); }); echo '<tr><td class="descriptionbox">'; echo I18N::translate('Fact or event'); echo '</td>'; echo '<td class="optionbox wrap">'; echo '<form method="get" name="newfactform" action="?" onsubmit="return false;">'; echo '<select id="newfact" name="newfact">'; echo '<option value="" disabled selected>' . I18N::translate('<select>') . '</option>'; foreach ($translated_addfacts as $fact => $fact_name) { echo '<option value="', $fact, '">', $fact_name, '</option>'; } if ($type == 'INDI' || $type == 'FAM') { echo '<option value="FACT">', I18N::translate('Custom fact'), '</option>'; echo '<option value="EVEN">', I18N::translate('Custom event'), '</option>'; } echo '</select>'; echo '<input type="button" value="', I18N::translate('Add'), '" onclick="add_record(\'' . $id . '\', \'newfact\');">'; echo '<span class="quickfacts">'; foreach ($quickfacts as $fact) { echo '<a href="#" onclick="add_new_record(\'' . $id . '\', \'' . $fact . '\');return false;">', GedcomTag::getLabel($fact), '</a>'; } echo '</span></form>'; echo '</td></tr>'; }
/** * Compare two tags, for sorting * * @param string $x * @param string $y * * @return int */ public static function tagSort($x, $y) { list($x1) = explode(':', $x . ':'); list($y1) = explode(':', $y . ':'); $tmp = I18N::strcasecmp(GedcomTag::getLabel($x1), GedcomTag::getLabel($y1)); if ($tmp) { return $tmp; } else { return I18N::strcasecmp(GedcomTag::getLabel($x), GedcomTag::getLabel($y)); } }
/** * Sort a list events for the today/upcoming blocks * * @param array $a * @param array $b * * @return int */ public static function eventSort($a, $b) { if ($a['jd'] == $b['jd']) { if ($a['anniv'] == $b['anniv']) { return I18N::strcasecmp($a['fact'], $b['fact']); } else { return $a['anniv'] - $b['anniv']; } } else { return $a['jd'] - $b['jd']; } }
/** * Themes menu. * * @return Menu|null */ public function menuThemes() { if ($this->tree && Site::getPreference('ALLOW_USER_THEMES') && $this->tree->getPreference('ALLOW_THEME_DROPDOWN')) { $submenus = array(); foreach (Theme::installedThemes() as $theme) { $class = 'menu-theme-' . $theme->themeId() . ($theme === $this ? ' active' : ''); $submenus[] = new Menu($theme->themeName(), '#', $class, array('onclick' => 'return false;', 'data-theme' => $theme->themeId())); } usort($submenus, function (Menu $x, Menu $y) { return I18N::strcasecmp($x->getLabel(), $y->getLabel()); }); $menu = new Menu(I18N::translate('Theme'), '#', 'menu-theme', array(), $submenus); return $menu; } else { return null; } }
if ($tag) { $all_tags[$tag] = GedcomTag::getLabel($tag); } } uasort($all_tags, '\\Fisharebest\\Webtrees\\I18N::strcasecmp'); $resns = Database::prepare("SELECT default_resn_id, tag_type, xref, resn" . " FROM `##default_resn`" . " LEFT JOIN `##name` ON (gedcom_id=n_file AND xref=n_id AND n_num=0)" . " WHERE gedcom_id=?" . " ORDER BY xref IS NULL, n_sort, xref, tag_type")->execute(array($WT_TREE->getTreeId()))->fetchAll(); foreach ($resns as $resn) { $resn->record = GedcomRecord::getInstance($resn->xref, $WT_TREE); if ($resn->tag_type) { $resn->tag_label = GedcomTag::getLabel($resn->tag_type); } else { $resn->tag_label = ''; } } usort($resns, function (\stdClass $x, \stdClass $y) { return I18N::strcasecmp($x->tag_label, $y->tag_label); }); // We have two fields in one $CALENDAR_FORMATS = explode('_and_', $WT_TREE->getPreference('CALENDAR_FORMAT') . '_and_'); // Split into separate fields $relatives_events = explode(',', $WT_TREE->getPreference('SHOW_RELATIVES_EVENTS')); switch (Filter::post('action')) { case 'privacy': foreach (Filter::postArray('delete', WT_REGEX_INTEGER) as $delete_resn) { Database::prepare("DELETE FROM `##default_resn` WHERE default_resn_id=?")->execute(array($delete_resn)); } $xrefs = Filter::postArray('xref', WT_REGEX_XREF); $tag_types = Filter::postArray('tag_type', WT_REGEX_TAG); $resns = Filter::postArray('resn'); foreach ($xrefs as $n => $xref) { $tag_type = $tag_types[$n];
/** * Scan the source code to find a list of all installed modules. * * During setup, new modules need a status of “enabled”. * In admin->modules, new modules need status of “disabled”. * * @param string $default_status * * @return AbstractModule[] */ public static function getInstalledModules($default_status) { $modules = array(); foreach (glob(WT_ROOT . WT_MODULES_DIR . '*/module.php') as $file) { try { $module = (include $file); if ($module instanceof AbstractModule) { $modules[$module->getName()] = $module; Database::prepare("INSERT IGNORE INTO `##module` (module_name, status, menu_order, sidebar_order, tab_order) VALUES (?, ?, ?, ?, ?)")->execute(array($module->getName(), $default_status, $module instanceof ModuleMenuInterface ? $module->defaultMenuOrder() : null, $module instanceof ModuleSidebarInterface ? $module->defaultSidebarOrder() : null, $module instanceof ModuleTabInterface ? $module->defaultTabOrder() : null)); // Set the default privcy for this module. Note that this also sets it for the // default family tree, with a gedcom_id of -1 if ($module instanceof ModuleMenuInterface) { Database::prepare("INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" . " SELECT ?, gedcom_id, 'menu', ?" . " FROM `##gedcom`")->execute(array($module->getName(), $module->defaultAccessLevel())); } if ($module instanceof ModuleSidebarInterface) { Database::prepare("INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" . " SELECT ?, gedcom_id, 'sidebar', ?" . " FROM `##gedcom`")->execute(array($module->getName(), $module->defaultAccessLevel())); } if ($module instanceof ModuleTabInterface) { Database::prepare("INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" . " SELECT ?, gedcom_id, 'tab', ?" . " FROM `##gedcom`")->execute(array($module->getName(), $module->defaultAccessLevel())); } if ($module instanceof ModuleBlockInterface) { Database::prepare("INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" . " SELECT ?, gedcom_id, 'block', ?" . " FROM `##gedcom`")->execute(array($module->getName(), $module->defaultAccessLevel())); } if ($module instanceof ModuleChartInterface) { Database::prepare("INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" . " SELECT ?, gedcom_id, 'chart', ?" . " FROM `##gedcom`")->execute(array($module->getName(), $module->defaultAccessLevel())); } if ($module instanceof ModuleReportInterface) { Database::prepare("INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" . " SELECT ?, gedcom_id, 'report', ?" . " FROM `##gedcom`")->execute(array($module->getName(), $module->defaultAccessLevel())); } if ($module instanceof ModuleThemeInterface) { Database::prepare("INSERT IGNORE INTO `##module_privacy` (module_name, gedcom_id, component, access_level)" . " SELECT ?, gedcom_id, 'theme', ?" . " FROM `##gedcom`")->execute(array($module->getName(), $module->defaultAccessLevel())); } } } catch (\Exception $ex) { // Old or invalid module? } } uasort($modules, function (AbstractModule $x, AbstractModule $y) { return I18N::strcasecmp($x->getTitle(), $y->getTitle()); }); return $modules; }
/** * Static helper function to sort an array of objects by name * Records whose names cannot be displayed are sorted at the end. * * @param GedcomRecord $x * @param GedcomRecord $y * * @return int */ public static function compare(GedcomRecord $x, GedcomRecord $y) { if ($x->canShowName()) { if ($y->canShowName()) { return I18N::strcasecmp($x->getSortName(), $y->getSortName()); } else { return -1; // only $y is private } } else { if ($y->canShowName()) { return 1; // only $x is private } else { return 0; // both $x and $y private } } }
use Fisharebest\Webtrees\Module\ModuleBlockInterface; use Fisharebest\Webtrees\Module\ModuleChartInterface; use Fisharebest\Webtrees\Module\ModuleConfigInterface; use Fisharebest\Webtrees\Module\ModuleMenuInterface; use Fisharebest\Webtrees\Module\ModuleReportInterface; use Fisharebest\Webtrees\Module\ModuleSidebarInterface; use Fisharebest\Webtrees\Module\ModuleTabInterface; use Fisharebest\Webtrees\Module\ModuleThemeInterface; define('WT_SCRIPT_NAME', 'admin_modules.php'); require 'includes/session.php'; $controller = new PageController(); $controller->restrictAccess(Auth::isAdmin())->setPageTitle(I18N::translate('Module administration')); $modules = Module::getInstalledModules('disabled'); $module_status = Database::prepare("SELECT module_name, status FROM `##module`")->fetchAssoc(); uasort($modules, function (AbstractModule $x, AbstractModule $y) { return I18N::strcasecmp($x->getTitle(), $y->getTitle()); }); if (Filter::post('action') === 'update_mods' && Filter::checkCsrf()) { foreach ($modules as $module) { $new_status = Filter::post('status-' . $module->getName(), '[01]'); if ($new_status !== null) { $new_status = $new_status ? 'enabled' : 'disabled'; $old_status = $module_status[$module->getName()]; if ($new_status !== $old_status) { Database::prepare("UPDATE `##module` SET status=? WHERE module_name=?")->execute(array($new_status, $module->getName())); if ($new_status === 'disabled') { FlashMessages::addMessage(I18N::translate('The module “%s” has been disabled.', $module->getTitle()), 'success'); } else { FlashMessages::addMessage(I18N::translate('The module “%s” has been enabled.', $module->getTitle()), 'success'); } }
/** * AdminConfig@jsonTasksList */ public function jsonTasksList() { global $WT_TREE; $controller = new JsonController(); $controller->restrictAccess(Auth::isAdmin()); // Generate an AJAX/JSON response for datatables to load a block of rows $search = Filter::postArray('search'); if ($search) { $search = $search['value']; } $start = Filter::postInteger('start'); $length = Filter::postInteger('length'); $order = Filter::postArray('order'); $order_by_name = false; foreach ($order as $key => &$value) { switch ($value['column']) { case 3: $order_by_name = true; unset($order[$key]); break; case 4: $value['column'] = 'majat_last_run'; break; case 4: $value['column'] = 'majat_last_result'; break; default: unset($order[$key]); } } $list = $this->provider->getFilteredTasksList($search, $order, $start, $length); if ($order_by_name) { usort($list, function (AbstractTask $a, AbstractTask $b) { return I18N::strcasecmp($a->getTitle(), $b->getTitle()); }); } $recordsFiltered = count($list); $recordsTotal = $this->provider->getTasksCount(); $data = array(); foreach ($list as $task) { $datum = array(); $datum[0] = ' <div class="btn-group"> <button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-expanded="false"> <i class="fa fa-pencil"></i><span class="caret"></span> </button> <ul class="dropdown-menu" role="menu"> <li> <a href="#" onclick="return set_admintask_status(\'' . $task->getName() . '\', ' . ($task->isEnabled() ? 'false' : 'true') . ');"> <i class="fa fa-fw ' . ($task->isEnabled() ? 'fa-times' : 'fa-check') . '"></i> ' . ($task->isEnabled() ? I18N::translate('Disable') : I18N::translate('Enable')) . ' </a> </li> <li> <a href="module.php?mod=' . $this->module->getName() . '&mod_action=Task@edit&task=' . $task->getName() . '"> <i class="fa fa-fw fa-pencil"></i> ' . I18N::translate('Edit') . ' </a> </li> </ul> </div>'; $datum[1] = $task->getName(); $datum[2] = $task->isEnabled() ? '<i class="fa fa-check"></i><span class="sr-only">' . I18N::translate('Enabled') . '</span>' : '<i class="fa fa-times"></i><span class="sr-only">' . I18N::translate('Disabled') . '</span>'; $datum[3] = $task->getTitle(); $date_format = str_replace('%', '', I18N::dateFormat()) . ' H:i:s'; $datum[4] = $task->getLastUpdated()->format($date_format); $datum[5] = $task->isLastRunSuccess() ? '<i class="fa fa-check"></i><span class="sr-only">' . I18N::translate('Yes') . '</span>' : '<i class="fa fa-times"></i><span class="sr-only">' . I18N::translate('No') . '</span>'; $dtF = new \DateTime('@0'); $dtT = new \DateTime('@' . $task->getFrequency() * 60); $datum[6] = $dtF->diff($dtT)->format(I18N::translate('%a d %h h %i m')); $datum[7] = $task->getRemainingOccurrences() > 0 ? I18N::number($task->getRemainingOccurrences()) : I18N::translate('Unlimited'); $datum[8] = $task->isRunning() ? '<i class="fa fa-cog fa-spin fa-fw"></i><span class="sr-only">' . I18N::translate('Running') . '</span>' : '<i class="fa fa-times"></i><span class="sr-only">' . I18N::translate('Not running') . '</span>'; if ($task->isEnabled() && !$task->isRunning()) { $datum[9] = ' <button id="bt_runtask_' . $task->getName() . '" class="btn btn-primary" href="#" onclick="return run_admintask(\'' . $task->getName() . '\')"> <div id="bt_runtasktext_' . $task->getName() . '"><i class="fa fa-cog fa-fw" ></i>' . I18N::translate('Run') . '</div> </button>'; } else { $datum[9] = ''; } $data[] = $datum; } $controller->pageHeader(); $controller->encode(array('draw' => Filter::getInteger('draw'), 'recordsTotal' => $recordsTotal, 'recordsFiltered' => $recordsFiltered, 'data' => $data)); }
/** * Get a list of all modules, enabled or not, which provide a specific function. * * We cannot currently use auto-loading for modules, as there may be user-defined * modules about which the auto-loader knows nothing. * * @param string $component The type of module, such as "tab", "report" or "menu" * * @return AbstractModule[] */ public static function getAllModulesByComponent($component) { $module_names = Database::prepare("SELECT SQL_CACHE module_name" . " FROM `##module`" . " ORDER BY CASE :component WHEN 'menu' THEN menu_order WHEN 'sidebar' THEN sidebar_order WHEN 'tab' THEN tab_order ELSE 0 END, module_name")->execute(array('component' => $component))->fetchOneColumn(); $array = array(); foreach ($module_names as $module_name) { $interface = '\\Fisharebest\\Webtrees\\Module\\Module' . ucfirst($component) . 'Interface'; $module = self::getModuleByName($module_name); if ($module instanceof $interface) { $array[$module_name] = $module; } } // The order of menus/sidebars/tabs is defined in the database. Others are sorted by name. if ($component !== 'menu' && $component !== 'sidebar' && $component !== 'tab') { uasort($array, function (AbstractModule $x, AbstractModule $y) { return I18N::strcasecmp($x->getTitle(), $y->getTitle()); }); } return $array; }