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