Return a menu item for this report.

@param Individual $individual

@return Menu
493383
public function getReportMenu(Individual $individual): Menu
    {
        return new Menu(
            $this->title(),
            route('report-setup', [
                'xref'   => $individual->xref(),
                'ged'    => $individual->tree()->name(),
                'report' => $this->name(),
            ]),
            'menu-report-' . $this->name(),
            ['rel' => 'nofollow']
        );
    }
345324
public function generate(Individual $individual, Individual $head): string
    {
        $mother = $this->mother($individual);

        if ($mother && $this->lastPartOfPlace($mother->getBirthPlace()->gedcomName()) !== $this->place()) {
            return 'Y';
        }

        return '';
    }
503640
public function generate(Individual $individual, Individual $head): string
    {
        switch ($individual->sex()) {
            case 'M':
                return $this->male;
            case 'F':
                return $this->female;
            default:
                return '';
        }
    }
894753
protected function chart(Individual $individual, int $chart_style, int $fan_width, int $generations, ChartService $chart_service): ResponseInterface
    {
        $ancestors = $chart_service->sosaStradonitzAncestors($individual, $generations);

        $gen  = $generations - 1;
        $sosa = 2 ** $generations - 1;

        // fan size
        $fanw = 640 * $fan_width / 100;
        $cx   = $fanw / 2 - 1; // center x
        $cy   = $cx; // center y
        $rx   = $fanw - 1;
        $rw   = $fanw / ($gen + 1);
        $fanh = $fanw; // fan height
        if ($chart_style === self::STYLE_HALF_CIRCLE) {
            $fanh = $fanh * ($gen + 1) / ($gen * 2);
        }
        if ($chart_style === self::STYLE_THREE_QUARTER_CIRCLE) {
            $fanh *= 0.86;
        }
        $scale = $fanw / 640;

        // Create the image
        $image = imagecreate((int) $fanw, (int) $fanh);

        // Create colors
        $transparent = imagecolorallocate($image, 0, 0, 0);
        imagecolortransparent($image, $transparent);

        $theme = app(ModuleThemeInterface::class);

        $foreground = $this->imageColor($image, $theme->parameter('chart-font-color'));

        $backgrounds = [
            'M' => $this->imageColor($image, $theme->parameter('chart-background-m')),
            'F' => $this->imageColor($image, $theme->parameter('chart-background-f')),
            'U' => $this->imageColor($image, $theme->parameter('chart-background-u')),
        ];

        imagefilledrectangle($image, 0, 0, (int) $fanw, (int) $fanh, $transparent);

        $fandeg = 90 * $chart_style;

        // Popup menus for each ancestor
        $html = '';

        // Areas for the imagemap
        $areas = '';

        // loop to create fan cells
        while ($gen >= 0) {
            // clean current generation area
            $deg2 = 360 + ($fandeg - 180) / 2;
            $deg1 = $deg2 - $fandeg;
            imagefilledarc($image, (int) $cx, (int) $cy, (int) $rx, (int) $rx, (int) $deg1, (int) $deg2, $backgrounds['U'], IMG_ARC_PIE);
            $rx -= 3;

            // calculate new angle
            $p2    = 2 ** $gen;
            $angle = $fandeg / $p2;
            $deg2  = 360 + ($fandeg - 180) / 2;
            $deg1  = $deg2 - $angle;
            // special case for rootid cell
            if ($gen == 0) {
                $deg1 = 90;
                $deg2 = 360 + $deg1;
            }

            // draw each cell
            while ($sosa >= $p2) {
                if ($ancestors->has($sosa)) {
                    $person  = $ancestors->get($sosa);
                    $name    = $person->fullName();
                    $addname = $person->alternateName();

                    $text = I18N::reverseText($name);
                    if ($addname) {
                        $text .= "\n" . I18N::reverseText($addname);
                    }

                    $text .= "\n" . I18N::reverseText($person->getLifeSpan());

                    $background = $backgrounds[$person->sex()];

                    imagefilledarc($image, (int) $cx, (int) $cy, (int) $rx, (int) $rx, (int) $deg1, (int) $deg2, $background, IMG_ARC_PIE);

                    // split and center text by lines
                    $wmax = (int) ($angle * 7 / 7 * $scale);
                    $wmax = min($wmax, 35 * $scale);
                    if ($gen == 0) {
                        $wmax = min($wmax, 17 * $scale);
                    }
                    $text = $this->splitAlignText($text, (int) $wmax);

                    // text angle
                    $tangle = 270 - ($deg1 + $angle / 2);
                    if ($gen == 0) {
                        $tangle = 0;
                    }

                    // calculate text position
                    $deg = $deg1 + 0.44;
                    if ($deg2 - $deg1 > 40) {
                        $deg = $deg1 + ($deg2 - $deg1) / 11;
                    }
                    if ($deg2 - $deg1 > 80) {
                        $deg = $deg1 + ($deg2 - $deg1) / 7;
                    }
                    if ($deg2 - $deg1 > 140) {
                        $deg = $deg1 + ($deg2 - $deg1) / 4;
                    }
                    if ($gen == 0) {
                        $deg = 180;
                    }
                    $rad = deg2rad($deg);
                    $mr  = ($rx - $rw / 4) / 2;
                    if ($gen > 0 && $deg2 - $deg1 > 80) {
                        $mr = $rx / 2;
                    }
                    $tx = $cx + $mr * cos($rad);
                    $ty = $cy + $mr * sin($rad);
                    if ($sosa == 1) {
                        $ty -= $mr / 2;
                    }

                    // print text
                    imagettftext(
                        $image,
                        7,
                        $tangle,
                        (int) $tx,
                        (int) $ty,
                        $foreground,
                        Webtrees::ROOT_DIR . 'resources/fonts/DejaVuSans.ttf',
                        $text
                    );

                    $areas .= '<area shape="poly" coords="';
                    // plot upper points
                    $mr  = $rx / 2;
                    $deg = $deg1;
                    while ($deg <= $deg2) {
                        $rad   = deg2rad($deg);
                        $tx    = round($cx + $mr * cos($rad));
                        $ty    = round($cy + $mr * sin($rad));
                        $areas .= "$tx,$ty,";
                        $deg   += ($deg2 - $deg1) / 6;
                    }
                    // plot lower points
                    $mr  = ($rx - $rw) / 2;
                    $deg = $deg2;
                    while ($deg >= $deg1) {
                        $rad   = deg2rad($deg);
                        $tx    = round($cx + $mr * cos($rad));
                        $ty    = round($cy + $mr * sin($rad));
                        $areas .= "$tx,$ty,";
                        $deg   -= ($deg2 - $deg1) / 6;
                    }
                    // join first point
                    $mr    = $rx / 2;
                    $deg   = $deg1;
                    $rad   = deg2rad($deg);
                    $tx    = round($cx + $mr * cos($rad));
                    $ty    = round($cy + $mr * sin($rad));
                    $areas .= "$tx,$ty";
                    // add action url
                    $areas .= '" href="#' . $person->xref() . '"';
                    $html  .= '<div id="' . $person->xref() . '" class="fan_chart_menu">';
                    $html  .= '<div class="person_box"><div class="details1">';
                    $html .= '<div class="charts">';
                    $html  .= '<a href="' . e($person->url()) . '" class="dropdown-item">' . $name . '</a>';
                    foreach ($theme->individualBoxMenu($person) as $menu) {
                        $html .= '<a href="' . e($menu->getLink()) . '" class="dropdown-item p-1 ' . e($menu->getClass()) . '">' . $menu->getLabel() . '</a>';
                    }
                    $html  .= '</div>';
                    $html  .= '</div></div>';
                    $html  .= '</div>';
                    $areas .= ' alt="' . strip_tags($person->fullName()) . '" title="' . strip_tags($person->fullName()) . '">';
                }
                $deg1 -= $angle;
                $deg2 -= $angle;
                $sosa--;
            }
            $rx -= $rw;
            $gen--;
        }

        ob_start();
        imagepng($image);
        imagedestroy($image);
        $png = ob_get_clean();

        return response(view('modules/fanchart/chart', [
            'fanh'  => $fanh,
            'fanw'  => $fanw,
            'html'  => $html,
            'areas' => $areas,
            'png'   => $png,
            'title' => $this->chartTitle($individual),
        ]));
    }
1224745
public function getTabs(Individual $individual): Collection
    {
        return $this->module_service->findByComponent(ModuleTabInterface::class, $individual->tree(), Auth::user())
            ->filter(static function (ModuleTabInterface $tab) use ($individual): bool {
                return $tab->hasTabContent($individual);
            });
    }
992177
private function getPersonalFacts(Individual $individual): Collection
    {
        $facts = $individual->facts();

        foreach ($individual->spouseFamilies() as $family) {
            $facts = $facts->merge($family->facts());
            // Add birth of children from this family to the facts array
            foreach ($family->children() as $child) {
                $childsBirth = $child->facts(['BIRT'])->first();
                if ($childsBirth && $childsBirth->place()->gedcomName() !== '') {
                    $facts->push($childsBirth);
                }
            }
        }

        $facts = Fact::sortFacts($facts);

        return $facts->filter(static function (Fact $item): bool {
            return $item->place()->gedcomName() !== '';
        });
    }
939935
public function generate(Individual $individual, Individual $head): string
    {
        if ($individual->getBirthDate()->isOK()) {
            foreach ($individual->spouseFamilies() as $family) {
                foreach ($family->facts(['MARR'], true) as $fact) {
                    if ($fact->date()->isOK()) {
                        return (string) Date::getAgeYears($individual->getBirthDate(), $fact->date());
                    }
                }
            }
        }

        return '';
    }
655231
public function spouseFamily(Individual $individual): ?Family
    {
        // Exclude families that were created after this census date
        $families = [];
        foreach ($individual->spouseFamilies() as $family) {
            if (Date::compare($family->getMarriageDate(), $this->date()) <= 0) {
                $families[] = $family;
            }
        }

        if (empty($families)) {
            return null;
        }

        usort($families, static function (Family $x, Family $y): int {
            return Date::compare($x->getMarriageDate(), $y->getMarriageDate());
        });

        return end($families);
    }
516071
public function individualList(ServerRequestInterface $request, Tree $tree, UserInterface $user, ?ModuleListInterface $moduleListInterface): ResponseInterface
    {
        return $this->individualOrFamilyList($request, $tree, $user, false, $moduleListInterface);
    }
926622
public static function sortableNames(Individual $individual) {
        $names   = $individual->getAllNames();
        $primary = $individual->getPrimaryName();

        list($surn, $givn) = explode(',', $names[$primary]['sort']);

        $givn = str_replace('@P.N.', 'AAAA', $givn);
        $surn = str_replace('@N.N.', 'AAAA', $surn);

        return array(
            $surn . 'AAAA' . $givn,
            $givn . 'AAAA' . $surn,
        );
    }
206038
public function chart(Individual $individual, int $generations, int $book_size, bool $show_spouse): ResponseInterface
    {
        $html = view('modules/family-book-chart/chart', ['individual' => $individual, 'generations' => $generations, 'book_size' => $book_size, 'show_spouse' => $show_spouse]);

        return response($html);
    }
958638
public function getUserMenu() {
    $event = new BuildUserMenuEvent();
    $this->trigger(self::EVENT_BUILD_USER_MENU, $event);
    $menu = [];
    foreach ($event->items as $block => $items) {
      foreach ($items as $item) {
        $menu[] = $item;
      }
    }
    return $menu;
  }
290871
private function getFactsWithMedia(Individual $individual): Collection
    {
        if ($this->facts === null) {
            $facts = $individual->facts();
            foreach ($individual->spouseFamilies() as $family) {
                if ($family->canShow()) {
                    foreach ($family->facts() as $fact) {
                        $facts->push($fact);
                    }
                }
            }

            $this->facts = new Collection();

            foreach ($facts as $fact) {
                if (preg_match('/(?:^1|\n\d) OBJE @' . Gedcom::REGEX_XREF . '@/', $fact->gedcom())) {
                    $this->facts->push($fact);
                }
            }
            $this->facts = Fact::sortFacts($this->facts);
        }

        return $this->facts;
    }
666686
public function chart(Individual $individual1, Individual $individual2, int $recursion, int $ancestors): ResponseInterface
    {
        $tree = $individual1->tree();

        $max_recursion = (int) $tree->getPreference('RELATIONSHIP_RECURSION', static::DEFAULT_RECURSION);

        $recursion = min($recursion, $max_recursion);

        $paths = $this->calculateRelationships($individual1, $individual2, $recursion, (bool) $ancestors);

        // @TODO - convert to views
        ob_start();
        if (I18N::direction() === 'ltr') {
            $diagonal1 = asset('css/images/dline.png');
            $diagonal2 = asset('css/images/dline2.png');
        } else {
            $diagonal1 = asset('css/images/dline2.png');
            $diagonal2 = asset('css/images/dline.png');
        }

        $num_paths = 0;
        foreach ($paths as $path) {
            // Extract the relationship names between pairs of individuals
            $relationships = $this->oldStyleRelationshipPath($tree, $path);
            if (empty($relationships)) {
                // Cannot see one of the families/individuals, due to privacy;
                continue;
            }
            echo '<h3>', I18N::translate('Relationship: %s', Functions::getRelationshipNameFromPath(implode('', $relationships), $individual1, $individual2)), '</h3>';
            $num_paths++;

            // Use a table/grid for layout.
            $table = [];
            // Current position in the grid.
            $x = 0;
            $y = 0;
            // Extent of the grid.
            $min_y = 0;
            $max_y = 0;
            $max_x = 0;
            // For each node in the path.
            foreach ($path as $n => $xref) {
                if ($n % 2 === 1) {
                    switch ($relationships[$n]) {
                        case 'hus':
                        case 'wif':
                        case 'spo':
                        case 'bro':
                        case 'sis':
                        case 'sib':
                            $table[$x + 1][$y] = '<div style="background:url(' . e(asset('css/images/hline.png')) . ') repeat-x center;  width: 94px; text-align: center"><div class="hline-text" style="height: 32px;">' . Functions::getRelationshipNameFromPath($relationships[$n], Individual::getInstance($path[$n - 1], $tree), Individual::getInstance($path[$n + 1], $tree)) . '</div><div style="height: 32px;">' . view('icons/arrow-right') . '</div></div>';
                            $x                 += 2;
                            break;
                        case 'son':
                        case 'dau':
                        case 'chi':
                            if ($n > 2 && preg_match('/fat|mot|par/', $relationships[$n - 2])) {
                                $table[$x + 1][$y - 1] = '<div style="background:url(' . $diagonal2 . '); width: 64px; height: 64px; text-align: center;"><div style="height: 32px; text-align: end;">' . Functions::getRelationshipNameFromPath($relationships[$n], Individual::getInstance($path[$n - 1], $tree), Individual::getInstance($path[$n + 1], $tree)) . '</div><div style="height: 32px; text-align: start;">' . view('icons/arrow-down') . '</div></div>';
                                $x                     += 2;
                            } else {
                                $table[$x][$y - 1] = '<div style="background:url(' . e('"' . asset('css/images/vline.png') . '"') . ') repeat-y center; height: 64px; text-align: center;"><div class="vline-text" style="display: inline-block; width:50%; line-height: 64px;">' . Functions::getRelationshipNameFromPath($relationships[$n], Individual::getInstance($path[$n - 1], $tree), Individual::getInstance($path[$n + 1], $tree)) . '</div><div style="display: inline-block; width:50%; line-height: 64px;">' . view('icons/arrow-down') . '</div></div>';
                            }
                            $y -= 2;
                            break;
                        case 'fat':
                        case 'mot':
                        case 'par':
                            if ($n > 2 && preg_match('/son|dau|chi/', $relationships[$n - 2])) {
                                $table[$x + 1][$y + 1] = '<div style="background:url(' . $diagonal1 . '); background-position: top right; width: 64px; height: 64px; text-align: center;"><div style="height: 32px; text-align: start;">' . Functions::getRelationshipNameFromPath($relationships[$n], Individual::getInstance($path[$n - 1], $tree), Individual::getInstance($path[$n + 1], $tree)) . '</div><div style="height: 32px; text-align: end;">' . view('icons/arrow-down') . '</div></div>';
                                $x                     += 2;
                            } else {
                                $table[$x][$y + 1] = '<div style="background:url(' . e('"' . asset('css/images/vline.png') . '"') . ') repeat-y center; height: 64px; text-align:center; "><div class="vline-text" style="display: inline-block; width: 50%; line-height: 64px;">' . Functions::getRelationshipNameFromPath($relationships[$n], Individual::getInstance($path[$n - 1], $tree), Individual::getInstance($path[$n + 1], $tree)) . '</div><div style="display: inline-block; width: 50%; line-height: 32px">' . view('icons/arrow-up') . '</div></div>';
                            }
                            $y += 2;
                            break;
                    }
                    $max_x = max($max_x, $x);
                    $min_y = min($min_y, $y);
                    $max_y = max($max_y, $y);
                } else {
                    $individual    = Individual::getInstance($xref, $tree);
                    $table[$x][$y] = view('chart-box', ['individual' => $individual]);
                }
            }
            echo '<div class="wt-chart wt-chart-relationships">';
            echo '<table style="border-collapse: collapse; margin: 20px 50px;">';
            for ($y = $max_y; $y >= $min_y; --$y) {
                echo '<tr>';
                for ($x = 0; $x <= $max_x; ++$x) {
                    echo '<td style="padding: 0;">';
                    if (isset($table[$x][$y])) {
                        echo $table[$x][$y];
                    }
                    echo '</td>';
                }
                echo '</tr>';
            }
            echo '</table>';
            echo '</div>';
        }

        if (!$num_paths) {
            echo '<p>', I18N::translate('No link between the two individuals could be found.'), '</p>';
        }

        $html = ob_get_clean();

        return response($html);
    }
678163
public function generate(Individual $individual, Individual $head): string
    {
        if ($individual->sex() === 'M') {
            return '';
        }

        return (string) Date::getAgeYears($individual->getEstimatedBirthDate(), $this->date());
    }
971219
public function chartMenu(Individual $individual): Menu
    {
        return new Menu(
            $this->title(),
            $this->chartUrl($individual),
            $this->chartMenuClass(),
            $this->chartUrlAttributes()
        );
    }
969202
public function generate(Individual $individual, Individual $head): string
    {
        $family = $this->spouseFamily($individual);
        $sex    = $individual->sex();

        if ($family === null || $family->facts(['MARR'])->isEmpty()) {
            if ($this->isChild($individual)) {
                return $this->conditionChild($sex);
            }

            return $this->conditionSingle($sex);
        }

        if ($family->facts(['DIV'])->isNotEmpty()) {
            return $this->conditionDivorced($sex);
        }

        $spouse = $family->spouse($individual);
        if ($spouse instanceof Individual && $this->isDead($spouse)) {
            return $this->conditionWidowed($sex);
        }

        return $this->conditionMarried($sex);
    }