@param ServerRequestInterface $request
@param Tree                   $tree

@return ResponseInterface
16297
public function postAdminMoveDownAction(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $block_id = (int) $request->get('block_id');

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

        $swap_block = DB::table('block')
            ->where('module_name', '=', $this->name())
            ->where('block_order', '=', static function (Builder $query) use ($block_order): void {
                $query
                    ->from('block')
                    ->where('module_name', '=', $this->name())
                    ->where('block_order', '>', $block_order)
                    ->select(DB::raw('MIN(block_order)'));
            })
            ->select(['block_order', 'block_id'])
            ->first();

        if ($swap_block !== null) {
            DB::table('block')
                ->where('block_id', '=', $block_id)
                ->update([
                    'block_order' => $swap_block->block_order,
                ]);

            DB::table('block')
                ->where('block_id', '=', $swap_block->block_id)
                ->update([
                    'block_order' => $block_order,
                ]);
        }

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

        return redirect($url);
    }
760238
public function page(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $query = $request->get('query', '');
        $xref  = $request->get('extra', '');

        $source = Source::getInstance($xref, $tree);

        Auth::checkSourceAccess($source);

        $regex_query = preg_quote(strtr($query, [' ' => '.+']), '/');

        // Fetch all records with a link to this source
        $individuals = DB::table('individuals')
            ->join('link', static function (JoinClause $join): void {
                $join
                    ->on('l_file', '=', 'i_file')
                    ->on('l_from', '=', 'i_id');
            })
            ->where('i_file', '=', $tree->id())
            ->where('l_to', '=', $xref)
            ->where('l_type', '=', 'SOUR')
            ->distinct()
            ->select(['individuals.*'])
            ->get()
            ->map(Individual::rowMapper())
            ->filter(GedcomRecord::accessFilter());

        $families = DB::table('families')
            ->join('link', static function (JoinClause $join): void {
                $join
                    ->on('l_file', '=', 'f_file')
                    ->on('l_from', '=', 'f_id')
                    ->where('l_type', '=', 'SOUR');
            })
            ->where('f_file', '=', $tree->id())
            ->where('l_to', '=', $xref)
            ->where('l_type', '=', 'SOUR')
            ->distinct()
            ->select(['families.*'])
            ->get()
            ->map(Family::rowMapper())
            ->filter(GedcomRecord::accessFilter());

        $pages = [];

        foreach ($individuals->merge($families) as $record) {
            if (preg_match_all('/\n1 SOUR @' . $xref . '@(?:\n[2-9].*)*\n2 PAGE (.*' . $regex_query . '.*)/i', $record->gedcom(), $matches)) {
                $pages = array_merge($pages, $matches[1]);
            }

            if (preg_match_all('/\n2 SOUR @' . $xref . '@(?:\n[3-9].*)*\n3 PAGE (.*' . $regex_query . '.*)/i', $record->gedcom(), $matches)) {
                $pages = array_merge($pages, $matches[1]);
            }
        }

        $pages = array_unique($pages);

        $pages = array_map(static function (string $page): array {
            return ['value' => $page];
        }, $pages);

        return response($pages);
    }
620625
public function addUnlinkedAction(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $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);
            }
        }
        if ((bool) $request->get('SOUR_INDI')) {
            $gedrec = $this->handleUpdates($gedrec);
        } else {
            $gedrec = $this->updateRest($gedrec);
        }

        $new_indi = $tree->createIndividual($gedrec);

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

        return redirect(route('admin-trees', ['ged' => $tree->name()]));
    }
449835
public function advanced(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $default_fields = array_fill_keys(self::DEFAULT_ADVANCED_FIELDS, '');

        $fields      = $request->get('fields', $default_fields);
        $modifiers   = $request->get('modifiers', []);
        $other_field = $request->get('other_field', '');
        $other_value = $request->get('other_value', '');

        if ($other_field !== '' && $other_value !== '') {
            $fields[$other_field] = $other_value;
        }

        $other_fields = $this->otherFields($fields);
        $date_options = $this->dateOptions();
        $name_options = $this->nameOptions();

        if (!empty(array_filter($fields))) {
            $individuals = $this->search_service->searchIndividualsAdvanced([$tree], $fields, $modifiers);
        } else {
            $individuals = [];
        }

        $title = I18N::translate('Advanced search');

        return $this->viewResponse('search-advanced-page', [
            'date_options' => $date_options,
            'fields'       => $fields,
            'individuals'  => $individuals,
            'modifiers'    => $modifiers,
            'name_options' => $name_options,
            'other_fields' => $other_fields,
            'title'        => $title,
        ]);
    }
220681
public function treePageBlock(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $block_id = $request->get('block_id');

        $block_id = (int) DB::table('block')
            ->where('block_id', '=', $block_id)
            ->where('gedcom_id', '=', $tree->id())
            ->value('block_id');

        $module = $this->getBlockModule($tree, $block_id);

        $html = view('layouts/ajax', [
            'content' => $module->getBlock($tree, $block_id, 'gedcom'),
        ]);

        return response($html);
    }
463964
public function postAddSourceAction(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $xref   = $request->get('xref', '');
        $option = $request->get('option', '');

        $source = Source::getInstance($xref, $tree);

        if ($source === null) {
            throw new SourceNotFoundException();
        }

        $this->addRecordToCart($source);

        if ($option === 'linked') {
            foreach ($source->linkedIndividuals('SOUR') as $individual) {
                $this->addRecordToCart($individual);
            }
            foreach ($source->linkedFamilies('SOUR') as $family) {
                $this->addRecordToCart($family);
            }
        }

        return redirect($source->url());
    }
102773
public function calendar(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $CALENDAR_FORMAT = $tree->getPreference('CALENDAR_FORMAT');

        $cal      = $request->getQueryParams()['cal'] ?? '';
        $day      = $request->getQueryParams()['day'] ?? '';
        $month    = $request->getQueryParams()['month'] ?? '';
        $year     = $request->getQueryParams()['year'] ?? '';
        $view     = $request->getQueryParams()['view'] ?? 'day';
        $filterev = $request->getQueryParams()['filterev'] ?? 'BIRT-MARR-DEAT';
        $filterof = $request->getQueryParams()['filterof'] ?? 'all';
        $filtersx = $request->getQueryParams()['filtersx'] ?? '';

        if ($cal . $day . $month . $year === '') {
            // No date specified? Use the most likely calendar
            $cal = $this->localization_service->calendar()->gedcomCalendarEscape();
        }

        // Create a CalendarDate from the parameters

        // We cannot display new-style/old-style years, so convert to new style
        if (preg_match('/^(\d\d)\d\d\/(\d\d)$/', $year, $match)) {
            $year = $match[1] . $match[2];
        }

        // advanced-year "year range"
        if (preg_match('/^(\d+)-(\d+)$/', $year, $match)) {
            if (strlen($match[1]) > strlen($match[2])) {
                $match[2] = substr($match[1], 0, strlen($match[1]) - strlen($match[2])) . $match[2];
            }
            $ged_date = new Date("FROM {$cal} {$match[1]} TO {$cal} {$match[2]}");
            $view     = 'year';
        } elseif (preg_match('/^(\d+)(\?+)$/', $year, $match)) {
            // advanced-year "decade/century wildcard"
            $y1       = $match[1] . str_replace('?', '0', $match[2]);
            $y2       = $match[1] . str_replace('?', '9', $match[2]);
            $ged_date = new Date("FROM {$cal} {$y1} TO {$cal} {$y2}");
            $view     = 'year';
        } else {
            if ($year < 0) {
                $year = (-$year) . ' B.C.';
            } // need BC to parse date
            $ged_date = new Date("{$cal} {$day} {$month} {$year}");
        }
        $cal_date = $ged_date->minimumDate();

        // Fill in any missing bits with todays date
        $today = $cal_date->today();
        if ($cal_date->day === 0) {
            $cal_date->day = $today->day;
        }
        if ($cal_date->month === 0) {
            $cal_date->month = $today->month;
        }
        if ($cal_date->year === 0) {
            $cal_date->year = $today->year;
        }

        $cal_date->setJdFromYmd();

        // Extract values from date
        $days_in_month = $cal_date->daysInMonth();
        $days_in_week  = $cal_date->daysInWeek();

        // Invalid dates? Go to monthly view, where they'll be found.
        if ($cal_date->day > $days_in_month && $view === 'day') {
            $view = 'month';
        }

        /** @var Fact[]|Fact[][] $found_facts */
        $found_facts = [];

        switch ($view) {
            case 'day':
                $found_facts = $this->applyFilter($this->calendar_service->getAnniversaryEvents($cal_date->minimumJulianDay(), $filterev, $tree), $filterof, $filtersx);
                break;
            case 'month':
                $cal_date->day = 0;
                $cal_date->setJdFromYmd();
                // Make a separate list for each day. Unspecified/invalid days go in day 0.
                for ($d = 0; $d <= $days_in_month; ++$d) {
                    $found_facts[$d] = [];
                }
                // Fetch events for each day
                $jds = range($cal_date->minimumJulianDay(), $cal_date->maximumJulianDay());

                foreach ($jds as $jd) {
                    foreach ($this->applyFilter($this->calendar_service->getAnniversaryEvents($jd, $filterev, $tree), $filterof, $filtersx) as $fact) {
                        $tmp = $fact->date()->minimumDate();
                        if ($tmp->day >= 1 && $tmp->day <= $tmp->daysInMonth()) {
                            // If the day is valid (for its own calendar), display it in the
                            // anniversary day (for the display calendar).
                            $found_facts[$jd - $cal_date->minimumJulianDay() + 1][] = $fact;
                        } else {
                            // Otherwise, display it in the "Day not set" box.
                            $found_facts[0][] = $fact;
                        }
                    }
                }
                break;
            case 'year':
                $cal_date->month = 0;
                $cal_date->setJdFromYmd();
                $found_facts = $this->applyFilter($this->calendar_service->getCalendarEvents($ged_date->minimumJulianDay(), $ged_date->maximumJulianDay(), explode('-', $filterev), $tree), $filterof, $filtersx);
                // Eliminate duplicates (e.g. BET JUL 1900 AND SEP 1900 will appear twice in 1900)
                $found_facts = array_unique($found_facts);
                break;
        }

        // Group the facts by family/individual
        $indis     = [];
        $fams      = [];
        $cal_facts = [];

        switch ($view) {
            case 'year':
            case 'day':
                foreach ($found_facts as $fact) {
                    $record = $fact->record();
                    $xref   = $record->xref();
                    if ($record instanceof Individual) {
                        if (empty($indis[$xref])) {
                            $indis[$xref] = $this->calendarFactText($fact, true);
                        } else {
                            $indis[$xref] .= '<br>' . $this->calendarFactText($fact, true);
                        }
                    } elseif ($record instanceof Family) {
                        if (empty($indis[$xref])) {
                            $fams[$xref] = $this->calendarFactText($fact, true);
                        } else {
                            $fams[$xref] .= '<br>' . $this->calendarFactText($fact, true);
                        }
                    }
                }
                break;
            case 'month':
                foreach ($found_facts as $d => $facts) {
                    $cal_facts[$d] = [];
                    foreach ($facts as $fact) {
                        $xref = $fact->record()->xref();
                        if (empty($cal_facts[$d][$xref])) {
                            $cal_facts[$d][$xref] = $this->calendarFactText($fact, false);
                        } else {
                            $cal_facts[$d][$xref] .= '<br>' . $this->calendarFactText($fact, false);
                        }
                    }
                }
                break;
        }

        ob_start();

        switch ($view) {
            case 'year':
            case 'day':
                echo '<table class="w-100"><tr>';
                echo '<td class="descriptionbox center"><i class="icon-indis"></i>', I18N::translate('Individuals'), '</td>';
                echo '<td class="descriptionbox center"><i class="icon-cfamily"></i>', I18N::translate('Families'), '</td>';
                echo '</tr><tr>';
                echo '<td class="optionbox wrap">';

                $content = $this->calendarListText($indis, '<li>', '</li>', $tree);
                if ($content) {
                    echo '<ul>', $content, '</ul>';
                }

                echo '</td>';
                echo '<td class="optionbox wrap">';

                $content = $this->calendarListText($fams, '<li>', '</li>', $tree);
                if ($content) {
                    echo '<ul>', $content, '</ul>';
                }

                echo '</td>';
                echo '</tr><tr>';
                echo '<td class="descriptionbox">', I18N::translate('Total individuals: %s', I18N::number(count($indis))), '</td>';
                echo '<td class="descriptionbox">', I18N::translate('Total families: %s', I18N::number(count($fams))), '</td>';
                echo '</tr></table>';

                break;
            case 'month':
                // We use JD%7 = 0/Mon…6/Sun. Standard definitions use 0/Sun…6/Sat.
                $week_start    = (I18N::firstDay() + 6) % 7;
                $weekend_start = ($this->localization_service->weekendStart() + 6) % 7;
                $weekend_end   = ($this->localization_service->weekendEnd() + 6) % 7;
                // The french  calendar has a 10-day week, which starts on primidi
                if ($days_in_week === 10) {
                    $week_start    = 0;
                    $weekend_start = -1;
                    $weekend_end   = -1;
                }
                echo '<table class="w-100"><thead><tr>';
                for ($week_day = 0; $week_day < $days_in_week; ++$week_day) {
                    $day_name = $cal_date->dayNames(($week_day + $week_start) % $days_in_week);
                    if ($week_day == $weekend_start || $week_day == $weekend_end) {
                        echo '<th class="descriptionbox weekend" width="' . (100 / $days_in_week) . '%">', $day_name, '</th>';
                    } else {
                        echo '<th class="descriptionbox" width="' . (100 / $days_in_week) . '%">', $day_name, '</th>';
                    }
                }
                echo '</tr>';
                echo '</thead>';
                echo '<tbody>';
                // Print days 1 to n of the month, but extend to cover "empty" days before/after the month to make whole weeks.
                // e.g. instead of 1 -> 30 (=30 days), we might have -1 -> 33 (=35 days)
                $start_d = 1 - ($cal_date->minimumJulianDay() - $week_start) % $days_in_week;
                $end_d   = $days_in_month + ($days_in_week - ($cal_date->maximumJulianDay() - $week_start + 1) % $days_in_week) % $days_in_week;
                // Make sure that there is an empty box for any leap/missing days
                if ($start_d === 1 && $end_d === $days_in_month && count($found_facts[0]) > 0) {
                    $end_d += $days_in_week;
                }
                for ($d = $start_d; $d <= $end_d; ++$d) {
                    if (($d + $cal_date->minimumJulianDay() - $week_start) % $days_in_week === 1) {
                        echo '<tr>';
                    }
                    echo '<td class="optionbox wrap">';
                    if ($d < 1 || $d > $days_in_month) {
                        if (count($cal_facts[0]) > 0) {
                            echo '<span class="cal_day">', I18N::translate('Day not set'), '</span><br style="clear: both;">';
                            echo '<div class="details1" style="height: 180px; overflow: auto;">';
                            echo $this->calendarListText($cal_facts[0], '', '', $tree);
                            echo '</div>';
                            $cal_facts[0] = [];
                        }
                    } else {
                        // Format the day number using the calendar
                        $tmp   = new Date($cal_date->format("%@ {$d} %O %E"));
                        $d_fmt = $tmp->minimumDate()->format('%j');
                        if ($d === $today->day && $cal_date->month === $today->month) {
                            echo '<span class="cal_day current_day">', $d_fmt, '</span>';
                        } else {
                            echo '<span class="cal_day">', $d_fmt, '</span>';
                        }
                        // Show a converted date
                        foreach (explode('_and_', $CALENDAR_FORMAT) as $convcal) {
                            switch ($convcal) {
                                case 'french':
                                    $alt_date = new FrenchDate($cal_date->minimumJulianDay() + $d - 1);
                                    break;
                                case 'gregorian':
                                    $alt_date = new GregorianDate($cal_date->minimumJulianDay() + $d - 1);
                                    break;
                                case 'jewish':
                                    $alt_date = new JewishDate($cal_date->minimumJulianDay() + $d - 1);
                                    break;
                                case 'julian':
                                    $alt_date = new JulianDate($cal_date->minimumJulianDay() + $d - 1);
                                    break;
                                case 'hijri':
                                    $alt_date = new HijriDate($cal_date->minimumJulianDay() + $d - 1);
                                    break;
                                case 'jalali':
                                    $alt_date = new JalaliDate($cal_date->minimumJulianDay() + $d - 1);
                                    break;
                                case 'none':
                                default:
                                    $alt_date = $cal_date;
                                    break;
                            }
                            if (get_class($alt_date) !== get_class($cal_date) && $alt_date->inValidRange()) {
                                echo '<span class="rtl_cal_day">' . $alt_date->format('%j %M') . '</span>';
                                // Just show the first conversion
                                break;
                            }
                        }
                        echo '<br style="clear: both;"><div class="details1" style="height: 180px; overflow: auto;">';
                        echo $this->calendarListText($cal_facts[$d], '', '', $tree);
                        echo '</div>';
                    }
                    echo '</td>';
                    if (($d + $cal_date->minimumJulianDay() - $week_start) % $days_in_week === 0) {
                        echo '</tr>';
                    }
                }
                echo '</tbody>';
                echo '</table>';
                break;
        }

        $html = ob_get_clean();

        return response($html);
    }
601651
public function loginPage(ServerRequestInterface $request, ?Tree $tree): ResponseInterface
    {
        // Already logged in?
        if (Auth::check()) {
            $ged = $tree !== null ? $tree->name() : '';

            return redirect(route('user-page', ['ged' => $ged]));
        }

        $error    = $request->get('error', '');
        $url      = $request->get('url', '');
        $username = $request->get('username', '');

        $title = I18N::translate('Sign in');

        switch (Site::getPreference('WELCOME_TEXT_AUTH_MODE')) {
            case 1:
            default:
                $welcome = I18N::translate('Anyone with a user account can access this website.');
                break;
            case 2:
                $welcome = I18N::translate('You need to be an authorized user to access this website.');
                break;
            case 3:
                $welcome = I18N::translate('You need to be a family member to access this website.');
                break;
            case 4:
                $welcome = Site::getPreference('WELCOME_TEXT_AUTH_MODE_' . WT_LOCALE);
                break;
        }

        if (Site::getPreference('USE_REGISTRATION_MODULE') === '1') {
            $welcome .= ' ' . I18N::translate('You can apply for an account using the link below.');
        }

        $can_register = Site::getPreference('USE_REGISTRATION_MODULE') === '1';

        return $this->viewResponse('login-page', [
            'can_register' => $can_register,
            'error'        => $error,
            'title'        => $title,
            'url'          => $url,
            'username'     => $username,
            'welcome'      => $welcome,
        ]);
    }
336211
public function linkMediaToRecordAction(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $xref = $request->get('xref', '');
        $link = $request->get('link', '');

        $media  = Media::getInstance($xref, $tree);
        $record = GedcomRecord::getInstance($link, $tree);

        $record->createFact('1 OBJE @' . $xref . '@', true);

        return redirect($media->url());
    }
809973
public function acceptChanges(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $xref = $request->get('xref', '');

        $record = GedcomRecord::getInstance($xref, $tree);

        Auth::checkRecordAccess($record, false);

        if ($record && Auth::isModerator($tree)) {
            if ($record->isPendingDeletion()) {
                /* I18N: %s is the name of a genealogy record */
                FlashMessages::addMessage(I18N::translate('“%s” has been deleted.', $record->fullName()));
            } else {
                /* I18N: %s is the name of a genealogy record */
                FlashMessages::addMessage(I18N::translate('The changes to “%s” have been accepted.', $record->fullName()));
            }
            FunctionsImport::acceptAllChanges($record->xref(), $record->tree());
        }

        return response();
    }
239297
public function postAddRepositoryAction(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $xref = $request->get('xref', '');

        $repository = Repository::getInstance($xref, $tree);

        if ($repository === null) {
            throw new RepositoryNotFoundException();
        }

        $this->addRecordToCart($repository);

        return redirect($repository->url());
    }
1270158
public function select2Family(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $page  = (int) $request->get('page');
        $query = $request->get('q', '');

        // Fetch one more row than we need, so we can know if more rows exist.
        $offset = $page * self::RESULTS_PER_PAGE;
        $limit  = self::RESULTS_PER_PAGE + 1;

        $results = $this->search_service
            ->searchFamilyNames([$tree], [$query], $offset, $limit)
            ->map(static function (Family $family): array {
                return [
                    'id'    => $family->xref(),
                    'text'  => view('selects/family', ['family' => $family]),
                    'title' => ' ',
                ];
            });

        return response([
            'results'    => $results->slice(0, self::RESULTS_PER_PAGE)->all(),
            'pagination' => [
                'more' => $results->count() > self::RESULTS_PER_PAGE,
            ],
        ]);
    }
504167
public function update(ServerRequestInterface $request, Tree $tree, UserInterface $user): ResponseInterface
    {
        $contact_method = (string) $request->get('contact_method');
        $email          = (string) $request->get('email');
        $language       = (string) $request->get('language');
        $real_name      = (string) $request->get('real_name');
        $password       = (string) $request->get('password');
        $rootid         = (string) $request->get('root_id');
        $theme          = (string) $request->get('theme');
        $time_zone      = (string) $request->get('timezone');
        $user_name      = (string) $request->get('user_name');
        $visible_online = (string) $request->get('visible_online');

        // Change the password
        if ($password !== '') {
            $user->setPassword($password);
        }

        // Change the username
        if ($user_name !== $user->userName()) {
            if ($this->user_service->findByUserName($user_name) === null) {
                $user->setUserName($user_name);
            } else {
                FlashMessages::addMessage(I18N::translate('Duplicate username. A user with that username already exists. Please choose another username.'));
            }
        }

        // Change the email
        if ($email !== $user->email()) {
            if ($this->user_service->findByEmail($email) === null) {
                $user->setEmail($email);
            } else {
                FlashMessages::addMessage(I18N::translate('Duplicate email address. A user with that email already exists.'));
            }
        }

        $user
            ->setRealName($real_name)
            ->setPreference('contactmethod', $contact_method)
            ->setPreference('language', $language)
            ->setPreference('theme', $theme)
            ->setPreference('TIMEZONE', $time_zone)
            ->setPreference('visibleonline', $visible_online);

        $tree->setUserPreference($user, 'rootid', $rootid);

        // Switch to the new theme now
        Session::put('theme', $theme);

        // Switch to the new language now
        Session::put('language', $language);

        return redirect(route('my-account', ['ged' => $tree->name()]));
    }
1304689
public function treePageDefaultUpdate(ServerRequestInterface $request): ResponseInterface
    {
        $main_blocks = (array) $request->get('main');
        $side_blocks = (array) $request->get('side');

        $this->updateTreeBlocks(-1, $main_blocks, $side_blocks);

        return redirect(route('admin-control-panel'));
    }
475344
public function createNoteObjectAction(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $note                = $request->get('note', '');
        $privacy_restriction = $request->get('privacy-restriction', '');
        $edit_restriction    = $request->get('edit-restriction', '');

        // Convert line endings to GEDDCOM continuations
        $note = preg_replace('/\r|\r\n|\n|\r/', "\n1 CONT ", $note);

        $gedcom = '0 @@ NOTE ' . $note;

        if (in_array($privacy_restriction, [
            'none',
            'privacy',
            'confidential',
        ])) {
            $gedcom .= "\n1 RESN " . $privacy_restriction;
        }

        if (in_array($edit_restriction, ['locked'])) {
            $gedcom .= "\n1 RESN " . $edit_restriction;
        }

        $record = $tree->createRecord($gedcom);

        return response([
            'id'   => $record->xref(),
            'text' => view('selects/note', [
                'note' => $record,
            ]),
            'html' => view('modals/record-created', [
                'title' => I18N::translate('The note has been created'),
                'name'  => $record->fullName(),
                'url'   => $record->url(),
            ]),
        ]);
    }
1103405
public function select2MediaObject(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $page  = (int) $request->get('page');
        $query = $request->get('q', '');

        // Fetch one more row than we need, so we can know if more rows exist.
        $offset = $page * self::RESULTS_PER_PAGE;
        $limit  = self::RESULTS_PER_PAGE + 1;

        $results = $this->search_service
            ->searchMedia([$tree], [$query], $offset, $limit)
            ->map(static function (Media $media): array {
                return [
                    'id'    => $media->xref(),
                    'text'  => view('selects/media', ['media' => $media]),
                    'title' => ' ',
                ];
            });

        return response([
            'results'    => $results->slice(0, self::RESULTS_PER_PAGE)->all(),
            'pagination' => [
                'more' => $results->count() > self::RESULTS_PER_PAGE,
            ],
        ]);
    }
911549
public function editFact(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $xref    = $request->get('xref', '');
        $fact_id = $request->get('fact_id', '');

        $record = GedcomRecord::getInstance($xref, $tree);
        Auth::checkRecordAccess($record, true);

        // Find the fact to edit
        $edit_fact = null;
        foreach ($record->facts() as $fact) {
            if ($fact->id() === $fact_id && $fact->canEdit()) {
                $edit_fact = $fact;
                break;
            }
        }
        if ($edit_fact === null) {
            throw new NotFoundHttpException();
        }

        $can_edit_raw = Auth::isAdmin() || $tree->getPreference('SHOW_GEDCOM_RECORD');

        $title = $record->fullName() . ' - ' . GedcomTag::getLabel($edit_fact->getTag());

        return $this->viewResponse('edit/edit-fact', [
            'can_edit_raw' => $can_edit_raw,
            'edit_fact'    => $edit_fact,
            'record'       => $record,
            'title'        => $title,
            'tree'         => $tree,
        ]);
    }