Get a the current access level for a module

@param Tree   $tree
@param string $interface

@return int
434387
final public function accessLevel(Tree $tree, string $interface): int
    {
        $access_levels = app('cache.array')
            ->rememberForever('module_privacy' . $tree->id(), static function () use ($tree): Collection {
                return DB::table('module_privacy')
                    ->where('gedcom_id', '=', $tree->id())
                    ->get();
            });

        $row = $access_levels->first(function (stdClass $row) use ($interface): bool {
            return $row->interface === $interface && $row->module_name === $this->name();
        });

        return $row ? (int) $row->access_level : $this->access_level;
    }
312861
public function step(ServerRequestInterface $request, ?Tree $tree): ResponseInterface
    {
        $step = $request->getQueryParams()['step'] ?? self::STEP_CHECK;

        switch ($step) {
            case self::STEP_CHECK:
                return $this->wizardStepCheck();

            case self::STEP_PREPARE:
                return $this->wizardStepPrepare();

            case self::STEP_PENDING:
                return $this->wizardStepPending();

            case self::STEP_EXPORT:
                if ($tree === null) {
                    throw new RuntimeException('Exporting a non-existant tree?');
                }

                return $this->wizardStepExport($tree);

            case self::STEP_DOWNLOAD:
                return $this->wizardStepDownload();

            case self::STEP_UNZIP:
                return $this->wizardStepUnzip();

            case self::STEP_COPY:
                return $this->wizardStepCopy();

            case self::STEP_CLEANUP:
                return $this->wizardStepCleanup();
        }

        throw new NotFoundHttpException();
    }
275382
public function getShowListAction(Tree $tree): ResponseInterface
    {
        $stories = DB::table('block')
            ->where('module_name', '=', $this->name())
            ->where('gedcom_id', '=', $tree->id())
            ->get()
            ->map(function (stdClass $story) use ($tree): stdClass {
                $block_id = (int) $story->block_id;

                $story->individual = Individual::getInstance($story->xref, $tree);
                $story->title      = $this->getBlockSetting($block_id, 'title');
                $story->languages  = $this->getBlockSetting($block_id, 'languages');

                return $story;
            })->filter(static function (stdClass $story): bool {
                // Filter non-existant and private individuals.
                return $story->individual instanceof Individual && $story->individual->canShow();
            })->filter(static function (stdClass $story): bool {
                // Filter foreign languages.
                return $story->languages === '' || in_array(WT_LOCALE, explode(',', $story->languages), true);
            });

        return $this->viewResponse('modules/stories/list', [
            'stories' => $stories,
            'title'   => $this->title(),
        ]);
    }
5144
public function synchronize(Tree $tree): ResponseInterface
    {
        $url = route('admin-trees', ['ged' => $tree->name()]);

        $gedcom_files = $this->gedcomFiles(WT_DATA_DIR);

        foreach ($gedcom_files as $gedcom_file) {
            // Only import files that have changed
            $filemtime = (string) filemtime(WT_DATA_DIR . $gedcom_file);

            $tree = Tree::findByName($gedcom_file) ?? Tree::create($gedcom_file, $gedcom_file);

            if ($tree->getPreference('filemtime') !== $filemtime) {
                $stream = app(StreamFactoryInterface::class)->createStreamFromFile(WT_DATA_DIR . $gedcom_file);
                $tree->importGedcomFile($stream, $gedcom_file);
                $tree->setPreference('filemtime', $filemtime);

                FlashMessages::addMessage(I18N::translate('The GEDCOM file “%s” has been imported.', e($gedcom_file)), 'success');
            }
        }

        foreach (Tree::getAll() as $tree) {
            if (!in_array($tree->name(), $gedcom_files, true)) {
                FlashMessages::addMessage(I18N::translate('The family tree “%s” has been deleted.', e($tree->title())), 'success');
                $tree->delete();
            }
        }

        return redirect($url);
    }
639647
function _displayTree($tree, $parent = NULL) {
        $outputList = TRUE;
        foreach ($tree as $name => $element) {
            if ($element->superclass() == $parent) {
                if ($outputList)
                    echo "<ul>\n";
                echo "<li><a href=\"", $this->_asURL($element), "\">", $element->qualifiedName(), "</a>";
                $this->_displayTree($tree, $name);
                echo "</li>\n";
                $outputList = FALSE;
            }
        }
        if (!$outputList)
            echo "</ul>\n";
    }
883295
public function tab(ServerRequestInterface $request, Tree $tree, UserInterface $user): ResponseInterface
    {
        $xref        = $request->get('xref', '');
        $record      = Individual::getInstance($xref, $tree);
        $module_name = $request->get('module');
        $module      = $this->module_service->findByName($module_name);

        Auth::checkIndividualAccess($record);
        Auth::checkComponentAccess($module, 'tab', $tree, $user);

        $layout = view('layouts/ajax', [
            'content' => $module->getTabContent($record),
        ]);

        return response($layout);
    }
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;
    }
525455
public function getShowAction(Tree $tree): ResponseInterface
    {
        return $this->viewResponse('modules/clippings/show', [
            'records' => $this->allRecordsInCart($tree),
            'title'   => I18N::translate('Family tree clippings cart'),
            'tree'    => $tree,
        ]);
    }
1012939
public function getEmptyAction(Tree $tree): ResponseInterface
    {
        $cart                = Session::get('cart', []);
        $cart[$tree->name()] = [];
        Session::put('cart', $cart);

        $url = route('module', [
            'module' => $this->name(),
            'action' => 'Show',
            'ged'    => $tree->name(),
        ]);

        return redirect($url);
    }
1078242
public function postDeleteNewsAction(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $news_id = $request->get('news_id');

        if (!Auth::isManager($tree)) {
            throw new AccessDeniedHttpException();
        }

        DB::table('news')
            ->where('news_id', '=', $news_id)
            ->where('gedcom_id', '=', $tree->id())
            ->delete();

        $url = route('tree-page', [
            'ged' => $tree->name(),
        ]);

        return redirect($url);
    }
1008434
private function findBegin(array &$tree): int {
        $base  = 1;
        $found      = false;
        while (!$found) {
            // 步进
            $base++;

            // 如有使用跳过
            if (isset($this->begin_used[$base])) {
                continue;
            }

            // 查找是否符合条件
            foreach ($tree as $child_code => $child_tree) {
                if (isset($this->check[$this->getState($base, $child_code)])) {
                    continue 2;
                }
            }

            $found = true;
        }
        return $base;
    }
706245
public function addSpouseAction(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $xref = $request->get('xref', '');

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

        Auth::checkIndividualAccess($individual, true);

        $sex = $request->get('SEX', 'U');

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

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

        $fam_gedcom = '';
        if (preg_match_all('/([A-Z0-9_]+)/', $tree->getPreference('QUICK_REQUIRED_FAMFACTS'), $matches)) {
            foreach ($matches[1] as $match) {
                $fam_gedcom .= $this->addNewFact($request, $tree, $match);
            }
        }
        if ((bool) $request->get('SOUR_FAM')) {
            $fam_gedcom = $this->handleUpdates($fam_gedcom);
        } else {
            $fam_gedcom = $this->updateRest($fam_gedcom);
        }

        // Create the new spouse
        $spouse = $tree->createIndividual($indi_gedcom);
        // Create a new family
        if ($sex === 'F') {
            $family = $tree->createFamily("0 @@ FAM\n1 WIFE @" . $spouse->xref() . "@\n1 HUSB @" . $individual->xref() . '@' . $fam_gedcom);
        } else {
            $family = $tree->createFamily("0 @@ FAM\n1 HUSB @" . $spouse->xref() . "@\n1 WIFE @" . $individual->xref() . '@' . $fam_gedcom);
        }
        // Link the spouses to the family
        $spouse->createFact('1 FAMS @' . $family->xref() . '@', true);
        $individual->createFact('1 FAMS @' . $family->xref() . '@', true);

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

        return redirect($individual->url());
    }
1161283
public function check(Tree $tree): ResponseInterface
    {
        // We need to work with raw GEDCOM data, as we are looking for errors
        // which may prevent the GedcomRecord objects from working.

        $q1 = DB::table('individuals')
            ->where('i_file', '=', $tree->id())
            ->select(['i_id AS xref', 'i_gedcom AS gedcom', DB::raw("'INDI' AS type")]);
        $q2 = DB::table('families')
            ->where('f_file', '=', $tree->id())
            ->select(['f_id AS xref', 'f_gedcom AS gedcom', DB::raw("'FAM' AS type")]);
        $q3 = DB::table('media')
            ->where('m_file', '=', $tree->id())
            ->select(['m_id AS xref', 'm_gedcom AS gedcom', DB::raw("'OBJE' AS type")]);
        $q4 = DB::table('sources')
            ->where('s_file', '=', $tree->id())
            ->select(['s_id AS xref', 's_gedcom AS gedcom', DB::raw("'SOUR' AS type")]);
        $q5 = DB::table('other')
            ->where('o_file', '=', $tree->id())
            ->whereNotIn('o_type', ['HEAD', 'TRLR'])
            ->select(['o_id AS xref', 'o_gedcom AS gedcom', 'o_type']);
        $q6 = DB::table('change')
            ->where('gedcom_id', '=', $tree->id())
            ->where('status', '=', 'pending')
            ->orderBy('change_id')
            ->select(['xref', 'new_gedcom AS gedcom', DB::raw("'' AS type")]);

        $rows = $q1
            ->unionAll($q2)
            ->unionAll($q3)
            ->unionAll($q4)
            ->unionAll($q5)
            ->unionAll($q6)
            ->get()
            ->map(static function (stdClass $row): stdClass {
                // Extract type for pending record
                if ($row->type === '' && preg_match('/^0 @[^@]*@ ([_A-Z0-9]+)/', $row->gedcom, $match)) {
                    $row->type = $match[1];
                }

                return $row;
            });

        $records = [];

        foreach ($rows as $row) {
            if ($row->gedcom !== '') {
                // existing or updated record
                $records[$row->xref] = $row;
            } else {
                // deleted record
                unset($records[$row->xref]);
            }
        }

        // LOOK FOR BROKEN LINKS
        $XREF_LINKS = [
            'NOTE'          => 'NOTE',
            'SOUR'          => 'SOUR',
            'REPO'          => 'REPO',
            'OBJE'          => 'OBJE',
            'SUBM'          => 'SUBM',
            'FAMC'          => 'FAM',
            'FAMS'          => 'FAM',
            //'ADOP'=>'FAM', // Need to handle this case specially. We may have both ADOP and FAMC links to the same FAM, but only store one.
            'HUSB'          => 'INDI',
            'WIFE'          => 'INDI',
            'CHIL'          => 'INDI',
            'ASSO'          => 'INDI',
            '_ASSO'         => 'INDI',
            // A webtrees extension
            'ALIA'          => 'INDI',
            'AUTH'          => 'INDI',
            // A webtrees extension
            'ANCI'          => 'SUBM',
            'DESI'          => 'SUBM',
            '_WT_OBJE_SORT' => 'OBJE',
            '_LOC'          => '_LOC',
        ];

        $RECORD_LINKS = [
            'INDI' => [
                'NOTE',
                'OBJE',
                'SOUR',
                'SUBM',
                'ASSO',
                '_ASSO',
                'FAMC',
                'FAMS',
                'ALIA',
                '_WT_OBJE_SORT',
                '_LOC',
            ],
            'FAM'  => [
                'NOTE',
                'OBJE',
                'SOUR',
                'SUBM',
                'ASSO',
                '_ASSO',
                'HUSB',
                'WIFE',
                'CHIL',
                '_LOC',
            ],
            'SOUR' => [
                'NOTE',
                'OBJE',
                'REPO',
                'AUTH',
            ],
            'REPO' => ['NOTE'],
            'OBJE' => ['NOTE'],
            // The spec also allows SOUR, but we treat this as a warning
            'NOTE' => [],
            // The spec also allows SOUR, but we treat this as a warning
            'SUBM' => [
                'NOTE',
                'OBJE',
            ],
            'SUBN' => ['SUBM'],
            '_LOC' => [
                'SOUR',
                'OBJE',
                '_LOC',
                'NOTE',
            ],
        ];

        $errors   = [];
        $warnings = [];

        // Generate lists of all links
        $all_links   = [];
        $upper_links = [];
        foreach ($records as $record) {
            $all_links[$record->xref]               = [];
            $upper_links[strtoupper($record->xref)] = $record->xref;
            preg_match_all('/\n\d (' . Gedcom::REGEX_TAG . ') @([^#@\n][^\n@]*)@/', $record->gedcom, $matches, PREG_SET_ORDER);
            foreach ($matches as $match) {
                $all_links[$record->xref][$match[2]] = $match[1];
            }
        }

        foreach ($all_links as $xref1 => $links) {
            $type1 = $records[$xref1]->type;
            foreach ($links as $xref2 => $type2) {
                $type3 = isset($records[$xref2]) ? $records[$xref2]->type : '';
                if (!array_key_exists($xref2, $all_links)) {
                    if (array_key_exists(strtoupper($xref2), $upper_links)) {
                        $warnings[] =
                            $this->checkLinkMessage($tree, $type1, $xref1, $type2, $xref2) . ' ' .
                            /* I18N: placeholders are GEDCOM XREFs, such as R123 */
                            I18N::translate('%1$s does not exist. Did you mean %2$s?', $this->checkLink($tree, $xref2), $this->checkLink($tree, $upper_links[strtoupper($xref2)]));
                    } else {
                        /* I18N: placeholders are GEDCOM XREFs, such as R123 */
                        $errors[] = $this->checkLinkMessage($tree, $type1, $xref1, $type2, $xref2) . ' ' . I18N::translate('%1$s does not exist.', $this->checkLink($tree, $xref2));
                    }
                } elseif ($type2 === 'SOUR' && $type1 === 'NOTE') {
                    // Notes are intended to add explanations and comments to other records. They should not have their own sources.
                } elseif ($type2 === 'SOUR' && $type1 === 'OBJE') {
                    // Media objects are intended to illustrate other records, facts, and source/citations. They should not have their own sources.
                } elseif ($type2 === 'OBJE' && $type1 === 'REPO') {
                    $warnings[] =
                        $this->checkLinkMessage($tree, $type1, $xref1, $type2, $xref2) .
                        ' ' .
                        I18N::translate('This type of link is not allowed here.');
                } elseif (!array_key_exists($type1, $RECORD_LINKS) || !in_array($type2, $RECORD_LINKS[$type1], true) || !array_key_exists($type2, $XREF_LINKS)) {
                    $errors[] =
                        $this->checkLinkMessage($tree, $type1, $xref1, $type2, $xref2) .
                        ' ' .
                        I18N::translate('This type of link is not allowed here.');
                } elseif ($XREF_LINKS[$type2] !== $type3) {
                    // Target XREF does exist - but is invalid
                    $errors[] =
                        $this->checkLinkMessage($tree, $type1, $xref1, $type2, $xref2) . ' ' .
                        /* I18N: %1$s is an internal ID number such as R123. %2$s and %3$s are record types, such as INDI or SOUR */
                        I18N::translate('%1$s is a %2$s but a %3$s is expected.', $this->checkLink($tree, $xref2), $this->formatType($type3), $this->formatType($type2));
                } elseif (
                    $type2 === 'FAMC' && (!array_key_exists($xref1, $all_links[$xref2]) || $all_links[$xref2][$xref1] !== 'CHIL') ||
                    $type2 === 'FAMS' && (!array_key_exists($xref1, $all_links[$xref2]) || $all_links[$xref2][$xref1] !== 'HUSB' && $all_links[$xref2][$xref1] !== 'WIFE') ||
                    $type2 === 'CHIL' && (!array_key_exists($xref1, $all_links[$xref2]) || $all_links[$xref2][$xref1] !== 'FAMC') ||
                    $type2 === 'HUSB' && (!array_key_exists($xref1, $all_links[$xref2]) || $all_links[$xref2][$xref1] !== 'FAMS') ||
                    $type2 === 'WIFE' && (!array_key_exists($xref1, $all_links[$xref2]) || $all_links[$xref2][$xref1] !== 'FAMS')
                ) {
                    /* I18N: %1$s and %2$s are internal ID numbers such as R123 */
                    $errors[] = $this->checkLinkMessage($tree, $type1, $xref1, $type2, $xref2) . ' ' . I18N::translate('%1$s does not have a link back to %2$s.', $this->checkLink($tree, $xref2), $this->checkLink($tree, $xref1));
                }
            }
        }

        $title = I18N::translate('Check for errors') . ' — ' . e($tree->title());

        return $this->viewResponse('admin/trees-check', [
            'errors'   => $errors,
            'title'    => $title,
            'tree'     => $tree,
            'warnings' => $warnings,
        ]);
    }
1071360
protected function _isChild( MW_Tree_Node_Interface $node, MW_Tree_Node_Interface $parent )
	{
		return $node->__get('left') > $parent->__get('left') && $node->__get('right') < $parent->__get('right');
	}
1185385
public function getAdminEditAction(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $this->layout = 'layouts/administration';

        $block_id = (int) $request->get('block_id');

        if ($block_id === 0) {
            // Creating a new faq
            $header  = '';
            $faqbody = '';

            $block_order = 1 + (int) DB::table('block')
                    ->where('module_name', '=', $this->name())
                    ->max('block_order');

            $languages = [];

            $title = I18N::translate('Add an FAQ');
        } else {
            // Editing an existing faq
            $header  = $this->getBlockSetting($block_id, 'header');
            $faqbody = $this->getBlockSetting($block_id, 'faqbody');

            $block_order = DB::table('block')
                ->where('block_id', '=', $block_id)
                ->value('block_order');

            $languages = explode(',', $this->getBlockSetting($block_id, 'languages'));

            $title = I18N::translate('Edit the FAQ');
        }

        $tree_names = ['' => I18N::translate('All')] + Tree::getIdList();

        return $this->viewResponse('modules/faq/edit', [
            'block_id'    => $block_id,
            'block_order' => $block_order,
            'header'      => $header,
            'faqbody'     => $faqbody,
            'languages'   => $languages,
            'title'       => $title,
            'tree'        => $tree,
            'tree_names'  => $tree_names,
        ]);
    }
797675
public function getDetailsAction(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $pid        = $request->get('pid', Gedcom::REGEX_XREF);
        $individual = Individual::getInstance($pid, $tree);

        if ($individual === null) {
            throw new IndividualNotFoundException();
        }

        if (!$individual->canShow()) {
            throw new IndividualAccessDeniedException();
        }

        $instance = $request->get('instance', '');
        $treeview = new TreeView($instance);

        return response($treeview->getDetails($individual));
    }
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();
    }