1<?php 2/** 3 * webtrees: online genealogy 4 * Copyright (C) 2019 webtrees development team 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16declare(strict_types=1); 17 18namespace Fisharebest\Webtrees\Module; 19 20use Fisharebest\Webtrees\Auth; 21use Fisharebest\Webtrees\Family; 22use Fisharebest\Webtrees\GedcomRecord; 23use Fisharebest\Webtrees\I18N; 24use Fisharebest\Webtrees\Individual; 25use Fisharebest\Webtrees\Tree; 26use Illuminate\Database\Capsule\Manager as DB; 27use Illuminate\Database\Query\JoinClause; 28use Illuminate\Support\Collection; 29use Symfony\Component\HttpFoundation\Request; 30 31/** 32 * Class ResearchTaskModule 33 */ 34class ResearchTaskModule extends AbstractModule implements ModuleBlockInterface 35{ 36 private const DEFAULT_SHOW_OTHER = '1'; 37 private const DEFAULT_SHOW_UNASSIGNED = '1'; 38 private const DEFAULT_SHOW_FUTURE = '1'; 39 40 /** {@inheritdoc} */ 41 public function getTitle(): string 42 { 43 /* I18N: Name of a module. Tasks that need further research. */ 44 return I18N::translate('Research tasks'); 45 } 46 47 /** {@inheritdoc} */ 48 public function getDescription(): string 49 { 50 /* I18N: Description of “Research tasks” module */ 51 return I18N::translate('A list of tasks and activities that are linked to the family tree.'); 52 } 53 54 /** 55 * Generate the HTML content of this block. 56 * 57 * @param Tree $tree 58 * @param int $block_id 59 * @param string $ctype 60 * @param string[] $cfg 61 * 62 * @return string 63 */ 64 public function getBlock(Tree $tree, int $block_id, string $ctype = '', array $cfg = []): string 65 { 66 $show_other = $this->getBlockSetting($block_id, 'show_other', self::DEFAULT_SHOW_OTHER); 67 $show_unassigned = $this->getBlockSetting($block_id, 'show_unassigned', self::DEFAULT_SHOW_UNASSIGNED); 68 $show_future = $this->getBlockSetting($block_id, 'show_future', self::DEFAULT_SHOW_FUTURE); 69 70 extract($cfg, EXTR_OVERWRITE); 71 72 $end_jd = $show_future ? 99999999 : WT_CLIENT_JD; 73 74 $individuals = $this->individualsWithTasks($tree, $end_jd); 75 $families = $this->familiesWithTasks($tree, $end_jd); 76 77 /** @var GedcomRecord[] $records */ 78 $records = $individuals->merge($families); 79 80 $tasks = []; 81 82 foreach ($records as $record) { 83 foreach ($record->facts(['_TODO']) as $task) { 84 $user_name = $task->attribute('_WT_USER'); 85 86 if ($user_name === Auth::user()->getUserName() || empty($user_name) && $show_unassigned || !empty($user_name) && $show_other) { 87 $tasks[] = $task; 88 } 89 } 90 } 91 92 if (empty($records)) { 93 $content = '<p>' . I18N::translate('There are no research tasks in this family tree.') . '</p>'; 94 } else { 95 $content = view('modules/todo/research-tasks', ['tasks' => $tasks]); 96 } 97 98 if ($ctype !== '') { 99 if ($ctype === 'gedcom' && Auth::isManager($tree)) { 100 $config_url = route('tree-page-block-edit', [ 101 'block_id' => $block_id, 102 'ged' => $tree->name(), 103 ]); 104 } elseif ($ctype === 'user' && Auth::check()) { 105 $config_url = route('user-page-block-edit', [ 106 'block_id' => $block_id, 107 'ged' => $tree->name(), 108 ]); 109 } else { 110 $config_url = ''; 111 } 112 113 return view('modules/block-template', [ 114 'block' => str_replace('_', '-', $this->getName()), 115 'id' => $block_id, 116 'config_url' => $config_url, 117 'title' => $this->getTitle(), 118 'content' => $content, 119 ]); 120 } 121 122 return $content; 123 } 124 125 /** {@inheritdoc} */ 126 public function loadAjax(): bool 127 { 128 return false; 129 } 130 131 /** {@inheritdoc} */ 132 public function isUserBlock(): bool 133 { 134 return true; 135 } 136 137 /** {@inheritdoc} */ 138 public function isGedcomBlock(): bool 139 { 140 return true; 141 } 142 143 /** 144 * Update the configuration for a block. 145 * 146 * @param Request $request 147 * @param int $block_id 148 * 149 * @return void 150 */ 151 public function saveBlockConfiguration(Request $request, int $block_id) 152 { 153 $this->setBlockSetting($block_id, 'show_other', $request->get('show_other', '')); 154 $this->setBlockSetting($block_id, 'show_unassigned', $request->get('show_unassigned', '')); 155 $this->setBlockSetting($block_id, 'show_future', $request->get('show_future', '')); 156 } 157 158 /** 159 * An HTML form to edit block settings 160 * 161 * @param Tree $tree 162 * @param int $block_id 163 * 164 * @return void 165 */ 166 public function editBlockConfiguration(Tree $tree, int $block_id) 167 { 168 $show_other = $this->getBlockSetting($block_id, 'show_other', self::DEFAULT_SHOW_OTHER); 169 $show_unassigned = $this->getBlockSetting($block_id, 'show_unassigned', self::DEFAULT_SHOW_UNASSIGNED); 170 $show_future = $this->getBlockSetting($block_id, 'show_future', self::DEFAULT_SHOW_FUTURE); 171 172 echo view('modules/todo/config', [ 173 'show_future' => $show_future, 174 'show_other' => $show_other, 175 'show_unassigned' => $show_unassigned, 176 ]); 177 } 178 179 /** 180 * @param Tree $tree 181 * @param int $max_julian_day 182 * 183 * @return Collection 184 */ 185 private function familiesWithTasks(Tree $tree, int $max_julian_day): Collection 186 { 187 return DB::table('families') 188 ->join('dates', function (JoinClause $join): void { 189 $join 190 ->on('f_file', '=', 'd_file') 191 ->on('f_id', '=', 'd_gid'); 192 }) 193 ->where('f_file', '=', $tree->id()) 194 ->where('d_fact', '=', '_TODO') 195 ->where('d_julianday1', '<', $max_julian_day) 196 ->select(['families.*']) 197 ->distinct() 198 ->get() 199 ->map(Family::rowMapper()); 200 } 201 202 /** 203 * @param Tree $tree 204 * @param int $max_julian_day 205 * 206 * @return Collection 207 */ 208 private function individualsWithTasks(Tree $tree, int $max_julian_day): Collection 209 { 210 return DB::table('individuals') 211 ->join('dates', function (JoinClause $join): void { 212 $join 213 ->on('i_file', '=', 'd_file') 214 ->on('i_id', '=', 'd_gid'); 215 }) 216 ->where('i_file', '=', $tree->id()) 217 ->where('d_fact', '=', '_TODO') 218 ->where('d_julianday1', '<', $max_julian_day) 219 ->select(['individuals.*']) 220 ->distinct() 221 ->get() 222 ->map(Individual::rowMapper($tree)); 223 } 224} 225