An HTML form to edit block settings

@param Tree $tree
@param int  $block_id

@return void
183783
public function editBlockConfiguration(Tree $tree, int $block_id): void
    {
        $calendar  = $this->getBlockSetting($block_id, 'calendar', 'jewish');
        $days      = $this->getBlockSetting($block_id, 'days', self::DEFAULT_DAYS);
        $infoStyle = $this->getBlockSetting($block_id, 'infoStyle', 'table');

        $styles = [
            /* I18N: An option in a list-box */
            'list'  => I18N::translate('list'),
            /* I18N: An option in a list-box */
            'table' => I18N::translate('table'),
        ];

        $calendars = [
            'jewish'    => I18N::translate('Jewish'),
            'gregorian' => I18N::translate('Gregorian'),
        ];

        echo view('modules/yahrzeit/config', [
            'calendar'  => $calendar,
            'calendars' => $calendars,
            'days'      => $days,
            'infoStyle' => $infoStyle,
            'max_days'  => self::MAX_DAYS,
            'styles'    => $styles,
        ]);
    }
665794
public function getBlock(Tree $tree, int $block_id, string $ctype = '', array $cfg = []): string
    {
        $num       = (int) $this->getBlockSetting($block_id, 'num', self::DEFAULT_NUMBER);
        $infoStyle = $this->getBlockSetting($block_id, 'infoStyle', self::DEFAULT_STYLE);

        extract($cfg, EXTR_OVERWRITE);

        // Use the count of base surnames.
        $top_surnames = DB::table('name')
            ->where('n_file', '=', $tree->id())
            ->where('n_type', '<>', '_MARNM')
            ->whereNotIn('n_surn', ['@N.N.', ''])
            ->groupBy('n_surn')
            ->orderByDesc(DB::raw('COUNT(n_surn)'))
            ->take($num)
            ->pluck('n_surn');

        $all_surnames = [];

        foreach ($top_surnames as $top_surname) {
            $variants = DB::table('name')
                ->where('n_file', '=', $tree->id())
                ->where(DB::raw('n_surn /*! COLLATE utf8_bin */'), '=', $top_surname)
                ->groupBy('surname')
                ->select([DB::raw('n_surname /*! COLLATE utf8_bin */ AS surname'), DB::raw('count(*) AS total')])
                ->pluck('total', 'surname')
                ->all();

            $all_surnames[$top_surname] = $variants;
        }
        
        //find a module providing individual lists
        $module = app(ModuleService::class)->findByComponent(ModuleListInterface::class, $tree, Auth::user())->first(static function (ModuleInterface $module) {
            return $module instanceof IndividualListModule;
        });
        
        switch ($infoStyle) {
            case 'tagcloud':
                uksort($all_surnames, [I18N::class, 'strcasecmp']);
                $content = FunctionsPrintLists::surnameTagCloud($all_surnames, $module, true, $tree);
                break;
            case 'list':
                uasort($all_surnames, [$this, 'surnameCountSort']);
                $content = FunctionsPrintLists::surnameList($all_surnames, 1, true, $module, $tree);
                break;
            case 'array':
                uasort($all_surnames, [$this, 'surnameCountSort']);
                $content = FunctionsPrintLists::surnameList($all_surnames, 2, true, $module, $tree);
                break;
            case 'table':
            default:
                $content = view('lists/surnames-table', [
                    'surnames' => $all_surnames,
                    'module'   => $module,
                    'families' => false,
                    'tree'     => $tree,
                ]);
                break;
        }

        if ($ctype !== '') {
            $num = count($top_surnames);
            if ($num === 1) {
                // I18N: i.e. most popular surname.
                $title = I18N::translate('Top surname');
            } else {
                // I18N: Title for a list of the most common surnames, %s is a number. Note that a separate translation exists when %s is 1
                $title = I18N::plural('Top %s surname', 'Top %s surnames', $num, I18N::number($num));
            }

            if ($ctype === 'gedcom' && Auth::isManager($tree)) {
                $config_url = route('tree-page-block-edit', [
                    'block_id' => $block_id,
                    'ged'      => $tree->name(),
                ]);
            } elseif ($ctype === 'user' && Auth::check()) {
                $config_url = route('user-page-block-edit', [
                    'block_id' => $block_id,
                    'ged'      => $tree->name(),
                ]);
            } else {
                $config_url = '';
            }

            return view('modules/block-template', [
                'block'      => Str::kebab($this->name()),
                'id'         => $block_id,
                'config_url' => $config_url,
                'title'      => $title,
                'content'    => $content,
            ]);
        }

        return $content;
    }
759927
public function postAdminEditAction(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $block_id    = (int) $request->get('block_id');
        $xref        = $request->get('xref', '');
        $story_body  = $request->get('story_body', '');
        $story_title = $request->get('story_title', '');
        $languages   = $request->get('languages', []);

        if ($block_id !== 0) {
            DB::table('block')
                ->where('block_id', '=', $block_id)
                ->update([
                    'gedcom_id' => $tree->id(),
                    'xref'      => $xref,
                ]);
        } else {
            DB::table('block')->insert([
                'gedcom_id'   => $tree->id(),
                'xref'        => $xref,
                'module_name' => $this->name(),
                'block_order' => 0,
            ]);

            $block_id = (int) DB::connection()->getPdo()->lastInsertId();
        }

        $this->setBlockSetting($block_id, 'story_body', $story_body);
        $this->setBlockSetting($block_id, 'title', $story_title);
        $this->setBlockSetting($block_id, 'languages', implode(',', $languages));

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

        return redirect($url);
    }
1155387
private void mergeTree(Block targetTree, Block sourceTree)
    {
        for (Block block : sourceTree.getChildren()) {
            // Check if the current block exists in the target tree at the same place in the tree
            int pos = indexOf(targetTree.getChildren(), block);
            if (pos > -1) {
                Block foundBlock = targetTree.getChildren().get(pos);
                mergeTree(foundBlock, block);
            } else {
                targetTree.addChild(block);
            }
        }
    }
863821
private function faqsForTree(Tree $tree): Collection
    {
        return DB::table('block')
            ->join('block_setting AS bs1', 'bs1.block_id', '=', 'block.block_id')
            ->join('block_setting AS bs2', 'bs2.block_id', '=', 'block.block_id')
            ->join('block_setting AS bs3', 'bs3.block_id', '=', 'block.block_id')
            ->where('module_name', '=', $this->name())
            ->where('bs1.setting_name', '=', 'header')
            ->where('bs2.setting_name', '=', 'faqbody')
            ->where('bs3.setting_name', '=', 'languages')
            ->where(static function (Builder $query) use ($tree): void {
                $query
                    ->whereNull('gedcom_id')
                    ->orWhere('gedcom_id', '=', $tree->id());
            })
            ->orderBy('block_order')
            ->select(['block.block_id', 'block_order', 'gedcom_id', 'bs1.setting_value AS header', 'bs2.setting_value AS faqbody', 'bs3.setting_value AS languages'])
            ->get();
    }
488200
public function getBlock(Tree $tree, int $block_id, string $ctype = '', array $cfg = []): string
    {
        $show_other      = $this->getBlockSetting($block_id, 'show_other', self::DEFAULT_SHOW_OTHER);
        $show_unassigned = $this->getBlockSetting($block_id, 'show_unassigned', self::DEFAULT_SHOW_UNASSIGNED);
        $show_future     = $this->getBlockSetting($block_id, 'show_future', self::DEFAULT_SHOW_FUTURE);

        extract($cfg, EXTR_OVERWRITE);

        $end_jd      = $show_future ? Carbon::maxValue()->julianDay() : Carbon::now()->julianDay();
        $individuals = $this->individualsWithTasks($tree, $end_jd);
        $families    = $this->familiesWithTasks($tree, $end_jd);

        /** @var GedcomRecord[] $records */
        $records = $individuals->merge($families);

        $tasks = [];

        foreach ($records as $record) {
            foreach ($record->facts(['_TODO']) as $task) {
                $user_name = $task->attribute('_WT_USER');

                if ($user_name === Auth::user()->userName() || empty($user_name) && $show_unassigned || !empty($user_name) && $show_other) {
                    $tasks[] = $task;
                }
            }
        }

        if (empty($records)) {
            $content = '<p>' . I18N::translate('There are no research tasks in this family tree.') . '</p>';
        } else {
            $content = view('modules/todo/research-tasks', ['tasks' => $tasks]);
        }

        if ($ctype !== '') {
            if ($ctype === 'gedcom' && Auth::isManager($tree)) {
                $config_url = route('tree-page-block-edit', [
                    'block_id' => $block_id,
                    'ged'      => $tree->name(),
                ]);
            } elseif ($ctype === 'user' && Auth::check()) {
                $config_url = route('user-page-block-edit', [
                    'block_id' => $block_id,
                    'ged'      => $tree->name(),
                ]);
            } else {
                $config_url = '';
            }

            return view('modules/block-template', [
                'block'      => Str::kebab($this->name()),
                'id'         => $block_id,
                'config_url' => $config_url,
                'title'      => $this->title(),
                'content'    => $content,
            ]);
        }

        return $content;
    }
1101567
synchronized void addBlock(Block block) {
    if (!isInitialized()) {
      return;
    }
    
    BlockScanInfo info = blockMap.get(block);
    if ( info != null ) {
      LOG.warn("Adding an already existing block " + block);
      delBlockInfo(info);
    }
    
    info = new BlockScanInfo(block);    
    info.lastScanTime = getNewBlockScanTime();
    
    addBlockInfo(info);
    adjustThrottler();
  }
766014
public function postAdminDeleteAction(ServerRequestInterface $request, Tree $tree): ResponseInterface
    {
        $block_id = (int) $request->get('block_id');

        DB::table('block_setting')->where('block_id', '=', $block_id)->delete();

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

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

        return redirect($url);
    }
890487
public function getBlock(Tree $tree, int $block_id, string $ctype = '', array $cfg = []): string
    {
        $calendar_service = new CalendarService();

        $days      = (int) $this->getBlockSetting($block_id, 'days', self::DEFAULT_DAYS);
        $infoStyle = $this->getBlockSetting($block_id, 'infoStyle', self::DEFAULT_STYLE);
        $calendar  = $this->getBlockSetting($block_id, 'calendar', self::DEFAULT_CALENDAR);

        extract($cfg, EXTR_OVERWRITE);

        $jewish_calendar = new JewishCalendar();
        $startjd         = Carbon::now()->julianDay();
        $endjd           = $startjd + $days - 1;

        // The standard anniversary rules cover most of the Yahrzeit rules, we just
        // need to handle a few special cases.
        // Fetch normal anniversaries, with an extra day before/after
        $yahrzeits = [];
        for ($jd = $startjd - 1; $jd <= $endjd + $days; ++$jd) {
            foreach ($calendar_service->getAnniversaryEvents($jd, 'DEAT _YART', $tree) as $fact) {
                // Exact hebrew dates only
                $date = $fact->date();
                if ($date->minimumDate() instanceof JewishDate && $date->minimumJulianDay() === $date->maximumJulianDay()) {
                    // ...then adjust DEAT dates (but not _YART)
                    if ($fact->getTag() === 'DEAT') {
                        $today     = new JewishDate($jd);
                        $hd        = $fact->date()->minimumDate();
                        $hd1       = new JewishDate($hd);
                        ++$hd1->year;
                        $hd1->setJdFromYmd();
                        // Special rules. See http://www.hebcal.com/help/anniv.html
                        // Everything else is taken care of by our standard anniversary rules.
                        if ($hd->day == 30 && $hd->month == 2 && $hd->year != 0 && $hd1->daysInMonth() < 30) {
                            // 30 CSH - Last day in CSH
                            $jd = $jewish_calendar->ymdToJd($today->year, 3, 1) - 1;
                        } elseif ($hd->day == 30 && $hd->month == 3 && $hd->year != 0 && $hd1->daysInMonth() < 30) {
                            // 30 KSL - Last day in KSL
                            $jd = $jewish_calendar->ymdToJd($today->year, 4, 1) - 1;
                        } elseif ($hd->day == 30 && $hd->month == 6 && $hd->year != 0 && $today->daysInMonth() < 30 && !$today->isLeapYear()) {
                            // 30 ADR - Last day in SHV
                            $jd = $jewish_calendar->ymdToJd($today->year, 6, 1) - 1;
                        }
                    }

                    // Filter adjusted dates to our date range
                    if ($jd >= $startjd && $jd < $startjd + $days) {
                        // upcomming yahrzeit dates
                        switch ($calendar) {
                            case 'gregorian':
                                $yahrzeit_date = new GregorianDate($jd);
                                break;
                            case 'jewish':
                            default:
                                $yahrzeit_date = new JewishDate($jd);
                                break;
                        }
                        $yahrzeit_date = new Date($yahrzeit_date->format('%@ %A %O %E'));

                        $yahrzeits[] = (object) [
                            'individual'    => $fact->record(),
                            'fact_date'     => $fact->date(),
                            'fact'          => $fact,
                            'jd'            => $jd,
                            'yahrzeit_date' => $yahrzeit_date,
                        ];
                    }
                }
            }
        }

        switch ($infoStyle) {
            case 'list':
                $content = view('modules/yahrzeit/list', [
                    'yahrzeits' => $yahrzeits,
                ]);
                break;
            case 'table':
            default:
                $content = view('modules/yahrzeit/table', [
                    'yahrzeits' => $yahrzeits,
                ]);
                break;
        }

        if ($ctype !== '') {
            if ($ctype === 'gedcom' && Auth::isManager($tree)) {
                $config_url = route('tree-page-block-edit', [
                    'block_id' => $block_id,
                    'ged'      => $tree->name(),
                ]);
            } elseif ($ctype === 'user' && Auth::check()) {
                $config_url = route('user-page-block-edit', [
                    'block_id' => $block_id,
                    'ged'      => $tree->name(),
                ]);
            } else {
                $config_url = '';
            }

            return view('modules/block-template', [
                'block'      => Str::kebab($this->name()),
                'id'         => $block_id,
                'config_url' => $config_url,
                'title'      => $this->title(),
                'content'    => $content,
            ]);
        }

        return $content;
    }
892161
public function editEditor($module_id, $file_id)
    {
        $this->setModuleEditor($module_id);
        $this->setFilePathEditor($file_id);
        $this->setTitleEditEditor();
        $this->setBreadcrumbEditEditor();
        $this->setMessageEditEditor();

        $this->setData('module', $this->data_module);
        $this->setData('can_save', $this->canSaveEditor());
        $this->setData('lines', count(file($this->data_file)));
        $this->setData('editor.content', file_get_contents($this->data_file));

        $this->submitEditor();
        $this->setJsSettingsEditor();
        $this->outputEditEditor();
    }
1300135
private function spliceTree($envs, Block $block, $without)
    {
        $newBlock = null;

        foreach ($envs as $e) {
            if (! isset($e->block)) {
                continue;
            }

            if ($e->block === $block) {
                continue;
            }

            if (isset($e->block->type) && $e->block->type === Type::T_AT_ROOT) {
                continue;
            }

            if ($e->block && $this->isWithout($without, $e->block)) {
                continue;
            }

            $b = new Block;
            $b->sourceName   = $e->block->sourceName;
            $b->sourceIndex  = $e->block->sourceIndex;
            $b->sourceLine   = $e->block->sourceLine;
            $b->sourceColumn = $e->block->sourceColumn;
            $b->selectors    = [];
            $b->comments     = $e->block->comments;
            $b->parent       = null;

            if ($newBlock) {
                $type = isset($newBlock->type) ? $newBlock->type : Type::T_BLOCK;

                $b->children = [[$type, $newBlock]];

                $newBlock->parent = $b;
            } elseif (count($block->children)) {
                foreach ($block->children as $child) {
                    if ($child[0] === Type::T_BLOCK) {
                        $child[1]->parent = $b;
                    }
                }

                $b->children = $block->children;
            }

            if (isset($e->block->type)) {
                $b->type = $e->block->type;
            }

            if (isset($e->block->name)) {
                $b->name = $e->block->name;
            }

            if (isset($e->block->queryList)) {
                $b->queryList = $e->block->queryList;
            }

            if (isset($e->block->value)) {
                $b->value = $e->block->value;
            }

            $newBlock = $b;
        }

        $type = isset($newBlock->type) ? $newBlock->type : Type::T_BLOCK;

        return [$type, $newBlock];
    }
321523
protected function buildBlock($id)
    {
        $blockType = $this->rawLayout->getProperty($id, RawLayout::BLOCK_TYPE, true);
        $options   = $this->rawLayout->getProperty($id, RawLayout::OPTIONS, true);
        $types     = $this->typeHelper->getTypes($blockType);

        $this->setBlockResolvedOptions($id, $blockType, $options, $types);
    }
335373
public function getBlock(Tree $tree, int $block_id, string $ctype = '', array $cfg = []): string
    {
        $statistics = app(Statistics::class);

        $show_last_update     = $this->getBlockSetting($block_id, 'show_last_update', '1');
        $show_common_surnames = $this->getBlockSetting($block_id, 'show_common_surnames', '1');
        $number_of_surnames   = (int) $this->getBlockSetting($block_id, 'number_of_surnames', self::DEFAULT_NUMBER_OF_SURNAMES);
        $stat_indi            = $this->getBlockSetting($block_id, 'stat_indi', '1');
        $stat_fam             = $this->getBlockSetting($block_id, 'stat_fam', '1');
        $stat_sour            = $this->getBlockSetting($block_id, 'stat_sour', '1');
        $stat_media           = $this->getBlockSetting($block_id, 'stat_media', '1');
        $stat_repo            = $this->getBlockSetting($block_id, 'stat_repo', '1');
        $stat_surname         = $this->getBlockSetting($block_id, 'stat_surname', '1');
        $stat_events          = $this->getBlockSetting($block_id, 'stat_events', '1');
        $stat_users           = $this->getBlockSetting($block_id, 'stat_users', '1');
        $stat_first_birth     = $this->getBlockSetting($block_id, 'stat_first_birth', '1');
        $stat_last_birth      = $this->getBlockSetting($block_id, 'stat_last_birth', '1');
        $stat_first_death     = $this->getBlockSetting($block_id, 'stat_first_death', '1');
        $stat_last_death      = $this->getBlockSetting($block_id, 'stat_last_death', '1');
        $stat_long_life       = $this->getBlockSetting($block_id, 'stat_long_life', '1');
        $stat_avg_life        = $this->getBlockSetting($block_id, 'stat_avg_life', '1');
        $stat_most_chil       = $this->getBlockSetting($block_id, 'stat_most_chil', '1');
        $stat_avg_chil        = $this->getBlockSetting($block_id, 'stat_avg_chil', '1');

        extract($cfg, EXTR_OVERWRITE);

        if ($show_common_surnames) {
            // Use the count of base surnames.
            $top_surnames = DB::table('name')
                ->where('n_file', '=', $tree->id())
                ->where('n_type', '<>', '_MARNM')
                ->whereNotIn('n_surn', ['@N.N.', ''])
                ->groupBy('n_surn')
                ->orderByDesc(DB::raw('COUNT(n_surn)'))
                ->take($number_of_surnames)
                ->pluck('n_surn');

            $all_surnames = [];

            foreach ($top_surnames as $top_surname) {
                $variants = DB::table('name')
                    ->where('n_file', '=', $tree->id())
                    ->where(DB::raw('n_surn /*! COLLATE utf8_bin */'), '=', $top_surname)
                    ->groupBy('surname')
                    ->select([DB::raw('n_surname /*! COLLATE utf8_bin */ AS surname'), DB::raw('count(*) AS total')])
                    ->pluck('total', 'surname')
                    ->all();

                $all_surnames[$top_surname] = $variants;
            }

            uksort($all_surnames, [I18N::class, 'strcasecmp']);

            //find a module providing individual lists
            $module = app(ModuleService::class)->findByComponent(ModuleListInterface::class, $tree, Auth::user())->first(static function (ModuleInterface $module) {
                return $module instanceof IndividualListModule;
            });
            
            $surnames = FunctionsPrintLists::surnameList($all_surnames, 2, false, $module, $tree);
        } else {
            $surnames = '';
        }

        $content = view('modules/gedcom_stats/statistics', [
            'show_last_update'     => $show_last_update,
            'show_common_surnames' => $show_common_surnames,
            'number_of_surnames'   => $number_of_surnames,
            'stat_indi'            => $stat_indi,
            'stat_fam'             => $stat_fam,
            'stat_sour'            => $stat_sour,
            'stat_media'           => $stat_media,
            'stat_repo'            => $stat_repo,
            'stat_surname'         => $stat_surname,
            'stat_events'          => $stat_events,
            'stat_users'           => $stat_users,
            'stat_first_birth'     => $stat_first_birth,
            'stat_last_birth'      => $stat_last_birth,
            'stat_first_death'     => $stat_first_death,
            'stat_last_death'      => $stat_last_death,
            'stat_long_life'       => $stat_long_life,
            'stat_avg_life'        => $stat_avg_life,
            'stat_most_chil'       => $stat_most_chil,
            'stat_avg_chil'        => $stat_avg_chil,
            'stats'                => $statistics,
            'surnames'             => $surnames,
        ]);

        if ($ctype !== '') {
            if ($ctype === 'gedcom' && Auth::isManager($tree)) {
                $config_url = route('tree-page-block-edit', [
                    'block_id' => $block_id,
                    'ged'      => $tree->name(),
                ]);
            } elseif ($ctype === 'user' && Auth::check()) {
                $config_url = route('user-page-block-edit', [
                    'block_id' => $block_id,
                    'ged'      => $tree->name(),
                ]);
            } else {
                $config_url = '';
            }

            return view('modules/block-template', [
                'block'      => Str::kebab($this->name()),
                'id'         => $block_id,
                'config_url' => $config_url,
                'title'      => $this->title(),
                'content'    => $content,
            ]);
        }

        return $content;
    }
677227
public function createBlockTranslation(Block $block, string $locale): void
    {
        $query = $this->connection->createQueryBuilder()
            ->insert('ngbm_block_translation')
            ->values(
                [
                    'block_id' => ':block_id',
                    'status' => ':status',
                    'locale' => ':locale',
                    'parameters' => ':parameters',
                ]
            )
            ->setParameter('block_id', $block->id, Type::INTEGER)
            ->setParameter('status', $block->status, Type::INTEGER)
            ->setParameter('locale', $locale, Type::STRING)
            ->setParameter('parameters', $block->parameters[$locale], Type::JSON_ARRAY);

        $query->execute();
    }
513314
public function getBlock(Tree $tree, int $block_id, string $ctype = '', array $cfg = []): string
    {
        $sendmail = (bool) $this->getBlockSetting($block_id, 'sendmail', '1');
        $days     = (int) $this->getBlockSetting($block_id, 'days', '1');

        extract($cfg, EXTR_OVERWRITE);

        $changes_exist = DB::table('change')
            ->where('status', 'pending')
            ->exists();

        if ($changes_exist && $sendmail) {
            $last_email_timestamp = Carbon::createFromTimestamp((int) Site::getPreference('LAST_CHANGE_EMAIL'));
            $next_email_timestamp = $last_email_timestamp->addDays($days);

            // There are pending changes - tell moderators/managers/administrators about them.
            if ($next_email_timestamp < Carbon::now()) {
                // Which users have pending changes?
                foreach ($this->user_service->all() as $user) {
                    if ($user->getPreference('contactmethod') !== 'none') {
                        foreach (Tree::getAll() as $tmp_tree) {
                            if ($tmp_tree->hasPendingEdit() && Auth::isManager($tmp_tree, $user)) {
                                I18N::init($user->getPreference('language'));

                                Mail::send(
                                    new TreeUser($tmp_tree),
                                    $user,
                                    new TreeUser($tmp_tree),
                                    I18N::translate('Pending changes'),
                                    view('emails/pending-changes-text', [
                                        'tree' => $tmp_tree,
                                        'user' => $user,
                                    ]),
                                    view('emails/pending-changes-html', [
                                        'tree' => $tmp_tree,
                                        'user' => $user,
                                    ])
                                );
                                I18N::init(WT_LOCALE);
                            }
                        }
                    }
                }
                Site::setPreference('LAST_CHANGE_EMAIL', (string) Carbon::now()->unix());
            }
        }
        if (Auth::isEditor($tree) && $tree->hasPendingEdit()) {
            $content = '';
            if (Auth::isModerator($tree)) {
                $content .= '<a href="' . e(route('show-pending', ['ged' => $tree->name()])) . '">' . I18N::translate('There are pending changes for you to moderate.') . '</a><br>';
            }
            if ($sendmail) {
                $last_email_timestamp = Carbon::createFromTimestamp((int) Site::getPreference('LAST_CHANGE_EMAIL'));
                $next_email_timestamp = $last_email_timestamp->copy()->addDays($days);

                $content .= I18N::translate('Last email reminder was sent ') . view('components/datetime', ['timestamp' => $last_email_timestamp]) . '<br>';
                $content .= I18N::translate('Next email reminder will be sent after ') . view('components/datetime', ['timestamp' => $next_email_timestamp]) . '<br><br>';
            }
            $content .= '<ul>';

            $changes = DB::table('change')
                ->where('gedcom_id', '=', $tree->id())
                ->where('status', '=', 'pending')
                ->select(['xref'])
                ->get();

            foreach ($changes as $change) {
                $record = GedcomRecord::getInstance($change->xref, $tree);
                if ($record->canShow()) {
                    $content .= '<li><a href="' . e($record->url()) . '">' . $record->fullName() . '</a></li>';
                }
            }
            $content .= '</ul>';

            if ($ctype !== '') {
                if ($ctype === 'gedcom' && Auth::isManager($tree)) {
                    $config_url = route('tree-page-block-edit', [
                        'block_id' => $block_id,
                        'ged'      => $tree->name(),
                    ]);
                } elseif ($ctype === 'user' && Auth::check()) {
                    $config_url = route('user-page-block-edit', [
                        'block_id' => $block_id,
                        'ged'      => $tree->name(),
                    ]);
                } else {
                    $config_url = '';
                }

                return view('modules/block-template', [
                    'block'      => Str::kebab($this->name()),
                    'id'         => $block_id,
                    'config_url' => $config_url,
                    'title'      => $this->title(),
                    'content'    => $content,
                ]);
            }

            return $content;
        }

        return '';
    }
930929
function (blockList) {
        this.errors = [];
        this.tree = new Node("file", null);
        this.tree.content = [];

        this._advance(blockList, 0, this.tree.content);

        this._postProcessTree();
    }
364020
private function getBlockModule(Tree $tree, int $block_id): ModuleBlockInterface
    {
        $active_blocks = $this->module_service->findByComponent(ModuleBlockInterface::class, $tree, Auth::user());

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

        $block = $active_blocks->first(static function (ModuleInterface $module) use ($module_name): bool {
            return $module->name() === $module_name;
        });

        if ($block === null) {
            throw new NotFoundHttpException('Block not found');
        }

        return $block;
    }