xref: /webtrees/app/Module/UpcomingAnniversariesModule.php (revision 771e86bf6fe88015a121927a182011cbc19da40e)
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		$summary = '';
134
135		if (empty($facts)) {
136			if ($filter) {
137				if ($endjd == $startjd) {
138					$summary = I18N::translate('No events for living individuals exist for tomorrow.');
139				} else {
140					// I18N: translation for %s==1 is unused; it is translated separately as “tomorrow”
141					$summary = I18N::plural('No events for living people exist for the next %s day.', 'No events for living people exist for the next %s days.', $endjd - $startjd + 1, I18N::number($endjd - $startjd + 1));
142				}
143			} else {
144				if ($endjd == $startjd) {
145					$summary = I18N::translate('No events exist for tomorrow.');
146				} else {
147					// I18N: translation for %s==1 is unused; it is translated separately as “tomorrow”
148					$summary = 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));
149				}
150			}
151		}
152
153		$content = view('blocks/events-' . $infoStyle, [
154				'facts'   => $facts,
155				'summary' => $summary,
156			]
157		);
158
159		if ($template) {
160			if ($ctype === 'gedcom' && Auth::isManager($WT_TREE)) {
161				$config_url = route('tree-page-block-edit', ['block_id' => $block_id, 'ged' => $WT_TREE->getName()]);
162			} elseif ($ctype === 'user' && Auth::check()) {
163				$config_url = route('user-page-block-edit', ['block_id' => $block_id, 'ged' => $WT_TREE->getName()]);
164			} else {
165				$config_url = '';
166			}
167
168			return view('blocks/template', [
169				'block'      => str_replace('_', '-', $this->getName()),
170				'id'         => $block_id,
171				'config_url' => $config_url,
172				'title'      => $this->getTitle(),
173				'content'    => $content,
174			]);
175		} else {
176			return $content;
177		}
178	}
179
180	/** {@inheritdoc} */
181	public function loadAjax(): bool {
182		return true;
183	}
184
185	/** {@inheritdoc} */
186	public function isUserBlock(): bool {
187		return true;
188	}
189
190	/** {@inheritdoc} */
191	public function isGedcomBlock(): bool {
192		return true;
193	}
194
195	/**
196	 * An HTML form to edit block settings
197	 *
198	 * @param int $block_id
199	 *
200	 * @return void
201	 */
202	public function configureBlock($block_id) {
203		if ($_SERVER['REQUEST_METHOD'] === 'POST') {
204			$this->setBlockSetting($block_id, 'days', Filter::postInteger('days', self::MIN_DAYS, self::MAX_DAYS, self::DEFAULT_DAYS));
205			$this->setBlockSetting($block_id, 'filter', Filter::postBool('filter'));
206			$this->setBlockSetting($block_id, 'infoStyle', Filter::post('infoStyle', 'list|table', self::DEFAULT_STYLE));
207			$this->setBlockSetting($block_id, 'sortStyle', Filter::post('sortStyle', 'alpha|anniv', self::DEFAULT_SORT));
208			$this->setBlockSetting($block_id, 'events', implode(',', Filter::postArray('events')));
209
210			return;
211		}
212
213		$default_events = implode(',', self::DEFAULT_EVENTS);
214
215		$days      = $this->getBlockSetting($block_id, 'days', self::DEFAULT_DAYS);
216		$filter    = $this->getBlockSetting($block_id, 'filter', self::DEFAULT_FILTER);
217		$infoStyle = $this->getBlockSetting($block_id, 'infoStyle', self::DEFAULT_STYLE);
218		$sortStyle = $this->getBlockSetting($block_id, 'sortStyle', self::DEFAULT_SORT);
219		$events    = $this->getBlockSetting($block_id, 'events', $default_events);
220
221		$event_array = explode(',', $events);
222
223		$all_events = [];
224		foreach (self::ALL_EVENTS as $event) {
225			$all_events[$event] = GedcomTag::getLabel($event);
226		}
227
228		$info_styles = [
229			'list'  => /* I18N: An option in a list-box */ I18N::translate('list'),
230			'table' => /* I18N: An option in a list-box */ I18N::translate('table'),
231		];
232
233		$sort_styles = [
234			'alpha' => /* I18N: An option in a list-box */ I18N::translate('sort by name'),
235			'anniv' => /* I18N: An option in a list-box */ I18N::translate('sort by date'),
236		];
237
238		echo view('blocks/upcoming-anniversaries-config', [
239			'all_events'  => $all_events,
240			'days'        => $days,
241			'event_array' => $event_array,
242			'filter'      => $filter,
243			'infoStyle'   => $infoStyle,
244			'info_styles' => $info_styles,
245			'max_days'    => self::MAX_DAYS,
246			'sortStyle'   => $sortStyle,
247			'sort_styles' => $sort_styles,
248		]);
249	}
250}
251