A menu, to be added to the main application menu.

@param Tree $tree

@return Menu|null
14063
public function getMenu(Tree $tree): ?Menu
    {
        $submenus = $this->module_service->findByComponent(ModuleListInterface::class, $tree, Auth::user())
            ->map(static function (ModuleListInterface $module) use ($tree): ?Menu {
                return $module->listMenu($tree);
            })
            ->filter()
            ->sort(static function (Menu $x, Menu $y) {
                return $x->getLabel() <=> $y->getLabel();
            });

        if ($submenus->isEmpty()) {
            return null;
        }

        return new Menu(I18N::translate('Lists'), '#', 'menu-list', [], $submenus->all());
    }
1288916
public static function addHomePage(TreeInterface $siteTree = null)
    {
        $app = Application::getFacadeApplication();
        // creates the home page of the site
        $db = $app->make(Connection::class);

        $cParentID = 0;
        $uID = HOME_UID;

        $data = [
            'name' => HOME_NAME,
            'uID' => $uID,
        ];
        $cobj = parent::createCollection($data);
        $cID = $cobj->getCollectionID();

        if (!is_object($siteTree)) {
            $site = \Core::make('site')->getSite();
            $siteTree = $site->getSiteTreeObject();
        }
        $siteTreeID = $siteTree->getSiteTreeID();

        $v = [$cID, $siteTreeID, $cParentID, $uID, 'OVERRIDE', 1, (int) $cID, 0];
        $q = 'insert into Pages (cID, siteTreeID, cParentID, uID, cInheritPermissionsFrom, cOverrideTemplatePermissions, cInheritPermissionsFromCID, cDisplayOrder) values (?, ?, ?, ?, ?, ?, ?, ?)';
        $r = $db->prepare($q);
        $r->execute($v);
        if (!$siteTree->getSiteHomePageID()) {
            $siteTree->setSiteHomePageID($cID);
            $em = $app->make(EntityManagerInterface::class);
            $em->flush($siteTree);
        }
        $pc = self::getByID($cID, 'RECENT');

        return $pc;
    }
761218
public function getMenu(Tree $tree): ?Menu
    {
        if (Tree::all()->count() === 1 || Site::getPreference('ALLOW_CHANGE_GEDCOM') !== '1') {
            return new Menu(I18N::translate('Family tree'), route('tree-page', ['ged' => $tree->name()]), 'menu-tree');
        }

        $submenus = [];
        foreach (Tree::all() as $menu_tree) {
            if ($menu_tree->id() === $tree->id()) {
                $active = 'active ';
            } else {
                $active = '';
            }
            $submenus[] = new Menu(e($menu_tree->title()), route('tree-page', ['ged' => $menu_tree->name()]), $active . 'menu-tree-' . $menu_tree->id());
        }

        return new Menu(I18N::translate('Family trees'), '#', 'menu-tree', [], $submenus);
    }
1341009
public function treePage(Tree $tree): ResponseInterface
    {
        $has_blocks = DB::table('block')
            ->where('gedcom_id', '=', $tree->id())
            ->exists();

        if (!$has_blocks) {
            $this->checkDefaultTreeBlocksExist();

            // Copy the defaults
            (new Builder(DB::connection()))->from('block')->insertUsing(
                ['gedcom_id', 'location', 'block_order', 'module_name'],
                static function (Builder $query) use ($tree): void {
                    $query
                        ->select([DB::raw($tree->id()), 'location', 'block_order', 'module_name'])
                        ->from('block')
                        ->where('gedcom_id', '=', -1);
                }
            );
        }

        return $this->viewResponse('tree-page', [
            'main_blocks' => $this->treeBlocks($tree->id(), 'main'),
            'side_blocks' => $this->treeBlocks($tree->id(), 'side'),
            'title'       => e($tree->title()),
            'meta_robots' => 'index,follow',
        ]);
    }
942798
protected function menuSearchAndReplace(Tree $tree): ?Menu
    {
        if (Auth::isEditor($tree)) {
            return new Menu(I18N::translate('Search and replace'), route('search-replace', [
                'ged'    => $tree->name(),
                'action' => 'replace',
            ]), 'menu-search-replace');
        }

        return null;
    }
69094
public function importLocationsFromTree(Tree $tree): ResponseInterface
    {
        // Get all the places from the places table ...
        $places = DB::table('places AS p0')
            ->leftJoin('places AS p1', 'p1.p_id', '=', 'p0.p_parent_id')
            ->leftJoin('places AS p2', 'p2.p_id', '=', 'p1.p_parent_id')
            ->leftJoin('places AS p3', 'p3.p_id', '=', 'p2.p_parent_id')
            ->leftJoin('places AS p4', 'p4.p_id', '=', 'p3.p_parent_id')
            ->leftJoin('places AS p5', 'p5.p_id', '=', 'p4.p_parent_id')
            ->leftJoin('places AS p6', 'p6.p_id', '=', 'p5.p_parent_id')
            ->leftJoin('places AS p7', 'p7.p_id', '=', 'p6.p_parent_id')
            ->leftJoin('places AS p8', 'p8.p_id', '=', 'p7.p_parent_id')
            ->where('p0.p_file', '=', $tree->id())
            ->select([
                'p0.p_place AS place0',
                'p1.p_place AS place1',
                'p2.p_place AS place2',
                'p3.p_place AS place3',
                'p4.p_place AS place4',
                'p5.p_place AS place5',
                'p6.p_place AS place6',
                'p7.p_place AS place7',
                'p8.p_place AS place8',
            ])
            ->get()
            ->map(static function (stdClass $row): string {
                return implode(', ', array_filter((array) $row));
            });

        // ... and the placelocation table
        $locations = DB::table('placelocation AS p0')
            ->leftJoin('placelocation AS p1', 'p1.pl_id', '=', 'p0.pl_parent_id')
            ->leftJoin('placelocation AS p2', 'p2.pl_id', '=', 'p1.pl_parent_id')
            ->leftJoin('placelocation AS p3', 'p3.pl_id', '=', 'p2.pl_parent_id')
            ->leftJoin('placelocation AS p4', 'p4.pl_id', '=', 'p3.pl_parent_id')
            ->leftJoin('placelocation AS p5', 'p5.pl_id', '=', 'p4.pl_parent_id')
            ->leftJoin('placelocation AS p6', 'p6.pl_id', '=', 'p5.pl_parent_id')
            ->leftJoin('placelocation AS p7', 'p7.pl_id', '=', 'p6.pl_parent_id')
            ->leftJoin('placelocation AS p8', 'p8.pl_id', '=', 'p7.pl_parent_id')
            ->select([
                'p0.pl_id',
                'p0.pl_place AS place0',
                'p1.pl_place AS place1',
                'p2.pl_place AS place2',
                'p3.pl_place AS place3',
                'p4.pl_place AS place4',
                'p5.pl_place AS place5',
                'p6.pl_place AS place6',
                'p7.pl_place AS place7',
                'p8.pl_place AS place8',
            ])
            ->get()
            ->map(static function (stdClass $row): stdClass {
                $row->place = implode(', ', array_filter(array_slice((array) $row, 1)));

                return $row;
            })
            ->pluck('place', 'pl_id');

        // Compare the two ...
        $diff = $places->diff($locations);

        // ... and process the differences
        $inserted = 0;
        if ($diff->isNotEmpty()) {
            $nextRecordId = 1 + (int) DB::table('placelocation')->max('pl_id');

            foreach ($diff as $place) {
                // For Westminster, London, England, we must also create England and London, England
                $place_parts = explode(', ', $place);
                $count       = count($place_parts);

                try {
                    $parent_id = 0;
                    for ($i = $count - 1; $i >= 0; $i--) {
                        $parent   = implode(', ', array_slice($place_parts, $i));
                        $place_id = $locations->search($parent);

                        if ($place_id === false) {
                            DB::table('placelocation')->insert([
                                'pl_id'        => $nextRecordId,
                                'pl_parent_id' => $parent_id,
                                'pl_level'     => $count - $i,
                                'pl_place'     => $place_parts[$i],
                            ]);

                            $parent_id             = $nextRecordId;
                            $locations[$parent_id] = $parent;
                            $inserted++;
                            $nextRecordId++;
                        } else {
                            $parent_id = $place_id;
                        }
                    }
                } catch (QueryException $ex) {
                    // Duplicates are expected due to collation differences.  e.g. Quebec / Québec
                }
            }
        }

        FlashMessages::addMessage(I18N::plural('%s location has been imported.', '%s locations have been imported.', $inserted, I18N::number($inserted)), 'success');

        $url = route('map-data');

        return redirect($url);
    }
1257628
public function menuMyPages(?Tree $tree): ?Menu
    {
        if ($tree instanceof Tree && Auth::id()) {
            return new Menu(I18N::translate('My pages'), '#', 'menu-mymenu', [], array_filter([
                $this->menuMyPage($tree),
                $this->menuMyIndividualRecord($tree),
                $this->menuMyPedigree($tree),
                $this->menuMyAccount(),
                $this->menuControlPanel($tree),
                $this->menuChangeBlocks($tree),
            ]));
        }

        return null;
    }
473386
public function addChildAction(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $xref = $request->get('xref', '');

        $family = Family::getInstance($xref, $tree);

        Auth::checkFamilyAccess($family, true);

        $PEDI      = $request->get('PEDI', '');
        $keep_chan = (bool) $request->get('keep_chan');

        $this->glevels = $request->get('glevels', []);
        $this->tag     = $request->get('tag', []);
        $this->text    = $request->get('text', []);
        $this->islink  = $request->get('islink', []);

        $this->splitSource();
        $gedrec = '0 @@ INDI';
        $gedrec .= $this->addNewName($request, $tree);
        $gedrec .= $this->addNewSex($request);
        if (preg_match_all('/([A-Z0-9_]+)/', $tree->getPreference('QUICK_REQUIRED_FACTS'), $matches)) {
            foreach ($matches[1] as $match) {
                $gedrec .= $this->addNewFact($request, $tree, $match);
            }
        }
        $gedrec .= "\n" . GedcomCodePedi::createNewFamcPedi($PEDI, $xref);
        if ((bool) $request->get('SOUR_INDI')) {
            $gedrec = $this->handleUpdates($gedrec);
        } else {
            $gedrec = $this->updateRest($gedrec);
        }

        // Create the new child
        $new_child = $tree->createIndividual($gedrec);

        // Insert new child at the right place
        $done = false;
        foreach ($family->facts(['CHIL']) as $fact) {
            $old_child = $fact->target();
            if ($old_child instanceof Individual && Date::compare($new_child->getEstimatedBirthDate(), $old_child->getEstimatedBirthDate()) < 0) {
                // Insert before this child
                $family->updateFact($fact->id(), '1 CHIL @' . $new_child->xref() . "@\n" . $fact->gedcom(), !$keep_chan);
                $done = true;
                break;
            }
        }
        if (!$done) {
            // Append child at end
            $family->createFact('1 CHIL @' . $new_child->xref() . '@', !$keep_chan);
        }

        if ($request->get('goto') === 'new') {
            return redirect($new_child->url());
        }

        return redirect($family->url());
    }
435724
protected function addPermissions($tree)
    {
        $user = $this->getSession()->getUser();

        $section = MenuService::getSection(
            $this->getRequestParameter('extension'),
            $this->getRequestParameter('perspective'),
            $this->getRequestParameter('section')
        );

        $actions = $section->getActions();

        //then compute ACL for each node of the tree
        $treeKeys = array_keys($tree);
        if (isset($treeKeys[0]) && is_int($treeKeys[0])) {
            foreach ($tree as $index => $treeNode) {
                $tree[$index] = $this->computePermissions($actions, $user, $treeNode);
            }
        } else {
            $tree = $this->computePermissions($actions, $user, $tree);
        }

        return $tree;
    }
90969
public static function buildMenuFromArray( $tree, $contentDrop = NULL )
	{
		$list	= array();
		foreach( $tree['children'] as $child )
			$list[]	= self::buildItemWithChildrenFromArray( $child, 1, $contentDrop );
		return UI_HTML_Elements::unorderedList( $list, 1 );
	}
658058
function _buildTree(&$tree, &$element) {
        $tree[$element->name()] = $element;
        if ($element->superclass()) {
            $rootDoc = & $this->_doclet->rootDoc();
            $superclass = & $rootDoc->classNamed($element->superclass());
            if ($superclass) {
                $this->_buildTree($tree, $superclass);
            }
        }
    }
541559
public function addChildAction(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $xref = $request->get('xref', '');

        $individual = Individual::getInstance($xref, $tree);

        Auth::checkIndividualAccess($individual, true);

        $PEDI = $request->get('PEDI', '');

        $this->glevels = $request->get('glevels', []);
        $this->tag     = $request->get('tag', []);
        $this->text    = $request->get('text', []);
        $this->islink  = $request->get('islink', []);

        // Create a family
        if ($individual->sex() === 'F') {
            $gedcom = "0 @@ FAM\n1 WIFE @" . $individual->xref() . '@';
        } else {
            $gedcom = "0 @@ FAM\n1 HUSB @" . $individual->xref() . '@';
        }
        $family = $tree->createFamily($gedcom);

        // Link the parent to the family
        $individual->createFact('1 FAMS @' . $family->xref() . '@', true);

        // Create a child
        $this->splitSource(); // separate SOUR record from the rest

        $gedcom = '0 @@ INDI';
        $gedcom .= $this->addNewName($request, $tree);
        $gedcom .= $this->addNewSex($request);
        $gedcom .= "\n" . GedcomCodePedi::createNewFamcPedi($PEDI, $family->xref());
        if (preg_match_all('/([A-Z0-9_]+)/', $tree->getPreference('QUICK_REQUIRED_FACTS'), $matches)) {
            foreach ($matches[1] as $match) {
                $gedcom .= $this->addNewFact($request, $tree, $match);
            }
        }
        if ((bool) $request->get('SOUR_INDI')) {
            $gedcom = $this->handleUpdates($gedcom);
        } else {
            $gedcom = $this->updateRest($gedcom);
        }

        $child = $tree->createIndividual($gedcom);

        // Link the family to the child
        $family->createFact('1 CHIL @' . $child->xref() . '@', true);

        if ($request->get('goto') === 'new') {
            return redirect($child->url());
        }

        return redirect($individual->url());
    }
187203
private function getMenusSection()
    {
        $builder = new TreeBuilder();
        $node = $builder->root('menus');

        $node
            ->useAttributeAsKey('name')
            ->prototype('array')
                ->children()
                    ->scalarNode('label')->isRequired()->cannotBeEmpty()->end()
                    ->scalarNode('icon')->isRequired()->cannotBeEmpty()->end()
                    ->integerNode('position')->defaultValue(0)->end()
                    ->scalarNode('domain')->defaultValue('messages')->end()
                    ->scalarNode('route')->defaultNull()->end()
                    ->arrayNode('entries')
                        ->useAttributeAsKey('name')
                        ->prototype('array')
                            ->children()
                                ->scalarNode('route')->isRequired()->cannotBeEmpty()->end()
                                ->scalarNode('label')->isRequired()->cannotBeEmpty()->end()
                                ->scalarNode('resource')->isRequired()->cannotBeEmpty()->end()
                                ->integerNode('position')->defaultValue(0)->end()
                                ->scalarNode('domain')->defaultValue('messages')->end()
                            ->end()
                        ->end()
                    ->end()
                ->end()
            ->end()
        ;

        return $node;
    }
1228434
private function faqsExist(Tree $tree, string $language): bool
    {
        return DB::table('block')
            ->join('block_setting', 'block_setting.block_id', '=', 'block.block_id')
            ->where('module_name', '=', $this->name())
            ->where('setting_name', '=', 'languages')
            ->where(static function (Builder $query) use ($tree): void {
                $query
                    ->whereNull('gedcom_id')
                    ->orWhere('gedcom_id', '=', $tree->id());
            })
            ->select(['setting_value AS languages'])
            ->get()
            ->filter(static function (stdClass $faq) use ($language): bool {
                return $faq->languages === '' || in_array($language, explode(',', $faq->languages), true);
            })
            ->isNotEmpty();
    }
472614
public function index(Tree $tree = null): ResponseInterface
    {
        $multiple_tree_threshold = (int) Site::getPreference('MULTIPLE_TREE_THRESHOLD', self::MULTIPLE_TREE_THRESHOLD);
        $gedcom_files            = $this->gedcomFiles(WT_DATA_DIR);

        $all_trees = Tree::getAll();

        // On sites with hundreds or thousands of trees, this page becomes very large.
        // Just show the current tree, the default tree, and unimported trees
        if (count($all_trees) >= $multiple_tree_threshold) {
            $all_trees = array_filter($all_trees, static function (Tree $x) use ($tree): bool {
                return $x->getPreference('imported') === '0' || $tree->id() === $x->id() || $x->name() === Site::getPreference('DEFAULT_GEDCOM');
            });
        }

        $default_tree_name  = $this->generateNewTreeName();
        $default_tree_title = I18N::translate('My family tree');

        $title = I18N::translate('Manage family trees');

        return $this->viewResponse('admin/trees', [
            'all_trees'               => $all_trees,
            'default_tree_name'       => $default_tree_name,
            'default_tree_title'      => $default_tree_title,
            'gedcom_files'            => $gedcom_files,
            'multiple_tree_threshold' => $multiple_tree_threshold,
            'title'                   => $title,
        ]);
    }
696098
protected function menuSearchAdvanced(Tree $tree): Menu
    {
        return new Menu(I18N::translate('Advanced search'), route('search-advanced', ['ged' => $tree->name()]), 'menu-search-advanced', ['rel' => 'nofollow']);
    }
732126
private function treeOutput(TreeBase $tree, Menu $menu = null)
    {
        $items = [];

        foreach ($tree->getChildren() as $item) {
            $element = [];

            $input = [
                '#prefix'         => '<div class="tree-item clearfix">',
                '#type'           => 'textfield',
                '#attributes'     => ['class' => ['']],
                '#value'          => $item->getTitle(),
                '#theme_wrappers' => [],
                '#suffix'         => '<span class="fa fa-times"></span></div>',
            ];

            $element['data']            = drupal_render($input);
            $element['data-item-type']  = 'node';
            $element['data-item-id']    = $item->getNodeId();
            $element['data-mlid']       = $item->getId();

            if ($item->hasChildren()) {
                $elements = $this->treeOutput($item);
                $element['data'] .= drupal_render($elements);
            }

            $items[] = $element;
        }

        $build = [
            '#theme' => 'item_list',
            '#type'  => 'ol',
            '#items' => $items,
        ];

        if ($menu) {
            $build['#attributes'] = [
                'data-menu' => $menu->getName(),
                'class'     => ['sortable'],
            ];
            $build['#title'] = $menu->getTitle();

            // If tree has no children, add an empty element to allow drop.
            if (!$tree->hasChildren()) {
                $build['#items'] = [' '];
            }
        }

        return $build;
    }