1<?php
2/**
3 *
4 * Plugin timetrack
5 *
6 * @package dokuwiki\plugin\timetrack
7 * @author     peterfromearth <coder@peterfromearth.de>
8 */
9// must be run within Dokuwiki
10if(!defined('DOKU_INC')) die();
11
12/**
13 * Class action_plugin_data
14 */
15class action_plugin_timetrack extends DokuWiki_Action_Plugin {
16
17    /**
18     * will hold the data helper plugin
19     * @var helper_plugin_timetrack
20     */
21    var $tthlp = null;
22
23    /**
24     * Constructor. Load helper plugin
25     */
26    function __construct(){
27        $this->tthlp = plugin_load('helper', 'timetrack');
28
29    }
30
31    /**
32     * Registers a callback function for a given event
33     */
34    function register(Doku_Event_Handler $controller) {
35        $controller->register_hook('TEMPLATE_PAGETOOLS_DISPLAY', 'BEFORE', $this, 'add_menu_item');
36        $controller->register_hook('MENU_ITEMS_ASSEMBLY', 'AFTER', $this, 'add_menu_item_new');
37
38        $controller->register_hook('TPL_ACTION_GET', 'BEFORE', $this, 'define_action');
39        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'ajax');
40//         $controller->register_hook('PARSER_CACHE_USE', 'BEFORE', $this, 'checkCacheBefore');
41        $controller->register_hook('PARSER_CACHE_USE', 'AFTER', $this, 'checkCacheAfter');
42    }
43
44
45    public function checkCacheAfter(Doku_Event $event, $param) {
46    	/* @var $cache  cache_renderer */
47    	$cache = $event->data;
48    	if($cache->mode !== 'xhtml') return;
49    	if(p_get_metadata($cache->page,'plugin_timetrack update_time') < $this->tthlp->getMaxUpdateTime($cache->page))
50    		$event->result = false;
51    }
52
53    /**
54     * Add an item to sitetools
55     * Can use `tpl_action()` because dokuwiki accepts 'youraction' by the define_action() handler below.
56     *
57     * @param Doku_Event $event
58     * @param $param
59     */
60    public function add_menu_item(Doku_Event $event, $param) {
61    	global $lang;
62    	global $ID;
63    	if(!$this->tthlp->isPageTracked($ID) && !$this->tthlp->isUserInDb()) return true;
64
65    	$lang['btn_plugin_timetrack'] = $this->getLang('timetrack');
66
67    	$event->data['items']['plugin_timetrack'] = tpl_action('plugin_timetrack', true, 'li', true);
68    }
69
70    public function add_menu_item_new(Doku_Event $event, $param) {
71        global $lang;
72        global $ID;
73        if(!$this->tthlp->isPageTracked($ID) && !$this->tthlp->isUserInDb()) return true;
74
75        $lang['btn_plugin_timetrack'] = $this->getLang('timetrack');
76
77        $event->data['items'][] = new \dokuwiki\plugin\timetrack\MenuItem();
78    }
79
80    /**
81     * Accepts the 'youraction' action, while using the default action link properties.
82     * Entries of $event->data can be modified eventually.
83     *
84     * @param Doku_Event $event
85     * @param $param
86     */
87    public function define_action(Doku_Event $event, $param) {
88    	if ($event->data['type'] != 'plugin_timetrack') {
89    		return;
90    	}
91
92    	$event->preventDefault();
93    }
94
95    public function ajax(Doku_Event $event, $param) {
96    	global $INPUT;
97    	sleep(1);
98
99    	if ($event->data !== 'plugin_timetrack') {
100    		return;
101    	}
102    	$event->stopPropagation();
103    	$event->preventDefault();
104
105    	$result = array();
106
107    	if(!checkSecurityToken()) {
108    		$result['dialog'] = 'security token false';
109
110    		$json = new JSON($result);
111    		echo $json->encode($result);
112    		exit;
113    	}
114
115
116    	$cmd = $INPUT->str('cmd');
117    	if(!cmd) $cmd = 'current';
118
119    	$pageid = cleanID($INPUT->str('pageid'));
120
121    	//if current page is not tracked or readable, do not show current action
122    	if($cmd === 'current' && (auth_quickaclcheck($pageid) < AUTH_READ || !$this->tthlp->isPageTracked($pageid))) {
123    		$cmd = 'overview';
124    	}
125
126    	$yearweek = null;
127    	if($INPUT->has('data') && isset($INPUT->arr('data')['yearweek'])) {
128    		$yearweek = $INPUT->arr('data')['yearweek'];
129    	} else if($INPUT->has('yearweek')) {
130    		$yearweek = $INPUT->str('yearweek');
131    	}
132
133    	$daterange = $this->tthlp->getDateRangeByYearWeek($yearweek);
134    	$errors = array();
135    	if($cmd === 'current') {
136    		$project_ids = $this->tthlp->getProjectIdsByPageId($pageid);
137    		$dbUserValues = $this->tthlp->getUserTimeData($this->tthlp->getCurrentUser(),$daterange,$project_ids);
138
139
140    		if($INPUT->has('UserTime')) {
141				$errors = $this->saveUserTime($INPUT->arr('UserTime'), $dbUserValues);
142    		}
143
144    		if($INPUT->has('UserTime') && empty($errors)) {
145    			$result = array(
146    				'success' => true
147    			);
148    		}
149			$dbTasks = $this->tthlp->getTasks($project_ids);
150			$dialogHtml = $this->renderErrors($errors);
151			$dialogHtml .= $this->tthlp->html_week(
152					$dbUserValues,
153					$daterange,
154					$dbTasks,
155					'current',
156					$pageid);
157
158			$result = array_merge($result,array(
159				'dialog' => $this->tthlp->html_prepareDialog('current',
160						$dialogHtml,
161						$pageid),
162				'cmd' => 'current',
163			));
164
165    	} else if($cmd === 'overview' ) {
166    		$result = array(
167    			'dialog' => $this->tthlp->html_prepareDialog('overview',$this->tthlp->html_overview($this->tthlp->getCurrentUser(),$daterange,$pageid),$pageid),
168    			'cmd' => 'overview',
169    		);
170
171    	} else if($cmd === 'recent' ) {
172    		$dbUserValues = $this->tthlp->getUserTimeData($this->tthlp->getCurrentUser(), $daterange);
173
174    		if($INPUT->has('UserTime')) {
175				$errors = $this->saveUserTime($INPUT->arr('UserTime'), $dbUserValues);
176    		}
177
178    		if($INPUT->has('UserTime') && empty($errors)) {
179    			$result = array(
180    				'success' => true
181    			);
182    		}
183    		$dbUserTasks = $this->tthlp->getRecentTasks($this->tthlp->getCurrentUser(), $daterange);
184    		$dialogHtml = $this->renderErrors($errors);
185    		$dialogHtml .= $this->tthlp->html_week($dbUserValues,$daterange,$dbUserTasks,'recent',$pageid);
186    		$result = array_merge($result,array(
187    			'dialog' => $this->tthlp->html_prepareDialog('recent',
188    				$dialogHtml,
189    				$pageid),
190    			'cmd' => 'recent',
191    		));
192
193    	} else {
194    		$result = array(
195    			'dialog' => 'cmd unknown'
196    		);
197    	}
198
199    	$json = new JSON($result);
200    	echo $json->encode($result);
201
202    }
203
204    public function validateUserTime($data) {
205    	$dates = array();
206    	$errors = array();
207    	$task_ids = array();
208    	foreach($data as $project_id => $taskValues) {
209    		foreach($taskValues as $task_id => $dateValues) {
210	    		foreach($dateValues as $date => $value) {
211	    			if($value < 0) $value = 0;
212	    			$dates[$date] += (int)$value;
213	    			$task_ids[] = $task_id;
214	    		}
215    		}
216    	}
217
218    	$task_ids = array_unique($task_ids);
219
220    	foreach($dates as $date=>$datevalue) {
221    		if($datevalue + $this->tthlp->getHoursNotInTasksOnDate($this->tthlp->getCurrentUser(),$date,$task_ids)
222    				> $this->getConf('max_hours'))
223    			$errors[] = sprintf($this->getLang('err_max_hours'),$date);
224    	}
225    	return $errors;
226    }
227
228    public function saveUserTime($userTime, &$dbUserValues) {
229    	$errors = $this->validateUserTime($userTime);
230
231    	if(empty($errors)) {
232    		foreach($userTime as $project_id => $taskValues) {
233    			if(!$this->tthlp->canAccessProject($project_id)) continue;
234
235    			$projectDetails = $this->tthlp->getProjectDetails($project_id);
236    			foreach($taskValues as $task_id => $dateValues) {
237    				foreach($dateValues as $date => $value) {
238    					$res = true;
239    					$value = (int)$value;
240    					if($value < 0) $value = 0;
241    					//not between from to
242    					if(($projectDetails['from'] && $projectDetails['from'] > strtotime($date)) ||
243    							($projectDetails['to'] && $projectDetails['to'] < strtotime($date)))
244    						continue;
245
246    					if(isset($dbUserValues[$project_id][$task_id][$date]) && $dbUserValues[$project_id][$task_id][$date]['value'] != $value) {
247    						$dbUserValues[$project_id][$task_id][$date]['value'] = $value;
248    						$res = $this->tthlp->updateUserTime($dbUserValues[$project_id][$task_id][$date]['id'], $value);
249    					} else if ($value){
250    						$dbUserValues[$project_id][$task_id][$date]['value'] = $value;
251
252    						$res = $this->tthlp->insertUserTime($this->tthlp->getCurrentUser(), $task_id, $date, $value);
253    					}
254    					if($res !== true) {
255    						$errors[] = $res;
256    					}
257    				}
258    			}
259    		}
260    	} else {
261    		foreach($userTime as $project_id => $taskValues) {
262    			foreach($taskValues as $task_id => $dateValues) {
263    				foreach($dateValues as $date => $value) {
264    					$dbUserValues[$project_id][$task_id][$date]['value_db'] = $dbUserValues[$project_id][$task_id][$date]['value']?$dbUserValues[$project_id][$task_id][$date]['value']:0;
265    					$dbUserValues[$project_id][$task_id][$date]['value'] = $value;
266    				}
267    			}
268    		}
269    	}
270
271    	return $errors;
272
273    }
274
275
276    public function renderErrors($errors) {
277    	if(!empty($errors)) {
278    		return '<div class="errors">' . implode('<br>',$errors) . '</div>';
279    	}
280    	return '';
281    }
282}
283