xref: /webtrees/app/Module/UpcomingAnniversariesModule.php (revision 1001c3a8c3f3447dd95f8a9481ce18ff581bd6da)
1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2018 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 */
16namespace Fisharebest\Webtrees\Module;
17
18use Fisharebest\Webtrees\Auth;
19use Fisharebest\Webtrees\Filter;
20use Fisharebest\Webtrees\Functions\FunctionsDb;
21use Fisharebest\Webtrees\GedcomTag;
22use Fisharebest\Webtrees\I18N;
23use Fisharebest\Webtrees\Tree;
24
25/**
26 * Class UpcomingAnniversariesModule
27 */
28class UpcomingAnniversariesModule extends AbstractModule implements ModuleBlockInterface {
29	// Default values for new blocks.
30	const DEFAULT_DAYS    = 7;
31	const DEFAULT_FILTER  = '1';
32	const DEFAULT_SORT    = 'alpha';
33	const DEFAULT_STYLE   = 'table';
34
35	// Can show this number of days into the future.
36	const MIN_DAYS = 1;
37	const MAX_DAYS = 30;
38
39	// All standard GEDCOM 5.5.1 events except CENS, RESI and EVEN
40	const ALL_EVENTS = [
41		'ADOP',
42		'ANUL',
43		'BAPM',
44		'BARM',
45		'BASM',
46		'BIRT',
47		'BLES',
48		'BURI',
49		'CHR',
50		'CHRA',
51		'CONF',
52		'CREM',
53		'DEAT',
54		'DIV',
55		'DIVF',
56		'EMIG',
57		'ENGA',
58		'FCOM',
59		'GRAD',
60		'IMMI',
61		'MARB',
62		'MARC',
63		'MARL',
64		'MARR',
65		'MARS',
66		'NATU',
67		'ORDN',
68		'PROB',
69		'RETI',
70		'WILL',
71	];
72
73	const DEFAULT_EVENTS = [
74		'BIRT',
75		'MARR',
76		'DEAT',
77	];
78
79	/**
80	 * How should this module be labelled on tabs, menus, etc.?
81	 *
82	 * @return string
83	 */
84	public function getTitle() {
85		return /* I18N: Name of a module */ I18N::translate('Upcoming events');
86	}
87
88	/**
89	 * A sentence describing what this module does.
90	 *
91	 * @return string
92	 */
93	public function getDescription() {
94		return /* I18N: Description of the “Upcoming events” module */ I18N::translate('A list of the anniversaries that will occur in the near future.');
95	}
96
97	/**
98	 * Generate the HTML content of this block.
99	 *
100	 * @param Tree     $tree
101	 * @param int      $block_id
102	 * @param bool     $template
103	 * @param string[] $cfg
104	 *
105	 * @return string
106	 */
107	public function getBlock(Tree $tree, int $block_id, bool $template = true, array $cfg = []): string {
108		global $ctype;
109
110		$default_events = implode(',', self::DEFAULT_EVENTS);
111
112		$days      = $this->getBlockSetting($block_id, 'days', self::DEFAULT_DAYS);
113		$filter    = (bool) $this->getBlockSetting($block_id, 'filter', self::DEFAULT_FILTER);
114		$infoStyle = $this->getBlockSetting($block_id, 'infoStyle', self::DEFAULT_STYLE);
115		$sortStyle = $this->getBlockSetting($block_id, 'sortStyle', self::DEFAULT_SORT);
116		$events    = $this->getBlockSetting($block_id, 'events', $default_events);
117
118		extract($cfg, EXTR_OVERWRITE);
119
120		$event_array = explode(',', $events);
121
122		// If we are only showing living individuals, then we don't need to search for DEAT events.
123		if ($filter) {
124			$death_events = explode('|', WT_EVENTS_DEAT);
125			$event_array = array_diff($event_array, $death_events);
126		}
127
128		$events_filter = implode('|', $event_array);
129
130		$startjd = WT_CLIENT_JD + 1;
131		$endjd   = WT_CLIENT_JD + (int) $days;
132
133		$facts = FunctionsDb::getEventsList($startjd, $endjd, $events_filter, $filter, $sortStyle, $tree);
134
135		if (empty($facts)) {
136			if ($endjd == $startjd) {
137				$content = view('blocks/events-empty', [
138					'message' => I18N::translate('No events exist for tomorrow.')
139				]);
140			} else {
141				$content = view('blocks/events-empty', [
142					'message' => /* I18N: translation for %s==1 is unused; it is translated separately as “tomorrow” */ I18N::plural('No events exist for the next %s day.', 'No events exist for the next %s days.', $endjd - $startjd + 1, I18N::number($endjd - $startjd + 1))
143				]);
144			}
145		} else {
146			$content = view('blocks/events-' . $infoStyle, [
147					'facts'   => $facts,
148				]
149			);
150		}
151
152		if ($template) {
153			if ($ctype === 'gedcom' && Auth::isManager($tree)) {
154				$config_url = route('tree-page-block-edit', ['block_id' => $block_id, 'ged' => $tree->getName()]);
155			} elseif ($ctype === 'user' && Auth::check()) {
156				$config_url = route('user-page-block-edit', ['block_id' => $block_id, 'ged' => $tree->getName()]);
157			} else {
158				$config_url = '';
159			}
160
161			return view('blocks/template', [
162				'block'      => str_replace('_', '-', $this->getName()),
163				'id'         => $block_id,
164				'config_url' => $config_url,
165				'title'      => $this->getTitle(),
166				'content'    => $content,
167			]);
168		} else {
169			return $content;
170		}
171	}
172
173	/** {@inheritdoc} */
174	public function loadAjax(): bool {
175		return true;
176	}
177
178	/** {@inheritdoc} */
179	public function isUserBlock(): bool {
180		return true;
181	}
182
183	/** {@inheritdoc} */
184	public function isGedcomBlock(): bool {
185		return true;
186	}
187
188	/**
189	 * An HTML form to edit block settings
190	 *
191	 * @param Tree $tree
192	 * @param int  $block_id
193	 *
194	 * @return void
195	 */
196	public function configureBlock(Tree $tree, int $block_id) {
197		if ($_SERVER['REQUEST_METHOD'] === 'POST') {
198			$this->setBlockSetting($block_id, 'days', Filter::postInteger('days', self::MIN_DAYS, self::MAX_DAYS, self::DEFAULT_DAYS));
199			$this->setBlockSetting($block_id, 'filter', Filter::postBool('filter'));
200			$this->setBlockSetting($block_id, 'infoStyle', Filter::post('infoStyle', 'list|table', self::DEFAULT_STYLE));
201			$this->setBlockSetting($block_id, 'sortStyle', Filter::post('sortStyle', 'alpha|anniv', self::DEFAULT_SORT));
202			$this->setBlockSetting($block_id, 'events', implode(',', Filter::postArray('events')));
203
204			return;
205		}
206
207		$default_events = implode(',', self::DEFAULT_EVENTS);
208
209		$days      = $this->getBlockSetting($block_id, 'days', self::DEFAULT_DAYS);
210		$filter    = $this->getBlockSetting($block_id, 'filter', self::DEFAULT_FILTER);
211		$infoStyle = $this->getBlockSetting($block_id, 'infoStyle', self::DEFAULT_STYLE);
212		$sortStyle = $this->getBlockSetting($block_id, 'sortStyle', self::DEFAULT_SORT);
213		$events    = $this->getBlockSetting($block_id, 'events', $default_events);
214
215		$event_array = explode(',', $events);
216
217		$all_events = [];
218		foreach (self::ALL_EVENTS as $event) {
219			$all_events[$event] = GedcomTag::getLabel($event);
220		}
221
222		$info_styles = [
223			'list'  => /* I18N: An option in a list-box */ I18N::translate('list'),
224			'table' => /* I18N: An option in a list-box */ I18N::translate('table'),
225		];
226
227		$sort_styles = [
228			'alpha' => /* I18N: An option in a list-box */ I18N::translate('sort by name'),
229			'anniv' => /* I18N: An option in a list-box */ I18N::translate('sort by date'),
230		];
231
232		echo view('blocks/upcoming-anniversaries-config', [
233			'all_events'  => $all_events,
234			'days'        => $days,
235			'event_array' => $event_array,
236			'filter'      => $filter,
237			'infoStyle'   => $infoStyle,
238			'info_styles' => $info_styles,
239			'max_days'    => self::MAX_DAYS,
240			'sortStyle'   => $sortStyle,
241			'sort_styles' => $sort_styles,
242		]);
243	}
244}
245