Show the pending changes for the current tree.

@param ServerRequestInterface $request
@param Tree                   $tree

@return ResponseInterface
176294
public function showChanges(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $url = $request->get('url', route('tree-page', ['ged' => $tree->name()]));

        $rows = DB::table('change')
            ->join('user', 'user.user_id', '=', 'change.user_id')
            ->join('gedcom', 'gedcom.gedcom_id', '=', 'change.gedcom_id')
            ->where('status', '=', 'pending')
            ->orderBy('change.gedcom_id')
            ->orderBy('change.xref')
            ->orderBy('change.change_id')
            ->select(['change.*', 'user.user_name', 'user.real_name', 'gedcom_name'])
            ->get();

        $changes = [];
        foreach ($rows as $row) {
            $row->change_time = Carbon::make($row->change_time);

            $change_tree = Tree::findById((int) $row->gedcom_id);

            preg_match('/^0 (?:@' . Gedcom::REGEX_XREF . '@ )?(' . Gedcom::REGEX_TAG . ')/', $row->old_gedcom . $row->new_gedcom, $match);

            switch ($match[1]) {
                case 'INDI':
                    $row->record = new Individual($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
                    break;
                case 'FAM':
                    $row->record = new Family($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
                    break;
                case 'SOUR':
                    $row->record = new Source($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
                    break;
                case 'REPO':
                    $row->record = new Repository($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
                    break;
                case 'OBJE':
                    $row->record = new Media($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
                    break;
                case 'NOTE':
                    $row->record = new Note($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
                    break;
                default:
                    $row->record = new GedcomRecord($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
                    break;
            }

            $changes[$row->gedcom_id][$row->xref][] = $row;
        }

        $title = I18N::translate('Pending changes');

        // If the current tree has changes, activate that tab.  Otherwise activate the first tab.
        if (empty($changes[$tree->id()])) {
            reset($changes);
            $active_tree_id = key($changes);
        } else {
            $active_tree_id = $tree->id();
        }

        return $this->viewResponse('pending-changes-page', [
            'active_tree_id' => $active_tree_id,
            'changes'        => $changes,
            'title'          => $title,
            'url'            => $url,
        ]);
    }
453701
public function reorderChildren(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $xref   = $request->get('xref', '');
        $family = Family::getInstance($xref, $tree);

        Auth::checkFamilyAccess($family, true);

        $title = $family->fullName() . ' — ' . I18N::translate('Re-order children');

        return $this->viewResponse('edit/reorder-children', [
            'title'  => $title,
            'family' => $family,
        ]);
    }
217275
public function updateFact(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $xref    = $request->get('xref', '');
        $fact_id = $request->get('fact_id', '');

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

        $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', []);

        // If the fact has a DATE or PLAC, then delete any value of Y
        if ($this->text[0] === 'Y') {
            foreach ($this->tag as $n => $value) {
                if ($this->glevels[$n] == 2 && ($value === 'DATE' || $value === 'PLAC') && $this->text[$n] !== '') {
                    $this->text[0] = '';
                    break;
                }
            }
        }

        $newged = '';
        if (!empty($_POST['NAME'])) {
            $newged     .= "\n1 NAME " . $_POST['NAME'];
            $name_facts = [
                'TYPE',
                'NPFX',
                'GIVN',
                'NICK',
                'SPFX',
                'SURN',
                'NSFX',
            ];
            foreach ($name_facts as $name_fact) {
                if (!empty($_POST[$name_fact])) {
                    $newged .= "\n2 " . $name_fact . ' ' . $_POST[$name_fact];
                }
            }
        }

        $newged = $this->handleUpdates($newged);

        // Add new names after existing names
        if (!empty($_POST['NAME'])) {
            preg_match_all('/[_0-9A-Z]+/', $tree->getPreference('ADVANCED_NAME_FACTS'), $match);
            $name_facts = array_unique(array_merge(['_MARNM'], $match[0]));
            foreach ($name_facts as $name_fact) {
                // Ignore advanced facts that duplicate standard facts.
                if (!in_array($name_fact, [
                        'TYPE',
                        'NPFX',
                        'GIVN',
                        'NICK',
                        'SPFX',
                        'SURN',
                        'NSFX',
                    ]) && !empty($_POST[$name_fact])) {
                    $newged .= "\n2 " . $name_fact . ' ' . $_POST[$name_fact];
                }
            }
        }

        $newged = trim($newged); // Remove leading newline

        $census_assistant = $this->module_service->findByInterface(CensusAssistantModule::class)->first();
        if ($census_assistant instanceof CensusAssistantModule && $record instanceof Individual) {
            $newged = $census_assistant->updateCensusAssistant($request, $record, $fact_id, $newged, $keep_chan);
        }

        $record->updateFact($fact_id, $newged, !$keep_chan);

        // For the GEDFact_assistant module
        $pid_array = $request->get('pid_array', '');
        if ($pid_array) {
            foreach (explode(',', $pid_array) as $pid) {
                if ($pid !== $xref) {
                    $indi = Individual::getInstance($pid, $tree);
                    if ($indi && $indi->canEdit()) {
                        $indi->updateFact($fact_id, $newged, !$keep_chan);
                    }
                }
            }
        }

        return redirect($record->url());
    }
149489
public function mergeRecords(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $title = I18N::translate('Merge records') . ' — ' . e($tree->title());

        $xref1 = $request->get('xref1', '');
        $xref2 = $request->get('xref2', '');

        $record1 = GedcomRecord::getInstance($xref1, $tree);
        $record2 = GedcomRecord::getInstance($xref2, $tree);

        if ($xref1 !== '' && $record1 === null) {
            $xref1 = '';
        }

        if ($xref2 !== '' && $record2 === null) {
            $xref2 = '';
        }

        if ($record1 === $record2) {
            $xref2 = '';
        }

        if ($record1 !== null && $record2 && $record1::RECORD_TYPE !== $record2::RECORD_TYPE) {
            $xref2 = '';
        }

        if ($xref1 === '' || $xref2 === '') {
            return $this->viewResponse('admin/merge-records-step-1', [
                'individual1' => $record1 instanceof Individual ? $record1 : null,
                'individual2' => $record2 instanceof Individual ? $record2 : null,
                'family1'     => $record1 instanceof Family ? $record1 : null,
                'family2'     => $record2 instanceof Family ? $record2 : null,
                'source1'     => $record1 instanceof Source ? $record1 : null,
                'source2'     => $record2 instanceof Source ? $record2 : null,
                'repository1' => $record1 instanceof Repository ? $record1 : null,
                'repository2' => $record2 instanceof Repository ? $record2 : null,
                'media1'      => $record1 instanceof Media ? $record1 : null,
                'media2'      => $record2 instanceof Media ? $record2 : null,
                'note1'       => $record1 instanceof Note ? $record1 : null,
                'note2'       => $record2 instanceof Note ? $record2 : null,
                'title'       => $title,
            ]);
        }

        // Facts found both records
        $facts = [];
        // Facts found in only one record
        $facts1 = [];
        $facts2 = [];

        foreach ($record1->facts() as $fact) {
            if (!$fact->isPendingDeletion() && $fact->getTag() !== 'CHAN') {
                $facts1[$fact->id()] = $fact;
            }
        }

        foreach ($record2->facts() as $fact) {
            if (!$fact->isPendingDeletion() && $fact->getTag() !== 'CHAN') {
                $facts2[$fact->id()] = $fact;
            }
        }

        foreach ($facts1 as $id1 => $fact1) {
            foreach ($facts2 as $id2 => $fact2) {
                if ($fact1->id() === $fact2->id()) {
                    $facts[] = $fact1;
                    unset($facts1[$id1]);
                    unset($facts2[$id2]);
                }
            }
        }

        return $this->viewResponse('admin/merge-records-step-2', [
            'facts'   => $facts,
            'facts1'  => $facts1,
            'facts2'  => $facts2,
            'record1' => $record1,
            'record2' => $record2,
            'title'   => $title,
        ]);
    }
153080
public function userPageUpdate(ServerRequestInterface $request, Tree $tree, UserInterface $user): ResponseInterface
    {
        $defaults = (bool) $request->get('defaults');

        if ($defaults) {
            $main_blocks = $this->userBlocks(-1, 'main')->all();
            $side_blocks = $this->userBlocks(-1, 'side')->all();
        } else {
            $main_blocks = (array) $request->get('main');
            $side_blocks = (array) $request->get('side');
        }

        $this->updateUserBlocks($user->id(), $main_blocks, $side_blocks);

        return redirect(route('user-page', ['ged' => $tree->name()]));
    }
515619
public function updateNoteObject(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $xref = $request->get('xref', '');

        $note = Note::getInstance($xref, $tree);

        Auth::checkNoteAccess($note, true);

        $NOTE = $request->get('NOTE');

        // "\" and "$" are signficant in replacement strings, so escape them.
        $NOTE = str_replace([
            '\\',
            '$',
        ], [
            '\\\\',
            '\\$',
        ], $NOTE);

        $gedrec = preg_replace(
            '/^0 @' . $note->xref() . '@ NOTE.*(\n1 CONT.*)*/',
            '0 @' . $note->xref() . '@ NOTE ' . preg_replace("/\r?\n/", "\n1 CONT ", $NOTE),
            $note->gedcom()
        );

        $note->updateRecord($gedrec, true);

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

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

        if ($media === null) {
            throw new MediaNotFoundException();
        }

        $this->addRecordToCart($media);

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

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

        Auth::checkRecordAccess($record);

        if (Auth::isModerator($tree)) {
            DB::table('change')
                ->where('gedcom_id', '=', $record->tree()->id())
                ->where('xref', '=', $record->xref())
                ->where('status', '=', 'pending')
                ->update(['status' => 'rejected']);

            /* I18N: %s is the name of an individual, source or other record */
            FlashMessages::addMessage(I18N::translate('The changes to “%s” have been rejected.', $record->fullName()));
        }

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

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

        Auth::checkIndividualAccess($individual, true);

        // Find the fact to edit
        foreach ($individual->facts() as $fact) {
            if ($fact->id() === $fact_id && $fact->canEdit()) {
                return $this->viewResponse('edit/new-individual', [
                    'tree'       => $tree,
                    'title'      => I18N::translate('Edit the name'),
                    'nextaction' => 'update',
                    'individual' => $individual,
                    'family'     => null,
                    'name_fact'  => $fact,
                    'famtag'     => '',
                    'gender'     => $individual->sex(),
                ]);
            }
        }

        throw new NotFoundHttpException();
    }
815552
public function replaceAction(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $search  = $request->get('search', '');
        $replace = $request->get('replace', '');
        $context = $request->get('context', '');

        switch ($context) {
            case 'all':
                $records = $this->search_service->searchIndividuals([$tree], [$search]);
                $count   = $this->replaceRecords($records, $search, $replace);
                FlashMessages::addMessage(I18N::plural('%s individual has been updated.', '%s individuals have been updated.', $count, I18N::number($count)));

                $records = $this->search_service->searchFamilies([$tree], [$search]);
                $count   = $this->replaceRecords($records, $search, $replace);
                FlashMessages::addMessage(I18N::plural('%s family has been updated.', '%s families have been updated.', $count, I18N::number($count)));

                $records = $this->search_service->searchRepositories([$tree], [$search]);
                $count   = $this->replaceRecords($records, $search, $replace);
                FlashMessages::addMessage(I18N::plural('%s repository has been updated.', '%s repositories have been updated.', $count, I18N::number($count)));

                $records = $this->search_service->searchSources([$tree], [$search]);
                $count   = $this->replaceRecords($records, $search, $replace);
                FlashMessages::addMessage(I18N::plural('%s source has been updated.', '%s sources have been updated.', $count, I18N::number($count)));

                $records = $this->search_service->searchNotes([$tree], [$search]);
                $count   = $this->replaceRecords($records, $search, $replace);
                FlashMessages::addMessage(I18N::plural('%s note has been updated.', '%s notes have been updated.', $count, I18N::number($count)));
                break;

            case 'name':
                $adv_name_tags = preg_split("/[\s,;: ]+/", $tree->getPreference('ADVANCED_NAME_FACTS'));
                $name_tags     = array_unique(array_merge([
                    'NAME',
                    'NPFX',
                    'GIVN',
                    'SPFX',
                    'SURN',
                    'NSFX',
                    '_MARNM',
                    '_AKA',
                ], $adv_name_tags));

                $records = $this->search_service->searchIndividuals([$tree], [$search]);
                $count   = $this->replaceIndividualNames($records, $search, $replace, $name_tags);
                FlashMessages::addMessage(I18N::plural('%s individual has been updated.', '%s individuals have been updated.', $count, I18N::number($count)));
                break;

            case 'place':
                $records = $this->search_service->searchIndividuals([$tree], [$search]);
                $count   = $this->replacePlaces($records, $search, $replace);
                FlashMessages::addMessage(I18N::plural('%s individual has been updated.', '%s individuals have been updated.', $count, I18N::number($count)));

                $records = $this->search_service->searchFamilies([$tree], [$search]);
                $count   = $this->replacePlaces($records, $search, $replace);
                FlashMessages::addMessage(I18N::plural('%s family has been updated.', '%s families have been updated.', $count, I18N::number($count)));
                break;
        }

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

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

        // Was the search query an XREF in the current tree?
        // If so, go straight to it.
        $record = GedcomRecord::getInstance($query, $tree);

        if ($record !== null && $record->canShow()) {
            return redirect($record->url());
        }

        return $this->general($request, $tree);
    }
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,
            ],
        ]);
    }
1105826
public function registerAction(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $this->checkRegistrationAllowed();

        $comments = $request->get('comments', '');
        $email    = $request->get('email', '');
        $password = $request->get('password', '');
        $realname = $request->get('realname', '');
        $username = $request->get('username', '');

        try {
            $this->doValidateRegistration($username, $email, $realname, $comments, $password);
        } catch (Exception $ex) {
            FlashMessages::addMessage($ex->getMessage(), 'danger');

            return redirect(route('register', [
                'comments' => $comments,
                'email'    => $email,
                'realname' => $realname,
                'username' => $username,
            ]));
        }

        Log::addAuthenticationLog('User registration requested for: ' . $username);

        $user = $this->user_service->create($username, $realname, $email, $password);
        $user
            ->setPreference('language', WT_LOCALE)
            ->setPreference('verified', '0')
            ->setPreference('verified_by_admin', '0')
            ->setPreference('reg_timestamp', date('U'))
            ->setPreference('reg_hashcode', md5(Uuid::uuid4()->toString()))
            ->setPreference('contactmethod', 'messaging2')
            ->setPreference('comment', $comments)
            ->setPreference('visibleonline', '1')
            ->setPreference('auto_accept', '0')
            ->setPreference('canadmin', '0')
            ->setPreference('sessiontime', '0');

        // Send a verification message to the user.
        /* I18N: %s is a server name/URL */
        Mail::send(
            new TreeUser($tree),
            $user,
            new TreeUser($tree),
            I18N::translate('Your registration at %s', WT_BASE_URL),
            view('emails/register-user-text', ['user' => $user]),
            view('emails/register-user-html', ['user' => $user])
        );

        // Tell the genealogy contact about the registration.
        $webmaster = $this->user_service->find((int) $tree->getPreference('WEBMASTER_USER_ID'));

        if ($webmaster !== null) {
            I18N::init($webmaster->getPreference('language'));

            /* I18N: %s is a server name/URL */
            $subject = I18N::translate('New registration at %s', $tree->title());

            /* I18N: %s is a server name/URL */
            Mail::send(
                new TreeUser($tree),
                $webmaster,
                $user,
                $subject,
                view('emails/register-notify-text', ['user' => $user, 'comments' => $comments]),
                view('emails/register-notify-html', ['user' => $user, 'comments' => $comments])
            );

            $mail1_method = $webmaster->getPreference('contact_method');
            if ($mail1_method !== 'messaging3' && $mail1_method !== 'mailto' && $mail1_method !== 'none') {
                DB::table('message')->insert([
                    'sender'     => $user->email(),
                    'ip_address' => $request->getClientIp(),
                    'user_id'    => $webmaster->id(),
                    'subject'    => $subject,
                    'body'       => view('emails/register-notify-text', ['user' => $user, 'comments' => $comments]),
                ]);
            }
        }

        $title = I18N::translate('Request a new user account');

        return $this->viewResponse('register-success-page', [
            'title' => $title,
            'user'  => $user,
        ]);
    }
279588
public function postDeleteMessageAction(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $message_ids = (array) $request->get('message_id', []);

        DB::table('message')
            ->where('user_id', '=', Auth::id())
            ->whereIn('message_id', $message_ids)
            ->delete();

        if ($request->get('ctype') === 'user') {
            $url = route('user-page', ['ged' => $tree->name()]);
        } else {
            $url = route('tree-page', ['ged' => $tree->name()]);
        }

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

        return response($this->placeSearch($tree, $page, $query, true));
    }
16394
public function editMediaFileAction(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $xref     = $request->get('xref', '');
        $fact_id  = $request->get('fact_id', '');
        $folder   = $request->get('folder', '');
        $new_file = $request->get('new_file', '');
        $remote   = $request->get('remote', '');
        $title    = $request->get('title', '');
        $type     = $request->get('type', '');
        $media    = Media::getInstance($xref, $tree);

        // Tidy whitespace
        $type  = trim(preg_replace('/\s+/', ' ', $type));
        $title = trim(preg_replace('/\s+/', ' ', $title));

        // Media object oes not exist?  Media object is read-only?
        if ($media === null || $media->isPendingDeletion() || !$media->canEdit()) {
            return redirect(route('tree-page', ['ged' => $tree->name()]));
        }

        // Find the fact we are editing.
        $media_file = null;
        foreach ($media->mediaFiles() as $tmp) {
            if ($tmp->factId() === $fact_id) {
                $media_file = $tmp;
            }
        }

        // Media file does not exist?
        if ($media_file === null) {
            return redirect(route('tree-page', ['ged' => $tree->name()]));
        }

        // We can edit the file as either a URL or a folder/file
        if ($remote !== '') {
            $file = $remote;
        } else {
            $new_file = str_replace('\\', '/', $new_file);
            $folder   = str_replace('\\', '/', $folder);
            $folder   = trim($folder, '/');

            if ($folder === '') {
                $file = $new_file;
            } else {
                $file = $folder . '/' . $new_file;
            }
            if (strpos($file, '../') !== false) {
                $file = '';
            }
        }

        // Invalid filename?  Do not change it.
        if ($file === '') {
            $file = $media_file->filename();
        }

        $MEDIA_DIRECTORY = $media->tree()->getPreference('MEDIA_DIRECTORY');
        $old             = $MEDIA_DIRECTORY . $media_file->filename();
        $new             = $MEDIA_DIRECTORY . $file;

        // Update the filesystem, if we can.
        if ($old !== $new && !$media_file->isExternal()) {
            // Don't overwrite existing file
            if (file_exists(WT_DATA_DIR . $new) && file_exists(WT_DATA_DIR . $old)) {
                FlashMessages::addMessage(I18N::translate('The media file %1$s could not be renamed to %2$s.', Html::filename($media_file->filename()), Html::filename($file)), 'info');
                $file = $media_file->filename();
            } else {
                try {
                    // The "old" file may not exist.  For example, if the file was renamed on disk,
                    // and we are now renaming the GEDCOM data to match.
                    if (file_exists(WT_DATA_DIR . $old)) {
                        File::mkdir(WT_DATA_DIR . $MEDIA_DIRECTORY . $folder);
                        rename(WT_DATA_DIR . $old, WT_DATA_DIR . $new);
                    }
                    FlashMessages::addMessage(I18N::translate('The media file %1$s has been renamed to %2$s.', Html::filename($media_file->filename()), Html::filename($file)), 'info');
                } catch (Throwable $ex) {
                    FlashMessages::addMessage($ex, 'info');
                    FlashMessages::addMessage(I18N::translate('The media file %1$s could not be renamed to %2$s.', Html::filename($media_file->filename()), Html::filename($file)), 'info');
                    $file = $media_file->filename();
                }
            }
        }

        $gedcom = $this->createMediaFileGedcom($file, $type, $title);

        $media->updateFact($fact_id, $gedcom, true);

        // Accept the changes, to keep the filesystem in sync with the GEDCOM data.
        if ($old !== $new && !$media_file->isExternal()) {
            FunctionsImport::acceptAllChanges($media->xref(), $tree);
        }

        return redirect($media->url());
    }
803418
public function preferencesUpdate(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        // Coming soon
        if ((bool) $request->get('all_trees')) {
            FlashMessages::addMessage(I18N::translate('The preferences for all family trees have been updated.'), 'success');
        }
        if ((bool) $request->get('new_trees')) {
            FlashMessages::addMessage(I18N::translate('The preferences for new family trees have been updated.'), 'success');
        }

        $tree->setPreference('ADVANCED_NAME_FACTS', implode(',', $request->get('ADVANCED_NAME_FACTS', [])));
        $tree->setPreference('ADVANCED_PLAC_FACTS', implode(',', $request->get('ADVANCED_PLAC_FACTS', [])));
        $tree->setPreference('ALLOW_THEME_DROPDOWN', (string) (bool) $request->get('ALLOW_THEME_DROPDOWN'));
        // For backwards compatibility with webtrees 1.x we store the two calendar formats in one variable
        // e.g. "gregorian_and_jewish"
        $tree->setPreference('CALENDAR_FORMAT', implode('_and_', array_unique([
            $request->get('CALENDAR_FORMAT0', 'none'),
            $request->get('CALENDAR_FORMAT1', 'none'),
        ])));
        $tree->setPreference('CHART_BOX_TAGS', implode(',', $request->get('CHART_BOX_TAGS', [])));
        $tree->setPreference('CONTACT_USER_ID', $request->get('CONTACT_USER_ID'));
        $tree->setPreference('EXPAND_NOTES', (string) (bool) $request->get('EXPAND_NOTES'));
        $tree->setPreference('EXPAND_SOURCES', (string) (bool) $request->get('EXPAND_SOURCES'));
        $tree->setPreference('FAM_FACTS_ADD', implode(',', $request->get('FAM_FACTS_ADD', [])));
        $tree->setPreference('FAM_FACTS_QUICK', implode(',', $request->get('FAM_FACTS_QUICK', [])));
        $tree->setPreference('FAM_FACTS_UNIQUE', implode(',', $request->get('FAM_FACTS_UNIQUE', [])));
        $tree->setPreference('FULL_SOURCES', (string) (bool) $request->get('FULL_SOURCES'));
        $tree->setPreference('FORMAT_TEXT', $request->get('FORMAT_TEXT'));
        $tree->setPreference('GENERATE_UIDS', (string) (bool) $request->get('GENERATE_UIDS'));
        $tree->setPreference('GEONAMES_ACCOUNT', $request->get('GEONAMES_ACCOUNT'));
        $tree->setPreference('HIDE_GEDCOM_ERRORS', (string) (bool) $request->get('HIDE_GEDCOM_ERRORS'));
        $tree->setPreference('INDI_FACTS_ADD', implode(',', $request->get('INDI_FACTS_ADD', [])));
        $tree->setPreference('INDI_FACTS_QUICK', implode(',', $request->get('INDI_FACTS_QUICK', [])));
        $tree->setPreference('INDI_FACTS_UNIQUE', implode(',', $request->get('INDI_FACTS_UNIQUE', [])));
        $tree->setPreference('LANGUAGE', $request->get('LANGUAGE'));
        $tree->setPreference('MEDIA_UPLOAD', $request->get('MEDIA_UPLOAD'));
        $tree->setPreference('META_DESCRIPTION', $request->get('META_DESCRIPTION'));
        $tree->setPreference('META_TITLE', $request->get('META_TITLE'));
        $tree->setPreference('NO_UPDATE_CHAN', (string) (bool) $request->get('NO_UPDATE_CHAN'));
        $tree->setPreference('PEDIGREE_ROOT_ID', $request->get('PEDIGREE_ROOT_ID'));
        $tree->setPreference('PREFER_LEVEL2_SOURCES', $request->get('PREFER_LEVEL2_SOURCES'));
        $tree->setPreference('QUICK_REQUIRED_FACTS', implode(',', $request->get('QUICK_REQUIRED_FACTS', [])));
        $tree->setPreference('QUICK_REQUIRED_FAMFACTS', implode(',', $request->get('QUICK_REQUIRED_FAMFACTS', [])));
        $tree->setPreference('REPO_FACTS_ADD', implode(',', $request->get('REPO_FACTS_ADD', [])));
        $tree->setPreference('REPO_FACTS_QUICK', implode(',', $request->get('REPO_FACTS_QUICK', [])));
        $tree->setPreference('REPO_FACTS_UNIQUE', implode(',', $request->get('REPO_FACTS_UNIQUE', [])));
        $tree->setPreference('SHOW_COUNTER', (string) (bool) $request->get('SHOW_COUNTER'));
        $tree->setPreference('SHOW_EST_LIST_DATES', (string) (bool) $request->get('SHOW_EST_LIST_DATES'));
        $tree->setPreference('SHOW_FACT_ICONS', (string) (bool) $request->get('SHOW_FACT_ICONS'));
        $tree->setPreference('SHOW_GEDCOM_RECORD', (string) (bool) $request->get('SHOW_GEDCOM_RECORD'));
        $tree->setPreference('SHOW_HIGHLIGHT_IMAGES', (string) (bool) $request->get('SHOW_HIGHLIGHT_IMAGES'));
        $tree->setPreference('SHOW_LAST_CHANGE', (string) (bool) $request->get('SHOW_LAST_CHANGE'));
        $tree->setPreference('SHOW_MEDIA_DOWNLOAD', $request->get('SHOW_MEDIA_DOWNLOAD'));
        $tree->setPreference('SHOW_NO_WATERMARK', $request->get('SHOW_NO_WATERMARK'));
        $tree->setPreference('SHOW_PARENTS_AGE', (string) (bool) $request->get('SHOW_PARENTS_AGE'));
        $tree->setPreference('SHOW_PEDIGREE_PLACES', $request->get('SHOW_PEDIGREE_PLACES'));
        $tree->setPreference('SHOW_PEDIGREE_PLACES_SUFFIX', (string) (bool) $request->get('SHOW_PEDIGREE_PLACES_SUFFIX'));
        $tree->setPreference('SHOW_RELATIVES_EVENTS', implode(',', $request->get('SHOW_RELATIVES_EVENTS', [])));
        $tree->setPreference('SOUR_FACTS_ADD', implode(',', $request->get('SOUR_FACTS_ADD', [])));
        $tree->setPreference('SOUR_FACTS_QUICK', implode(',', $request->get('SOUR_FACTS_QUICK', [])));
        $tree->setPreference('SOUR_FACTS_UNIQUE', implode(',', $request->get('SOUR_FACTS_UNIQUE', [])));
        $tree->setPreference('SUBLIST_TRIGGER_I', (string) (int) $request->get('SUBLIST_TRIGGER_I', 200));
        $tree->setPreference('SURNAME_LIST_STYLE', $request->get('SURNAME_LIST_STYLE'));
        $tree->setPreference('SURNAME_TRADITION', $request->get('SURNAME_TRADITION'));
        $tree->setPreference('THEME_DIR', $request->get('THEME_DIR'));
        $tree->setPreference('USE_SILHOUETTE', (string) (bool) $request->get('USE_SILHOUETTE'));
        $tree->setPreference('WEBMASTER_USER_ID', $request->get('WEBMASTER_USER_ID'));
        $tree->setPreference('WEBTREES_EMAIL', $request->get('WEBTREES_EMAIL'));
        $tree->setPreference('title', $request->get('title'));

        // Only accept valid folders for MEDIA_DIRECTORY
        $MEDIA_DIRECTORY = preg_replace('/[\/\\\\]+/', '/', $request->get('MEDIA_DIRECTORY') . '/');
        if (substr($MEDIA_DIRECTORY, 0, 1) === '/') {
            $MEDIA_DIRECTORY = substr($MEDIA_DIRECTORY, 1);
        }

        if ($MEDIA_DIRECTORY) {
            if (is_dir(WT_DATA_DIR . $MEDIA_DIRECTORY)) {
                $tree->setPreference('MEDIA_DIRECTORY', $MEDIA_DIRECTORY);
            } elseif (File::mkdir(WT_DATA_DIR . $MEDIA_DIRECTORY)) {
                $tree->setPreference('MEDIA_DIRECTORY', $MEDIA_DIRECTORY);
                FlashMessages::addMessage(I18N::translate('The folder %s has been created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY)), 'info');
            } else {
                FlashMessages::addMessage(I18N::translate('The folder %s does not exist, and it could not be created.', Html::filename(WT_DATA_DIR . $MEDIA_DIRECTORY)), 'danger');
            }
        }

        $gedcom = $request->get('gedcom');
        if ($gedcom && $gedcom !== $tree->name()) {
            try {
                DB::table('gedcom')
                    ->where('gedcom_id', '=', $tree->id())
                    ->update(['gedcom_name' => $gedcom]);

                DB::table('site_setting')
                    ->where('setting_name', '=', 'DEFAULT_GEDCOM')
                    ->where('setting_value', '=', $tree->name())
                    ->update(['setting_value' => $gedcom]);
            } catch (Exception $ex) {
                // Probably a duplicate name.
            }
        }

        FlashMessages::addMessage(I18N::translate('The preferences for the family tree “%s” have been updated.', e($tree->title())), 'success');

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

        return redirect($url);
    }