. */ namespace Fisharebest\Webtrees\Module; use Fisharebest\Webtrees\Auth; use Fisharebest\Webtrees\Database; use Fisharebest\Webtrees\Filter; use Fisharebest\Webtrees\GedcomRecord; use Fisharebest\Webtrees\GedcomTag; use Fisharebest\Webtrees\I18N; use Fisharebest\Webtrees\Individual; use Fisharebest\Webtrees\Theme; use Fisharebest\Webtrees\Tree; use Fisharebest\Webtrees\User; use PDO; use Ramsey\Uuid\Uuid; use stdClass; /** * Class FamilyTreeFavoritesModule * * Note that the user favorites module simply extends this module, so ensure that the * logic works for both. */ class FamilyTreeFavoritesModule extends AbstractModule implements ModuleBlockInterface { // How to update the database schema for this module const SCHEMA_TARGET_VERSION = 4; const SCHEMA_SETTING_NAME = 'FV_SCHEMA_VERSION'; const SCHEMA_MIGRATION_PREFIX = '\Fisharebest\Webtrees\Module\FamilyTreeFavorites\Schema'; /** * Create a new module. * * @param string $directory Where is this module installed */ public function __construct($directory) { parent::__construct($directory); // Create/update the database tables. // NOTE: if we want to set any module-settings, we'll need to move this. Database::updateSchema(self::SCHEMA_MIGRATION_PREFIX, self::SCHEMA_SETTING_NAME, self::SCHEMA_TARGET_VERSION); } /** * How should this module be labelled on tabs, menus, etc.? * * @return string */ public function getTitle() { return /* I18N: Name of a module */ I18N::translate('Favorites'); } /** * A sentence describing what this module does. * * @return string */ public function getDescription() { return /* I18N: Description of the “Favorites” module */ I18N::translate('Display and manage a family tree’s favorite pages.'); } /** * Generate the HTML content of this block. * * @param Tree $tree * @param int $block_id * @param bool $template * @param string[] $cfg * * @return string */ public function getBlock(Tree $tree, int $block_id, bool $template = true, array $cfg = []): string { global $ctype; $action = Filter::get('action'); switch ($action) { case 'deletefav': $favorite_id = Filter::getInteger('favorite_id'); if ($favorite_id) { self::deleteFavorite($favorite_id); } break; case 'addfav': $gid = Filter::get('gid', WT_REGEX_XREF, ''); $favnote = Filter::get('favnote'); $url = Filter::getUrl('url'); $favtitle = Filter::get('favtitle'); if ($gid !== '') { $record = GedcomRecord::getInstance($gid, $tree); if ($record && $record->canShow()) { self::addFavorite([ 'user_id' => $ctype === 'user' ? Auth::id() : null, 'gedcom_id' => $tree->getTreeId(), 'gid' => $record->getXref(), 'type' => $record::RECORD_TYPE, 'url' => null, 'note' => $favnote, 'title' => $favtitle, ]); } } elseif ($url !== null) { self::addFavorite([ 'user_id' => $ctype === 'user' ? Auth::id() : null, 'gedcom_id' => $tree->getTreeId(), 'gid' => null, 'type' => 'URL', 'url' => $url, 'note' => $favnote, 'title' => $favtitle ? $favtitle : $url, ]); } break; } $userfavs = $this->getFavorites($tree, Auth::user()); $content = ''; if ($userfavs) { foreach ($userfavs as $favorite) { $removeFavourite = '' . I18N::translate('Remove') . ' '; if ($favorite->favorite_type === 'URL') { $content .= '
'; if ($ctype == 'user' || Auth::isManager($tree)) { $content .= $removeFavourite; } $content .= '' . e($favorite->title) . ''; $content .= '
' . e((string) $favorite->note); $content .= '
'; } else { $record = GedcomRecord::getInstance($favorite->xref, $tree); if ($record && $record->canShow()) { if ($record instanceof Individual) { $content .= '
'; if ($ctype == 'user' || Auth::isManager($tree)) { $content .= $removeFavourite; } $content .= Theme::theme()->individualBoxLarge($record); $content .= e((string) $favorite->note); $content .= '
'; } else { $content .= '
'; if ($ctype == 'user' || Auth::isManager($tree)) { $content .= $removeFavourite; } $content .= $record->formatList(); $content .= '
' . e((string) $favorite->note); $content .= '
'; } } } } } if ($ctype == 'user' || Auth::isManager($tree)) { $uniqueID = Uuid::uuid4(); // This block can theoretically appear multiple times, so use a unique ID. $content .= '
'; $content .= '' . I18N::translate('Add a favorite') . ''; $content .= '
'; $content .= ''; } $content = view('blocks/favorites', [ 'ctype' => $ctype, 'favorites' => $userfavs, 'is_manager' => Auth::isManager($tree), 'tree' => $tree, ]); if ($template) { return view('blocks/template', [ 'block' => str_replace('_', '-', $this->getName()), 'id' => $block_id, 'config_url' => '', 'title' => $this->getTitle(), 'content' => $content, ]); } else { return $content; } } /** * Should this block load asynchronously using AJAX? * * Simple blocks are faster in-line, more comples ones * can be loaded later. * * @return bool */ public function loadAjax(): bool { return false; } /** * Can this block be shown on the user’s home page? * * @return bool */ public function isUserBlock(): bool { return false; } /** * Can this block be shown on the tree’s home page? * * @return bool */ public function isGedcomBlock(): bool { return true; } /** * An HTML form to edit block settings * * @param Tree $tree * @param int $block_id * * @return void */ public function configureBlock(Tree $tree, int $block_id) { } /** * Delete a favorite from the database * * @param int $favorite_id * * @return bool */ public static function deleteFavorite($favorite_id) { return (bool) Database::prepare("DELETE FROM `##favorite` WHERE favorite_id=?") ->execute([$favorite_id]); } /** * Store a new favorite in the database * * @param $favorite * * @return bool */ public static function addFavorite($favorite) { // -- make sure a favorite is added if (empty($favorite['gid']) && empty($favorite['url'])) { return false; } //-- make sure this is not a duplicate entry $sql = "SELECT 1 FROM `##favorite` WHERE"; if (!empty($favorite['gid'])) { $sql .= " xref=?"; $vars = [$favorite['gid']]; } else { $sql .= " url=?"; $vars = [$favorite['url']]; } $sql .= " AND gedcom_id=?"; $vars[] = $favorite['gedcom_id']; if ($favorite['user_id']) { $sql .= " AND user_id=?"; $vars[] = $favorite['user_id']; } else { $sql .= " AND user_id IS NULL"; } if (Database::prepare($sql)->execute($vars)->fetchOne()) { return false; } //-- add the favorite to the database return (bool) Database::prepare("INSERT INTO `##favorite` (user_id, gedcom_id, xref, favorite_type, url, title, note) VALUES (? ,? ,? ,? ,? ,? ,?)") ->execute([$favorite['user_id'], $favorite['gedcom_id'], $favorite['gid'], $favorite['type'], $favorite['url'], $favorite['title'], $favorite['note']]); } /** * Get favorites for a user or family tree * * @param Tree $tree * @param User $user * * @return stdClass[] */ public static function getFavorites(Tree $tree, User $user) { $favorites = Database::prepare( "SELECT favorite_id, user_id, gedcom_id, xref, favorite_type, title, note, url" . " FROM `##favorite` WHERE gedcom_id = :tree_id AND user_id IS NULL") ->execute([ 'tree_id' => $tree->getTreeId(), ]) ->fetchAll(); foreach ($favorites as $favorite) { $favorite->record = GedcomRecord::getInstance($favorite->xref, $tree); } return $favorites; } }