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 */
16namespace Fisharebest\Webtrees\Module;
17
18use Fisharebest\Webtrees\Auth;
19use Fisharebest\Webtrees\Database;
20use Fisharebest\Webtrees\Filter;
21use Fisharebest\Webtrees\Functions\FunctionsEdit;
22use Fisharebest\Webtrees\GedcomRecord;
23use Fisharebest\Webtrees\GedcomTag;
24use Fisharebest\Webtrees\I18N;
25use Fisharebest\Webtrees\Theme;
26use Rhumsaa\Uuid\Uuid;
27
28/**
29 * Class ResearchTaskModule
30 */
31class ResearchTaskModule extends AbstractModule implements ModuleBlockInterface
32{
33    const DEFAULT_SHOW_OTHER      = '1';
34    const DEFAULT_SHOW_UNASSIGNED = '1';
35    const DEFAULT_SHOW_FUTURE     = '1';
36    const DEFAULT_BLOCK           = '1';
37
38    /** {@inheritdoc} */
39    public function getTitle()
40    {
41        return /* I18N: Name of a module. Tasks that need further research. */ I18N::translate('Research tasks');
42    }
43
44    /** {@inheritdoc} */
45    public function getDescription()
46    {
47        return /* I18N: Description of “Research tasks” module */ I18N::translate('A list of tasks and activities that are linked to the family tree.');
48    }
49
50    /**
51     * Generate the HTML content of this block.
52     *
53     * @param int      $block_id
54     * @param bool     $template
55     * @param string[] $cfg
56     *
57     * @return string
58     */
59    public function getBlock($block_id, $template = true, $cfg = array())
60    {
61        global $ctype, $controller, $WT_TREE;
62
63        $show_other      = $this->getBlockSetting($block_id, 'show_other', self::DEFAULT_SHOW_OTHER);
64        $show_unassigned = $this->getBlockSetting($block_id, 'show_unassigned', self::DEFAULT_SHOW_UNASSIGNED);
65        $show_future     = $this->getBlockSetting($block_id, 'show_future', self::DEFAULT_SHOW_FUTURE);
66        $block           = $this->getBlockSetting($block_id, 'block', self::DEFAULT_BLOCK);
67
68        foreach (array('show_unassigned', 'show_other', 'show_future', 'block') as $name) {
69            if (array_key_exists($name, $cfg)) {
70                $$name = $cfg[$name];
71            }
72        }
73
74        $id    = $this->getName() . $block_id;
75        $class = $this->getName() . '_block';
76        if ($ctype === 'gedcom' && Auth::isManager($WT_TREE) || $ctype === 'user' && Auth::check()) {
77            $title = '<a class="icon-admin" title="' . I18N::translate('Preferences') . '" href="block_edit.php?block_id=' . $block_id . '&amp;ged=' . $WT_TREE->getNameHtml() . '&amp;ctype=' . $ctype . '"></a>';
78        } else {
79            $title = '';
80        }
81        $title .= $this->getTitle();
82
83        $table_id = Uuid::uuid4(); // create a unique ID
84
85        $controller
86            ->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)
87            ->addInlineJavascript('
88			jQuery("#' . $table_id . '").dataTable({
89				dom: \'t\',
90				' . I18N::datatablesI18N() . ',
91				autoWidth: false,
92				paginate: false,
93				lengthChange: false,
94				filter: false,
95				info: true,
96				jQueryUI: true,
97				columns: [
98					null,
99					null,
100					null,
101					null
102				]
103			});
104			jQuery("#' . $table_id . '").css("visibility", "visible");
105			jQuery(".loading-image").css("display", "none");
106		');
107
108        $content = '';
109        $content .= '<div class="loading-image">&nbsp;</div>';
110        $content .= '<table id="' . $table_id . '" style="visibility:hidden;">';
111        $content .= '<thead><tr>';
112        $content .= '<th>' . GedcomTag::getLabel('DATE') . '</th>';
113        $content .= '<th>' . I18N::translate('Record') . '</th>';
114        $content .= '<th>' . I18N::translate('Username') . '</th>';
115        $content .= '<th>' . GedcomTag::getLabel('TEXT') . '</th>';
116        $content .= '</tr></thead><tbody>';
117
118        $found  = false;
119        $end_jd = $show_future ? 99999999 : WT_CLIENT_JD;
120
121        $xrefs = Database::prepare(
122            "SELECT DISTINCT d_gid FROM `##dates`" .
123            " WHERE d_file = :tree_id AND d_fact = '_TODO' AND d_julianday1 < :jd"
124        )->execute(array(
125            'tree_id' => $WT_TREE->getTreeId(),
126            'jd'      => $end_jd,
127        ))->fetchOneColumn();
128
129        $facts = array();
130        foreach ($xrefs as $xref) {
131            $record = GedcomRecord::getInstance($xref, $WT_TREE);
132            if ($record->canShow()) {
133                foreach ($record->getFacts('_TODO') as $fact) {
134                    $facts[] = $fact;
135                }
136            }
137        }
138
139        foreach ($facts as $fact) {
140            $record    = $fact->getParent();
141            $user_name = $fact->getAttribute('_WT_USER');
142            if ($user_name === Auth::user()->getUserName() || !$user_name && $show_unassigned || $user_name && $show_other) {
143                $content .= '<tr>';
144                $content .= '<td data-sort="' . $fact->getDate()->julianDay() . '">' . $fact->getDate()->display() . '</td>';
145                $content .= '<td data-sort="' . Filter::escapeHtml($record->getSortName()) . '"><a href="' . $record->getHtmlUrl() . '">' . $record->getFullName() . '</a></td>';
146                $content .= '<td>' . $user_name . '</td>';
147                $content .= '<td dir="auto">' . $fact->getValue() . '</td>';
148                $content .= '</tr>';
149                $found = true;
150            }
151        }
152
153        $content .= '</tbody></table>';
154        if (!$found) {
155            $content .= '<p>' . I18N::translate('There are no research tasks in this family tree.') . '</p>';
156        }
157
158        if ($template) {
159            if ($block) {
160                $class .= ' small_inner_block';
161            }
162
163            return Theme::theme()->formatBlock($id, $title, $class, $content);
164        } else {
165            return $content;
166        }
167    }
168
169    /** {@inheritdoc} */
170    public function loadAjax()
171    {
172        return false;
173    }
174
175    /** {@inheritdoc} */
176    public function isUserBlock()
177    {
178        return true;
179    }
180
181    /** {@inheritdoc} */
182    public function isGedcomBlock()
183    {
184        return true;
185    }
186
187    /**
188     * An HTML form to edit block settings
189     *
190     * @param int $block_id
191     */
192    public function configureBlock($block_id)
193    {
194        if (Filter::postBool('save') && Filter::checkCsrf()) {
195            $this->setBlockSetting($block_id, 'show_other', Filter::postBool('show_other'));
196            $this->setBlockSetting($block_id, 'show_unassigned', Filter::postBool('show_unassigned'));
197            $this->setBlockSetting($block_id, 'show_future', Filter::postBool('show_future'));
198            $this->setBlockSetting($block_id, 'block', Filter::postBool('block'));
199        }
200
201        $show_other      = $this->getBlockSetting($block_id, 'show_other', self::DEFAULT_SHOW_OTHER);
202        $show_unassigned = $this->getBlockSetting($block_id, 'show_unassigned', self::DEFAULT_SHOW_UNASSIGNED);
203        $show_future     = $this->getBlockSetting($block_id, 'show_future', self::DEFAULT_SHOW_FUTURE);
204        $block           = $this->getBlockSetting($block_id, 'block', self::DEFAULT_BLOCK);
205
206        ?>
207        <tr>
208            <td colspan="2">
209                <?php echo I18N::translate('Research tasks are special events, added to individuals in your family tree, which identify the need for further research. You can use them as a reminder to check facts against more reliable sources, to obtain documents or photographs, to resolve conflicting information, etc.'); ?>
210                <?php echo I18N::translate('To create new research tasks, you must first add “research task” to the list of facts and events in the family tree’s preferences.'); ?>
211                <?php echo I18N::translate('Research tasks are stored using the custom GEDCOM tag “_TODO”. Other genealogy applications may not recognize this tag.'); ?>
212            </td>
213        </tr>
214        <?php
215
216        echo '<tr><td class="descriptionbox wrap width33">';
217        echo I18N::translate('Show research tasks that are assigned to other users');
218        echo '</td><td class="optionbox">';
219        echo FunctionsEdit::editFieldYesNo('show_other', $show_other);
220        echo '</td></tr>';
221
222        echo '<tr><td class="descriptionbox wrap width33">';
223        echo I18N::translate('Show research tasks that are not assigned to any user');
224        echo '</td><td class="optionbox">';
225        echo FunctionsEdit::editFieldYesNo('show_unassigned', $show_unassigned);
226        echo '</td></tr>';
227
228        echo '<tr><td class="descriptionbox wrap width33">';
229        echo I18N::translate('Show research tasks that have a date in the future');
230        echo '</td><td class="optionbox">';
231        echo FunctionsEdit::editFieldYesNo('show_future', $show_future);
232        echo '</td></tr>';
233
234        echo '<tr><td class="descriptionbox wrap width33">';
235        echo /* I18N: label for a yes/no option */ I18N::translate('Add a scrollbar when block contents grow');
236        echo '</td><td class="optionbox">';
237        echo FunctionsEdit::editFieldYesNo('block', $block);
238        echo '</td></tr>';
239    }
240}
241