|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* EGroupware - InfoLog - Business object |
|
4
|
|
|
* |
|
5
|
|
|
* @link http://www.egroupware.org |
|
6
|
|
|
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de> |
|
7
|
|
|
* @author Joerg Lehrke <[email protected]> |
|
8
|
|
|
* @package infolog |
|
9
|
|
|
* @copyright (c) 2003-17 by Ralf Becker <RalfBecker-AT-outdoor-training.de> |
|
10
|
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License |
|
11
|
|
|
*/ |
|
12
|
|
|
|
|
13
|
|
|
use EGroupware\Api; |
|
14
|
|
|
use EGroupware\Api\Link; |
|
15
|
|
|
use EGroupware\Api\Acl; |
|
16
|
|
|
use EGroupware\Api\Vfs; |
|
17
|
|
|
|
|
18
|
|
|
/** |
|
19
|
|
|
* This class is the BO-layer of InfoLog |
|
20
|
|
|
*/ |
|
21
|
|
|
class infolog_bo |
|
22
|
|
|
{ |
|
23
|
|
|
/** |
|
24
|
|
|
* Undelete right |
|
25
|
|
|
*/ |
|
26
|
|
|
const ACL_UNDELETE = Acl::CUSTOM1; |
|
27
|
|
|
|
|
28
|
|
|
var $enums; |
|
29
|
|
|
var $status; |
|
30
|
|
|
/** |
|
31
|
|
|
* Instance of our so class |
|
32
|
|
|
* |
|
33
|
|
|
* @var infolog_so |
|
34
|
|
|
*/ |
|
35
|
|
|
var $so; |
|
36
|
|
|
/** |
|
37
|
|
|
* Total from last search call |
|
38
|
|
|
* @var int |
|
39
|
|
|
*/ |
|
40
|
|
|
var $total; |
|
41
|
|
|
var $vfs; |
|
42
|
|
|
var $vfs_basedir='/infolog'; |
|
43
|
|
|
/** |
|
44
|
|
|
* Set Logging |
|
45
|
|
|
* |
|
46
|
|
|
* @var boolean |
|
47
|
|
|
*/ |
|
48
|
|
|
var $log = false; |
|
49
|
|
|
/** |
|
50
|
|
|
* Cached timezone data |
|
51
|
|
|
* |
|
52
|
|
|
* @var array id => data |
|
53
|
|
|
*/ |
|
54
|
|
|
protected static $tz_cache = array(); |
|
55
|
|
|
/** |
|
56
|
|
|
* current time as timestamp in user-time and server-time |
|
57
|
|
|
* |
|
58
|
|
|
* @var int |
|
59
|
|
|
*/ |
|
60
|
|
|
var $user_time_now; |
|
61
|
|
|
var $now; |
|
62
|
|
|
/** |
|
63
|
|
|
* name of timestamps in an InfoLog entry |
|
64
|
|
|
* |
|
65
|
|
|
* @var array |
|
66
|
|
|
*/ |
|
67
|
|
|
var $timestamps = array('info_startdate','info_enddate','info_datemodified','info_datecompleted','info_created'); |
|
68
|
|
|
/** |
|
69
|
|
|
* fields the responsible user can change |
|
70
|
|
|
* |
|
71
|
|
|
* @var array |
|
72
|
|
|
*/ |
|
73
|
|
|
var $responsible_edit=array('info_status','info_percent','info_datecompleted'); |
|
74
|
|
|
/** |
|
75
|
|
|
* Fields to exclude from copy, if an entry is copied, the ones below are excluded by default. |
|
76
|
|
|
* |
|
77
|
|
|
* @var array |
|
78
|
|
|
*/ |
|
79
|
|
|
var $copy_excludefields = array('info_id', 'info_uid', 'info_etag', 'caldav_name', 'info_created', 'info_creator', 'info_datemodified', 'info_modifier'); |
|
80
|
|
|
/** |
|
81
|
|
|
* Fields to exclude from copy, if a sub-entry is created, the ones below are excluded by default. |
|
82
|
|
|
* |
|
83
|
|
|
* @var array |
|
84
|
|
|
*/ |
|
85
|
|
|
var $sub_excludefields = array('info_id', 'info_uid', 'info_etag', 'caldav_name', 'info_created', 'info_creator', 'info_datemodified', 'info_modifier'); |
|
86
|
|
|
/** |
|
87
|
|
|
* Additional fields to $sub_excludefields to exclude, if no config stored |
|
88
|
|
|
* |
|
89
|
|
|
* @var array |
|
90
|
|
|
*/ |
|
91
|
|
|
var $default_sub_excludefields = array('info_des'); |
|
92
|
|
|
/** |
|
93
|
|
|
* implicit ACL rights of the responsible user: read or edit |
|
94
|
|
|
* |
|
95
|
|
|
* @var string |
|
96
|
|
|
*/ |
|
97
|
|
|
var $implicit_rights='read'; |
|
98
|
|
|
/** |
|
99
|
|
|
* Custom fields read from the infolog config |
|
100
|
|
|
* |
|
101
|
|
|
* @var array |
|
102
|
|
|
*/ |
|
103
|
|
|
var $customfields=array(); |
|
104
|
|
|
/** |
|
105
|
|
|
* Group owners for certain types read from the infolog config |
|
106
|
|
|
* |
|
107
|
|
|
* @var array |
|
108
|
|
|
*/ |
|
109
|
|
|
var $group_owners=array(); |
|
110
|
|
|
/** |
|
111
|
|
|
* Current user |
|
112
|
|
|
* |
|
113
|
|
|
* @var int |
|
114
|
|
|
*/ |
|
115
|
|
|
var $user; |
|
116
|
|
|
/** |
|
117
|
|
|
* History loggin: ''=no, 'history'=history & delete allowed, 'history_admin_delete', 'history_no_delete' |
|
118
|
|
|
* |
|
119
|
|
|
* @var string |
|
120
|
|
|
*/ |
|
121
|
|
|
var $history; |
|
122
|
|
|
/** |
|
123
|
|
|
* Instance of infolog_tracking, only instaciated if needed! |
|
124
|
|
|
* |
|
125
|
|
|
* @var infolog_tracking |
|
126
|
|
|
*/ |
|
127
|
|
|
var $tracking; |
|
128
|
|
|
/** |
|
129
|
|
|
* Maximum number of line characters (-_+=~) allowed in a mail, to not stall the layout. |
|
130
|
|
|
* Longer lines / biger number of these chars are truncated to that max. number or chars. |
|
131
|
|
|
* |
|
132
|
|
|
* @var int |
|
133
|
|
|
*/ |
|
134
|
|
|
var $max_line_chars = 40; |
|
135
|
|
|
|
|
136
|
|
|
/** |
|
137
|
|
|
* Available filters |
|
138
|
|
|
* |
|
139
|
|
|
* @var array filter => label pairs |
|
140
|
|
|
*/ |
|
141
|
|
|
var $filters = array( |
|
142
|
|
|
'' => 'no Filter', |
|
143
|
|
|
'done' => 'done', |
|
144
|
|
|
'responsible' => 'responsible', |
|
145
|
|
|
'responsible-open-today' => 'responsible open', |
|
146
|
|
|
'responsible-open-overdue' => 'responsible overdue', |
|
147
|
|
|
'responsible-upcoming' => 'responsible upcoming', |
|
148
|
|
|
'responsible-open-upcoming'=> 'responsible open and upcoming', |
|
149
|
|
|
'delegated' => 'delegated', |
|
150
|
|
|
'delegated-open-today' => 'delegated open', |
|
151
|
|
|
'delegated-open-overdue' => 'delegated overdue', |
|
152
|
|
|
'delegated-upcoming' => 'delegated upcomming', |
|
153
|
|
|
'delegated-open-upcoming' => 'delegated open and upcoming', |
|
154
|
|
|
'own' => 'own', |
|
155
|
|
|
'own-open-today' => 'own open', |
|
156
|
|
|
'own-open-overdue' => 'own overdue', |
|
157
|
|
|
'own-upcoming' => 'own upcoming', |
|
158
|
|
|
'own-open-upcoming' => 'own open and upcoming', |
|
159
|
|
|
'private' => 'private', |
|
160
|
|
|
'open-today' => 'open(status)', |
|
161
|
|
|
'open-overdue' => 'overdue', |
|
162
|
|
|
'upcoming' => 'upcoming', |
|
163
|
|
|
'open-upcoming' => 'open and upcoming', |
|
164
|
|
|
'bydate' => 'startdate', |
|
165
|
|
|
'duedate' => 'enddate' |
|
166
|
|
|
); |
|
167
|
|
|
|
|
168
|
|
|
/** |
|
169
|
|
|
* Constructor Infolog BO |
|
170
|
|
|
* |
|
171
|
|
|
* @param int $info_id |
|
172
|
|
|
*/ |
|
173
|
|
|
function __construct($info_id = 0) |
|
174
|
|
|
{ |
|
175
|
|
|
$this->enums = $this->stock_enums = array( |
|
|
|
|
|
|
176
|
|
|
'priority' => array ( |
|
177
|
|
|
3 => 'urgent', |
|
178
|
|
|
2 => 'high', |
|
179
|
|
|
1 => 'normal', |
|
180
|
|
|
0 => 'low' |
|
181
|
|
|
), |
|
182
|
|
|
'confirm' => array( |
|
183
|
|
|
'not' => 'not','accept' => 'accept','finish' => 'finish', |
|
184
|
|
|
'both' => 'both' ), |
|
185
|
|
|
'type' => array( |
|
186
|
|
|
'task' => 'task','phone' => 'phone','note' => 'note','email' => 'email' |
|
187
|
|
|
/* ,'confirm' => 'confirm','reject' => 'reject','fax' => 'fax' not implemented so far */ ) |
|
188
|
|
|
); |
|
189
|
|
|
$this->status = $this->stock_status = array( |
|
|
|
|
|
|
190
|
|
|
'defaults' => array( |
|
191
|
|
|
'task' => 'not-started', 'phone' => 'not-started', 'note' => 'done','email' => 'done'), |
|
192
|
|
|
'task' => array( |
|
193
|
|
|
'offer' => 'offer', // --> NEEDS-ACTION |
|
194
|
|
|
'not-started' => 'not-started', // iCal NEEDS-ACTION |
|
195
|
|
|
'ongoing' => 'ongoing', // iCal IN-PROCESS |
|
196
|
|
|
'done' => 'done', // iCal COMPLETED |
|
197
|
|
|
'cancelled' => 'cancelled', // iCal CANCELLED |
|
198
|
|
|
'billed' => 'billed', // --> DONE |
|
199
|
|
|
'template' => 'template', // --> cancelled |
|
200
|
|
|
'nonactive' => 'nonactive', // --> cancelled |
|
201
|
|
|
'archive' => 'archive' ), // --> cancelled |
|
202
|
|
|
'phone' => array( |
|
203
|
|
|
'not-started' => 'call', // iCal NEEDS-ACTION |
|
204
|
|
|
'ongoing' => 'will-call', // iCal IN-PROCESS |
|
205
|
|
|
'done' => 'done', // iCal COMPLETED |
|
206
|
|
|
'billed' => 'billed' ), // --> DONE |
|
207
|
|
|
'note' => array( |
|
208
|
|
|
'ongoing' => 'ongoing', // iCal has no status on notes |
|
209
|
|
|
'done' => 'done' ), |
|
210
|
|
|
'email' => array( |
|
211
|
|
|
'ongoing' => 'ongoing', // iCal has no status on notes |
|
212
|
|
|
'done' => 'done' ), |
|
213
|
|
|
); |
|
214
|
|
|
if (($config_data = Api\Config::read('infolog'))) |
|
215
|
|
|
{ |
|
216
|
|
|
$this->allow_past_due_date = $config_data['allow_past_due_date'] === null ? 1 : $config_data['allow_past_due_date']; |
|
|
|
|
|
|
217
|
|
|
if (isset($config_data['status']) && is_array($config_data['status'])) |
|
218
|
|
|
{ |
|
219
|
|
|
foreach(array_keys($config_data['status']) as $key) |
|
220
|
|
|
{ |
|
221
|
|
|
if (!is_array($this->status[$key])) |
|
222
|
|
|
{ |
|
223
|
|
|
$this->status[$key] = array(); |
|
224
|
|
|
} |
|
225
|
|
|
$this->status[$key] = array_merge($this->status[$key],(array)$config_data['status'][$key]); |
|
226
|
|
|
} |
|
227
|
|
|
} |
|
228
|
|
|
if (isset($config_data['types']) && is_array($config_data['types'])) |
|
229
|
|
|
{ |
|
230
|
|
|
//echo "stock-types:<pre>"; print_r($this->enums['type']); echo "</pre>\n"; |
|
231
|
|
|
//echo "config-types:<pre>"; print_r($config_data['types']); echo "</pre>\n"; |
|
232
|
|
|
$this->enums['type'] += $config_data['types']; |
|
233
|
|
|
//echo "types:<pre>"; print_r($this->enums['type']); echo "</pre>\n"; |
|
234
|
|
|
} |
|
235
|
|
|
if ($config_data['group_owners']) $this->group_owners = $config_data['group_owners']; |
|
236
|
|
|
|
|
237
|
|
|
$this->customfields = Api\Storage\Customfields::get('infolog'); |
|
238
|
|
|
if ($this->customfields) |
|
|
|
|
|
|
239
|
|
|
{ |
|
240
|
|
|
foreach($this->customfields as $name => $field) |
|
241
|
|
|
{ |
|
242
|
|
|
// old infolog customefield record |
|
243
|
|
|
if(empty($field['type'])) |
|
244
|
|
|
{ |
|
245
|
|
|
if (count($field['values'])) $field['type'] = 'select'; // selectbox |
|
246
|
|
|
elseif ($field['rows'] > 1) $field['type'] = 'textarea'; // textarea |
|
247
|
|
|
elseif (intval($field['len']) > 0) $field['type'] = 'text'; // regular input field |
|
248
|
|
|
else $field['type'] = 'label'; // header-row |
|
249
|
|
|
$field['type2'] = $field['typ']; |
|
250
|
|
|
unset($field['typ']); |
|
251
|
|
|
$this->customfields[$name] = $field; |
|
252
|
|
|
$save_config = true; |
|
253
|
|
|
} |
|
254
|
|
|
} |
|
255
|
|
|
if ($save_config) Api\Config::save_value('customfields',$this->customfields,'infolog'); |
|
256
|
|
|
} |
|
257
|
|
|
if (is_array($config_data['responsible_edit'])) |
|
258
|
|
|
{ |
|
259
|
|
|
$this->responsible_edit = array_merge($this->responsible_edit,$config_data['responsible_edit']); |
|
260
|
|
|
} |
|
261
|
|
|
if (is_array($config_data['copy_excludefields'])) |
|
262
|
|
|
{ |
|
263
|
|
|
$this->copy_excludefields = array_merge($this->copy_excludefields,$config_data['copy_excludefields']); |
|
264
|
|
|
} |
|
265
|
|
|
if (is_array($config_data['sub_excludefields']) && $config_data['sub_excludefields']) |
|
266
|
|
|
{ |
|
267
|
|
|
$this->sub_excludefields = array_merge($this->sub_excludefields,$config_data['sub_excludefields']); |
|
268
|
|
|
} |
|
269
|
|
|
else |
|
270
|
|
|
{ |
|
271
|
|
|
$this->sub_excludefields = array_merge($this->sub_excludefields,$this->default_sub_excludefields); |
|
272
|
|
|
} |
|
273
|
|
|
if ($config_data['implicit_rights'] == 'edit') |
|
274
|
|
|
{ |
|
275
|
|
|
$this->implicit_rights = 'edit'; |
|
276
|
|
|
} |
|
277
|
|
|
$this->history = $config_data['history']; |
|
278
|
|
|
} |
|
279
|
|
|
// sort types by there translation |
|
280
|
|
|
foreach($this->enums['type'] as $key => $val) |
|
281
|
|
|
{ |
|
282
|
|
|
if (($val = lang($key)) != $key.'*') $this->enums['type'][$key] = lang($key); |
|
|
|
|
|
|
283
|
|
|
} |
|
284
|
|
|
natcasesort($this->enums['type']); |
|
285
|
|
|
|
|
286
|
|
|
$this->user = $GLOBALS['egw_info']['user']['account_id']; |
|
287
|
|
|
|
|
288
|
|
|
$this->now = time(); |
|
289
|
|
|
$this->user_time_now = Api\DateTime::server2user($this->now,'ts'); |
|
|
|
|
|
|
290
|
|
|
|
|
291
|
|
|
$this->grants = $GLOBALS['egw']->acl->get_grants('infolog',$this->group_owners ? $this->group_owners : true); |
|
|
|
|
|
|
292
|
|
|
$this->so = new infolog_so($this->grants); |
|
293
|
|
|
|
|
294
|
|
|
if ($info_id) |
|
295
|
|
|
{ |
|
296
|
|
|
$this->read( $info_id ); |
|
297
|
|
|
} |
|
298
|
|
|
else |
|
299
|
|
|
{ |
|
300
|
|
|
$this->init(); |
|
301
|
|
|
} |
|
302
|
|
|
} |
|
303
|
|
|
|
|
304
|
|
|
/** |
|
305
|
|
|
* checks if there are customfields for typ $typ |
|
306
|
|
|
* |
|
307
|
|
|
* @param string $type |
|
308
|
|
|
* @return boolean True if there are customfields for $typ, else False |
|
309
|
|
|
*/ |
|
310
|
|
|
function has_customfields($type) |
|
311
|
|
|
{ |
|
312
|
|
|
foreach($this->customfields as $field) |
|
313
|
|
|
{ |
|
314
|
|
|
if ((!$type || empty($field['type2']) || in_array($type,is_array($field['type2']) ? $field['type2'] : explode(',',$field['type2'])))) |
|
315
|
|
|
{ |
|
316
|
|
|
return True; |
|
317
|
|
|
} |
|
318
|
|
|
} |
|
319
|
|
|
return False; |
|
320
|
|
|
} |
|
321
|
|
|
|
|
322
|
|
|
/** |
|
323
|
|
|
* check's if user has the requiered rights on entry $info_id |
|
324
|
|
|
* |
|
325
|
|
|
* @param int|array $info data or info_id of infolog entry to check |
|
326
|
|
|
* @param int $required_rights ACL::{READ|EDIT|ADD|DELETE}|infolog_bo::ACL_UNDELETE |
|
327
|
|
|
* @param int $other uid to check (if info==0) or 0 to check against $this->user |
|
328
|
|
|
* @param int $user = null user whos rights to check, default current user |
|
329
|
|
|
* @return boolean |
|
330
|
|
|
*/ |
|
331
|
|
|
function check_access($info,$required_rights,$other=0,$user=null) |
|
332
|
|
|
{ |
|
333
|
|
|
static $cache = array(); |
|
334
|
|
|
|
|
335
|
|
|
$info_id = is_array($info) ? $info['info_id'] : $info; |
|
336
|
|
|
|
|
337
|
|
|
if (!$user) $user = $this->user; |
|
|
|
|
|
|
338
|
|
|
if ($user == $this->user) |
|
339
|
|
|
{ |
|
340
|
|
|
$grants = $this->grants; |
|
341
|
|
|
if ($info_id) $access =& $cache[$info_id][$required_rights]; // we only cache the current user! |
|
342
|
|
|
} |
|
343
|
|
|
else |
|
344
|
|
|
{ |
|
345
|
|
|
$grants = $GLOBALS['egw']->acl->get_grants('infolog',$this->group_owners ? $this->group_owners : true,$user); |
|
346
|
|
|
} |
|
347
|
|
|
if (!$info) |
|
348
|
|
|
{ |
|
349
|
|
|
$owner = $other ? $other : $user; |
|
350
|
|
|
$grant = $grants[$owner]; |
|
351
|
|
|
return $grant & $required_rights; |
|
352
|
|
|
} |
|
353
|
|
|
|
|
354
|
|
|
|
|
355
|
|
|
if (!isset($access)) |
|
356
|
|
|
{ |
|
357
|
|
|
// handle delete for the various history modes |
|
358
|
|
|
if ($this->history) |
|
359
|
|
|
{ |
|
360
|
|
|
if (!is_array($info) && !($info = $this->so->read(array('info_id' => $info_id)))) return false; |
|
361
|
|
|
|
|
362
|
|
|
if ($info['info_status'] == 'deleted' && |
|
363
|
|
|
($required_rights == Acl::EDIT || // no edit rights for deleted entries |
|
364
|
|
|
$required_rights == Acl::ADD || // no add rights for deleted entries |
|
365
|
|
|
$required_rights == Acl::DELETE && ($this->history == 'history_no_delete' || // no delete at all! |
|
366
|
|
|
$this->history == 'history_admin_delete' && (!isset($GLOBALS['egw_info']['user']['apps']['admin']) || $user!=$this->user)))) // delete only for admins |
|
367
|
|
|
{ |
|
368
|
|
|
$access = false; |
|
369
|
|
|
} |
|
370
|
|
|
elseif ($required_rights == self::ACL_UNDELETE) |
|
371
|
|
|
{ |
|
372
|
|
|
if ($info['info_status'] != 'deleted') |
|
373
|
|
|
{ |
|
374
|
|
|
$access = false; // can only undelete deleted items |
|
375
|
|
|
} |
|
376
|
|
|
else |
|
377
|
|
|
{ |
|
378
|
|
|
// undelete requires edit rights |
|
379
|
|
|
$access = $this->so->check_access( $info,Acl::EDIT,$this->implicit_rights == 'edit',$grants,$user ); |
|
380
|
|
|
} |
|
381
|
|
|
} |
|
382
|
|
|
} |
|
383
|
|
|
elseif ($required_rights == self::ACL_UNDELETE) |
|
384
|
|
|
{ |
|
385
|
|
|
$access = false; |
|
386
|
|
|
} |
|
387
|
|
|
if (!isset($access)) |
|
388
|
|
|
{ |
|
389
|
|
|
$access = $this->so->check_access( $info,$required_rights,$this->implicit_rights == 'edit',$grants,$user ); |
|
390
|
|
|
} |
|
391
|
|
|
} |
|
392
|
|
|
// else $cached = ' (from cache)'; |
|
393
|
|
|
// error_log(__METHOD__."($info_id,$required_rights,$other,$user) returning$cached ".array2string($access)); |
|
394
|
|
|
return $access; |
|
395
|
|
|
} |
|
396
|
|
|
|
|
397
|
|
|
/** |
|
398
|
|
|
* Check if user is responsible for an entry: he or one of his memberships is in responsible |
|
399
|
|
|
* |
|
400
|
|
|
* @param array $info infolog entry as array |
|
401
|
|
|
* @return boolean |
|
402
|
|
|
*/ |
|
403
|
|
|
function is_responsible($info) |
|
404
|
|
|
{ |
|
405
|
|
|
return $this->so->is_responsible($info); |
|
406
|
|
|
} |
|
407
|
|
|
|
|
408
|
|
|
/** |
|
409
|
|
|
* init internal data to be empty |
|
410
|
|
|
*/ |
|
411
|
|
|
function init() |
|
412
|
|
|
{ |
|
413
|
|
|
$this->so->init(); |
|
414
|
|
|
} |
|
415
|
|
|
|
|
416
|
|
|
/** |
|
417
|
|
|
* convert a link_id value into an info_from text |
|
418
|
|
|
* |
|
419
|
|
|
* @param array &$info infolog entry, key info_from gets set by this function |
|
420
|
|
|
* @param string $not_app = '' app to exclude |
|
421
|
|
|
* @param string $not_id = '' id to exclude |
|
422
|
|
|
* @return boolean True if we have a linked item, False otherwise |
|
423
|
|
|
*/ |
|
424
|
|
|
function link_id2from(&$info,$not_app='',$not_id='') |
|
425
|
|
|
{ |
|
426
|
|
|
//error_log(__METHOD__ . "(subject='{$info['info_subject']}', link_id='{$info['info_link_id']}', from='{$info['info_from']}', not_app='$not_app', not_id='$not_id')"); |
|
427
|
|
|
|
|
428
|
|
|
if ($info['info_link_id'] > 0 && |
|
429
|
|
|
(isset($info['links']) && ($link = $info['links'][$info['info_link_id']]) || // use supplied links info |
|
430
|
|
|
($link = Link::get_link($info['info_link_id'])) !== False)) // if link not found in supplied links, we always search! |
|
431
|
|
|
{ |
|
432
|
|
|
if (isset($info['links']) && isset($link['app'])) |
|
433
|
|
|
{ |
|
434
|
|
|
$app = $link['app']; |
|
435
|
|
|
$id = $link['id']; |
|
436
|
|
|
} |
|
437
|
|
|
else |
|
438
|
|
|
{ |
|
439
|
|
|
$nr = $link['link_app1'] == 'infolog' && $link['link_id1'] == $info['info_id'] ? '2' : '1'; |
|
440
|
|
|
$app = $link['link_app'.$nr]; |
|
441
|
|
|
$id = $link['link_id'.$nr]; |
|
442
|
|
|
} |
|
443
|
|
|
$title = Link::title($app,$id); |
|
444
|
|
|
|
|
445
|
|
|
if ((string)$info['info_custom_from'] === '') // old entry |
|
446
|
|
|
{ |
|
447
|
|
|
$info['info_custom_from'] = (int) ($title != $info['info_from'] && @htmlentities($title) != $info['info_from']); |
|
448
|
|
|
} |
|
449
|
|
|
if (!$info['info_custom_from']) |
|
450
|
|
|
{ |
|
451
|
|
|
$info['info_from'] = ''; |
|
452
|
|
|
$info['info_custom_from'] = 0; |
|
453
|
|
|
} |
|
454
|
|
|
if ($app == $not_app && $id == $not_id) |
|
455
|
|
|
{ |
|
456
|
|
|
return False; |
|
457
|
|
|
} |
|
458
|
|
|
// if link is a project and no other project selected, also add as project |
|
459
|
|
|
if ($app == 'projectmanager' && $id && !$info['pm_id']) |
|
460
|
|
|
{ |
|
461
|
|
|
$info['old_pm_id'] = $info['pm_id'] = $id; |
|
462
|
|
|
} |
|
463
|
|
|
else |
|
464
|
|
|
{ |
|
465
|
|
|
// Link might be contact, check others |
|
466
|
|
|
$this->get_pm_id($info); |
|
467
|
|
|
} |
|
468
|
|
|
$info['info_link'] = $info['info_contact'] = array( |
|
469
|
|
|
'app' => $app, |
|
470
|
|
|
'id' => $id, |
|
471
|
|
|
'title' => (!empty($info['info_from']) ? $info['info_from'] : $title), |
|
472
|
|
|
); |
|
473
|
|
|
|
|
474
|
|
|
//echo " title='$title'</p>\n"; |
|
475
|
|
|
return $info['blur_title'] = $title; |
|
476
|
|
|
} |
|
477
|
|
|
|
|
478
|
|
|
// Set ID to 'none' instead of unset to make it seem like there's a value |
|
479
|
|
|
$info['info_link'] = $info['info_contact'] = $info['info_from'] ? array('id' => 'none', 'title' => $info['info_from']) : null; |
|
480
|
|
|
$info['info_link_id'] = 0; // link might have been deleted |
|
481
|
|
|
$info['info_custom_from'] = (int)!!$info['info_from']; |
|
482
|
|
|
|
|
483
|
|
|
$this->get_pm_id($info); |
|
484
|
|
|
|
|
485
|
|
|
return False; |
|
486
|
|
|
} |
|
487
|
|
|
|
|
488
|
|
|
/** |
|
489
|
|
|
* Find projectmanager ID from linked project(s) |
|
490
|
|
|
* |
|
491
|
|
|
* @param Array $info |
|
492
|
|
|
*/ |
|
493
|
|
|
public function get_pm_id(&$info) |
|
494
|
|
|
{ |
|
495
|
|
|
$pm_links = Link::get_links('infolog',$info['info_id'],'projectmanager'); |
|
496
|
|
|
|
|
497
|
|
|
$old_pm_id = is_array($pm_links) ? array_shift($pm_links) : $info['old_pm_id']; |
|
|
|
|
|
|
498
|
|
|
if (!isset($info['pm_id']) && $old_pm_id) $info['pm_id'] = $old_pm_id; |
|
499
|
|
|
return $old_pm_id; |
|
500
|
|
|
} |
|
501
|
|
|
|
|
502
|
|
|
/** |
|
503
|
|
|
* Create a subject from a description: truncate it and add ' ...' |
|
504
|
|
|
*/ |
|
505
|
|
|
static function subject_from_des($des) |
|
506
|
|
|
{ |
|
507
|
|
|
return substr($des,0,60).' ...'; |
|
508
|
|
|
} |
|
509
|
|
|
|
|
510
|
|
|
/** |
|
511
|
|
|
* Convert the timestamps from given timezone to another and keep dates. |
|
512
|
|
|
* The timestamps are mostly expected to be in server-time |
|
513
|
|
|
* and $fromTZId is only used to qualify dates. |
|
514
|
|
|
* |
|
515
|
|
|
* @param array $values to modify |
|
516
|
|
|
* @param string $fromTZId = null |
|
517
|
|
|
* @param string $toTZId = false |
|
518
|
|
|
* TZID timezone name e.g. 'UTC' |
|
519
|
|
|
* or NULL for timestamps in user-time |
|
520
|
|
|
* or false for timestamps in server-time |
|
521
|
|
|
*/ |
|
522
|
|
|
function time2time(&$values, $fromTZId=false, $toTZId=null) |
|
523
|
|
|
{ |
|
524
|
|
|
|
|
525
|
|
|
if ($fromTZId === $toTZId) return; |
|
526
|
|
|
|
|
527
|
|
|
$tz = Api\DateTime::$server_timezone; |
|
528
|
|
|
|
|
529
|
|
|
if ($fromTZId) |
|
530
|
|
|
{ |
|
531
|
|
|
if (!isset(self::$tz_cache[$fromTZId])) |
|
532
|
|
|
{ |
|
533
|
|
|
self::$tz_cache[$fromTZId] = calendar_timezones::DateTimeZone($fromTZId); |
|
534
|
|
|
} |
|
535
|
|
|
$fromTZ = self::$tz_cache[$fromTZId]; |
|
536
|
|
|
} |
|
537
|
|
|
elseif (is_null($fromTZId)) |
|
|
|
|
|
|
538
|
|
|
{ |
|
539
|
|
|
$tz = Api\DateTime::$user_timezone; |
|
540
|
|
|
$fromTZ = Api\DateTime::$user_timezone; |
|
541
|
|
|
} |
|
542
|
|
|
else |
|
543
|
|
|
{ |
|
544
|
|
|
$fromTZ = Api\DateTime::$server_timezone; |
|
545
|
|
|
} |
|
546
|
|
|
if ($toTZId) |
|
547
|
|
|
{ |
|
548
|
|
|
if (!isset(self::$tz_cache[$toTZId])) |
|
549
|
|
|
{ |
|
550
|
|
|
self::$tz_cache[$toTZId] = calendar_timezones::DateTimeZone($toTZId); |
|
551
|
|
|
} |
|
552
|
|
|
$toTZ = self::$tz_cache[$toTZId]; |
|
553
|
|
|
} |
|
554
|
|
|
elseif (is_null($toTZId)) |
|
555
|
|
|
{ |
|
556
|
|
|
$toTZ = Api\DateTime::$user_timezone; |
|
557
|
|
|
} |
|
558
|
|
|
else |
|
559
|
|
|
{ |
|
560
|
|
|
$toTZ = Api\DateTime::$server_timezone; |
|
561
|
|
|
} |
|
562
|
|
|
//error_log(__METHOD__.'(values[info_enddate]='.date('Y-m-d H:i:s',$values['info_enddate']).", from=".array2string($fromTZId).", to=".array2string($toTZId).") tz=".$tz->getName().', fromTZ='.$fromTZ->getName().', toTZ='.$toTZ->getName().', userTZ='.Api\DateTime::$user_timezone->getName()); |
|
563
|
|
|
foreach($this->timestamps as $key) |
|
564
|
|
|
{ |
|
565
|
|
|
if ($values[$key]) |
|
566
|
|
|
{ |
|
567
|
|
|
$time = new Api\DateTime($values[$key], $tz); |
|
568
|
|
|
$time->setTimezone($fromTZ); |
|
569
|
|
|
if ($time->format('Hi') == '0000') |
|
570
|
|
|
{ |
|
571
|
|
|
// we keep dates the same in new timezone |
|
572
|
|
|
$arr = Api\DateTime::to($time,'array'); |
|
573
|
|
|
$time = new Api\DateTime($arr, $toTZ); |
|
574
|
|
|
} |
|
575
|
|
|
else |
|
576
|
|
|
{ |
|
577
|
|
|
$time->setTimezone($toTZ); |
|
578
|
|
|
} |
|
579
|
|
|
$values[$key] = Api\DateTime::to($time,'ts'); |
|
580
|
|
|
} |
|
581
|
|
|
} |
|
582
|
|
|
//error_log(__METHOD__.'() --> values[info_enddate]='.date('Y-m-d H:i:s',$values['info_enddate'])); |
|
583
|
|
|
} |
|
584
|
|
|
|
|
585
|
|
|
/** |
|
586
|
|
|
* convert a date from server to user-time |
|
587
|
|
|
* |
|
588
|
|
|
* @param int $ts timestamp in server-time |
|
589
|
|
|
* @param string $date_format = 'ts' date-formats: 'ts'=timestamp, 'server'=timestamp in server-time, 'array'=array or string with date-format |
|
590
|
|
|
* @return mixed depending of $date_format |
|
591
|
|
|
*/ |
|
592
|
|
|
function date2usertime($ts,$date_format='ts') |
|
593
|
|
|
{ |
|
594
|
|
|
if (empty($ts) || $date_format == 'server') return $ts; |
|
595
|
|
|
|
|
596
|
|
|
return Api\DateTime::server2user($ts,$date_format); |
|
597
|
|
|
} |
|
598
|
|
|
|
|
599
|
|
|
/** |
|
600
|
|
|
* Read an infolog entry specified by $info_id |
|
601
|
|
|
* |
|
602
|
|
|
* @param int|array $info_id integer id or array with id's or array with column=>value pairs of the entry to read |
|
603
|
|
|
* @param boolean $run_link_id2from = true should link_id2from run, default yes, |
|
604
|
|
|
* need to be set to false if called from link-title to prevent an infinit recursion |
|
605
|
|
|
* @param string $date_format = 'ts' date-formats: 'ts'=timestamp, 'server'=timestamp in server-time, |
|
606
|
|
|
* 'array'=array or string with date-format |
|
607
|
|
|
* @param boolean $ignore_acl = false if true, do NOT check access, default false |
|
608
|
|
|
* |
|
609
|
|
|
* @return array|boolean infolog entry, null if not found or false if no permission to read it |
|
610
|
|
|
*/ |
|
611
|
|
|
function &read($info_id,$run_link_id2from=true,$date_format='ts',$ignore_acl=false) |
|
612
|
|
|
{ |
|
613
|
|
|
//error_log(__METHOD__.'('.array2string($info_id).', '.array2string($run_link_id2from).", '$date_format') ".function_backtrace()); |
|
614
|
|
|
if (is_scalar($info_id) || isset($info_id[count($info_id)-1])) |
|
615
|
|
|
{ |
|
616
|
|
|
if (is_scalar($info_id) && !is_numeric($info_id)) |
|
|
|
|
|
|
617
|
|
|
{ |
|
618
|
|
|
$info_id = array('info_uid' => $info_id); |
|
619
|
|
|
} |
|
620
|
|
|
else |
|
621
|
|
|
{ |
|
622
|
|
|
$info_id = array('info_id' => $info_id); |
|
623
|
|
|
} |
|
624
|
|
|
} |
|
625
|
|
|
|
|
626
|
|
|
if (!$info_id || ($data = $this->so->read($info_id)) === False) |
|
627
|
|
|
{ |
|
628
|
|
|
return null; |
|
629
|
|
|
} |
|
630
|
|
|
|
|
631
|
|
|
if (!$ignore_acl && !$this->check_access($data,Acl::READ)) // check behind read, to prevent a double read |
|
632
|
|
|
{ |
|
633
|
|
|
return False; |
|
634
|
|
|
} |
|
635
|
|
|
|
|
636
|
|
|
if ($data['info_subject'] == $this->subject_from_des($data['info_des'])) |
|
637
|
|
|
{ |
|
638
|
|
|
$data['info_subject'] = ''; |
|
639
|
|
|
} |
|
640
|
|
|
if ($run_link_id2from) |
|
641
|
|
|
{ |
|
642
|
|
|
$this->link_id2from($data); |
|
643
|
|
|
} |
|
644
|
|
|
// convert server- to user-time |
|
645
|
|
|
if ($date_format == 'ts') |
|
646
|
|
|
{ |
|
647
|
|
|
$this->time2time($data); |
|
648
|
|
|
|
|
649
|
|
|
// pre-cache title and file access |
|
650
|
|
|
self::set_link_cache($data); |
|
|
|
|
|
|
651
|
|
|
} |
|
652
|
|
|
|
|
653
|
|
|
return $data; |
|
654
|
|
|
} |
|
655
|
|
|
|
|
656
|
|
|
/** |
|
657
|
|
|
* Delete an infolog entry, evtl. incl. it's children / subs |
|
658
|
|
|
* |
|
659
|
|
|
* @param int|array $info_id int id |
|
660
|
|
|
* @param boolean $delete_children should the children be deleted |
|
661
|
|
|
* @param int|boolean $new_parent parent to use for not deleted children if > 0 |
|
662
|
|
|
* @param boolean $skip_notification Do not send notification of delete |
|
663
|
|
|
* @return boolean True if delete was successful, False otherwise ($info_id does not exist or no rights) |
|
664
|
|
|
*/ |
|
665
|
|
|
function delete($info_id,$delete_children=False,$new_parent=False, $skip_notification=False) |
|
666
|
|
|
{ |
|
667
|
|
|
if (is_array($info_id)) |
|
668
|
|
|
{ |
|
669
|
|
|
$info_id = (int)(isset($info_id[0]) ? $info_id[0] : (isset($info_id['info_id']) ? $info_id['info_id'] : $info_id['info_id'])); |
|
670
|
|
|
} |
|
671
|
|
|
if (($info = $this->so->read(array('info_id' => $info_id), true, 'server')) === False) |
|
|
|
|
|
|
672
|
|
|
{ |
|
673
|
|
|
return False; |
|
674
|
|
|
} |
|
675
|
|
|
if (!$this->check_access($info,Acl::DELETE)) |
|
676
|
|
|
{ |
|
677
|
|
|
return False; |
|
678
|
|
|
} |
|
679
|
|
|
// check if we have children and delete or re-parent them |
|
680
|
|
|
if (($children = $this->so->get_children($info_id))) |
|
681
|
|
|
{ |
|
682
|
|
|
foreach($children as $id => $owner) |
|
683
|
|
|
{ |
|
684
|
|
|
if ($delete_children && $this->so->grants[$owner] & Acl::DELETE) |
|
685
|
|
|
{ |
|
686
|
|
|
$this->delete($id,$delete_children,$new_parent,$skip_notification); // call ourself recursive to delete the child |
|
687
|
|
|
} |
|
688
|
|
|
else // dont delete or no rights to delete the child --> re-parent it |
|
689
|
|
|
{ |
|
690
|
|
|
$this->so->write(array( |
|
691
|
|
|
'info_id' => $id, |
|
692
|
|
|
'info_parent_id' => $new_parent, |
|
693
|
|
|
)); |
|
694
|
|
|
} |
|
695
|
|
|
} |
|
696
|
|
|
} |
|
697
|
|
|
$deleted = $info; |
|
698
|
|
|
$deleted['info_status'] = 'deleted'; |
|
699
|
|
|
$deleted['info_datemodified'] = time(); |
|
700
|
|
|
$deleted['info_modifier'] = $this->user; |
|
701
|
|
|
|
|
702
|
|
|
// if we have history switched on and not an already deleted item --> set only status deleted |
|
703
|
|
|
if ($this->history && $info['info_status'] != 'deleted') |
|
704
|
|
|
{ |
|
705
|
|
|
if ($info['info_status'] == 'deleted') return false; // entry already deleted |
|
706
|
|
|
|
|
707
|
|
|
$this->so->write($deleted); |
|
708
|
|
|
|
|
709
|
|
|
Link::unlink(0,'infolog',$info_id,'','!file','',true); // keep the file attachments, hide the rest |
|
710
|
|
|
} |
|
711
|
|
|
else |
|
712
|
|
|
{ |
|
713
|
|
|
$this->so->delete($info_id,false); // we delete the children via bo to get all notifications! |
|
714
|
|
|
|
|
715
|
|
|
Link::unlink(0,'infolog',$info_id); |
|
716
|
|
|
} |
|
717
|
|
|
if ($info['info_status'] != 'deleted') // dont notify of final purge of already deleted items |
|
718
|
|
|
{ |
|
719
|
|
|
// send email notifications and do the history logging |
|
720
|
|
|
if(!$skip_notification) |
|
721
|
|
|
{ |
|
722
|
|
|
if (!is_object($this->tracking)) |
|
723
|
|
|
{ |
|
724
|
|
|
$this->tracking = new infolog_tracking($this); |
|
725
|
|
|
} |
|
726
|
|
|
$this->tracking->track($deleted,$info,$this->user,true); |
|
727
|
|
|
} |
|
728
|
|
|
} |
|
729
|
|
|
return True; |
|
730
|
|
|
} |
|
731
|
|
|
|
|
732
|
|
|
/** |
|
733
|
|
|
* writes the given $values to InfoLog, a new entry gets created if info_id is not set or 0 |
|
734
|
|
|
* |
|
735
|
|
|
* checks and asures ACL |
|
736
|
|
|
* |
|
737
|
|
|
* @param array &$values values to write |
|
738
|
|
|
* @param boolean $check_defaults = true check and set certain defaults |
|
739
|
|
|
* @param boolean|int $touch_modified = true touch the modification date and sets the modifier's user-id, 2: only modifier |
|
740
|
|
|
* @param boolean $user2server = true conversion between user- and server-time necessary |
|
741
|
|
|
* @param boolean $skip_notification = false true = do NOT send notification, false (default) = send notifications |
|
742
|
|
|
* @param boolean $throw_exception = false Throw an exception (if required fields are not set) |
|
743
|
|
|
* @param string $purge_cfs = null null=dont, 'ical'=only iCal X-properties (cfs name starting with "#"), 'all'=all cfs |
|
744
|
|
|
* @param boolean $ignore_acl =true |
|
745
|
|
|
* |
|
746
|
|
|
* @return int|boolean info_id on a successfull write or false |
|
747
|
|
|
*/ |
|
748
|
|
|
function write(&$values_in, $check_defaults=true, $touch_modified=true, $user2server=true, |
|
749
|
|
|
$skip_notification=false, $throw_exception=false, $purge_cfs=null, $ignore_acl=false) |
|
750
|
|
|
{ |
|
751
|
|
|
$values = $values_in; |
|
752
|
|
|
//echo "boinfolog::write()values="; _debug_array($values); |
|
753
|
|
|
if (!$ignore_acl && (!$values['info_id'] && !$this->check_access(0,Acl::EDIT,$values['info_owner']) && |
|
754
|
|
|
!$this->check_access(0,Acl::ADD,$values['info_owner']))) |
|
755
|
|
|
{ |
|
756
|
|
|
return false; |
|
757
|
|
|
} |
|
758
|
|
|
// we need to get the old values to update the links in customfields and for the tracking |
|
759
|
|
|
if ($values['info_id']) |
|
760
|
|
|
{ |
|
761
|
|
|
$old = $this->read($values['info_id'], false, 'server', $ignore_acl); |
|
762
|
|
|
} |
|
763
|
|
|
|
|
764
|
|
|
if (($status_only = !$ignore_acl && $values['info_id'] && !$this->check_access($values,Acl::EDIT))) |
|
765
|
|
|
{ |
|
766
|
|
|
if (!isset($values['info_responsible'])) |
|
767
|
|
|
{ |
|
768
|
|
|
$responsible = $old['info_responsible']; |
|
769
|
|
|
} |
|
770
|
|
|
else |
|
771
|
|
|
{ |
|
772
|
|
|
$responsible = $values['info_responsible']; |
|
773
|
|
|
} |
|
774
|
|
|
if (!($status_only = in_array($this->user, (array)$responsible))) // responsible has implicit right to change status |
|
775
|
|
|
{ |
|
776
|
|
|
$status_only = !!array_intersect((array)$responsible,array_keys($GLOBALS['egw']->accounts->memberships($this->user))); |
|
777
|
|
|
} |
|
778
|
|
|
if (!$status_only && $values['info_status'] != 'deleted') |
|
779
|
|
|
{ |
|
780
|
|
|
$status_only = $undelete = $this->check_access($values['info_id'],self::ACL_UNDELETE); |
|
781
|
|
|
} |
|
782
|
|
|
} |
|
783
|
|
|
if (!$ignore_acl && ($values['info_id'] && !$this->check_access($values['info_id'],Acl::EDIT) && !$status_only || |
|
784
|
|
|
!$values['info_id'] && $values['info_id_parent'] && !$this->check_access($values['info_id_parent'],Acl::ADD))) |
|
785
|
|
|
{ |
|
786
|
|
|
return false; |
|
787
|
|
|
} |
|
788
|
|
|
|
|
789
|
|
|
// Make sure status is still valid if the type changes |
|
790
|
|
|
if($old['info_type'] != $values['info_type'] && $values['info_status']) |
|
791
|
|
|
{ |
|
792
|
|
|
if (isset($this->status[$values['info_type']]) && |
|
793
|
|
|
!in_array($values['info_status'], array_keys($this->status[$values['info_type']]))) |
|
794
|
|
|
{ |
|
795
|
|
|
$values['info_status'] = $this->status['defaults'][$values['info_type']]; |
|
796
|
|
|
} |
|
797
|
|
|
} |
|
798
|
|
|
if ($status_only && !$undelete) // make sure only status gets writen |
|
799
|
|
|
{ |
|
800
|
|
|
$set_completed = !$values['info_datecompleted'] && // set date completed of finished job, only if its not already set |
|
801
|
|
|
in_array($values['info_status'],array('done','billed','cancelled')); |
|
802
|
|
|
|
|
803
|
|
|
$values = $old; |
|
804
|
|
|
// only overwrite explicitly allowed fields |
|
805
|
|
|
$values['info_datemodified'] = $values_in['info_datemodified']; |
|
806
|
|
|
foreach ($this->responsible_edit as $name) |
|
807
|
|
|
{ |
|
808
|
|
|
if (isset($values_in[$name])) $values[$name] = $values_in[$name]; |
|
809
|
|
|
} |
|
810
|
|
|
if ($set_completed) |
|
811
|
|
|
{ |
|
812
|
|
|
$values['info_datecompleted'] = $user2server ? $this->user_time_now : $this->now; |
|
813
|
|
|
$values['info_percent'] = 100; |
|
814
|
|
|
$forcestatus = true; |
|
815
|
|
|
$status = 'done'; |
|
816
|
|
|
if (isset($values['info_type']) && !in_array($values['info_status'],array('done','billed','cancelled'))) { |
|
817
|
|
|
$forcestatus = false; |
|
818
|
|
|
//echo "set_completed:"; _debug_array($this->status[$values['info_type']]); |
|
819
|
|
|
if (isset($this->status[$values['info_type']]['done'])) { |
|
820
|
|
|
$forcestatus = true; |
|
821
|
|
|
$status = 'done'; |
|
822
|
|
|
} elseif (isset($this->status[$values['info_type']]['billed'])) { |
|
823
|
|
|
$forcestatus = true; |
|
824
|
|
|
$status = 'billed'; |
|
825
|
|
|
} elseif (isset($this->status[$values['info_type']]['cancelled'])) { |
|
826
|
|
|
$forcestatus = true; |
|
827
|
|
|
$status = 'cancelled'; |
|
828
|
|
|
} |
|
829
|
|
|
} |
|
830
|
|
|
if ($forcestatus && !in_array($values['info_status'],array('done','billed','cancelled'))) $values['info_status'] = $status; |
|
831
|
|
|
} |
|
832
|
|
|
$check_defaults = false; |
|
833
|
|
|
} |
|
834
|
|
|
if ($check_defaults) |
|
835
|
|
|
{ |
|
836
|
|
|
if (!$values['info_datecompleted'] && |
|
837
|
|
|
(in_array($values['info_status'],array('done','billed')))) |
|
838
|
|
|
{ |
|
839
|
|
|
$values['info_datecompleted'] = $user2server ? $this->user_time_now : $this->now; // set date completed to today if status == done |
|
840
|
|
|
} |
|
841
|
|
|
// Check for valid status / percent combinations |
|
842
|
|
|
if (in_array($values['info_status'],array('done','billed'))) |
|
843
|
|
|
{ |
|
844
|
|
|
$values['info_percent'] = 100; |
|
845
|
|
|
} |
|
846
|
|
|
else if (in_array($values['info_status'], array('not-started'))) |
|
847
|
|
|
{ |
|
848
|
|
|
$values['info_percent'] = 0; |
|
849
|
|
|
} |
|
850
|
|
|
else if (($values['info_status'] != 'archive' && $values['info_status'] != 'cancelled' && |
|
851
|
|
|
isset($this->stock_status[$values['info_type']]) && in_array($values['info_status'], $this->stock_status[$values['info_type']])) && |
|
852
|
|
|
((int)$values['info_percent'] == 100 || $values['info_percent'] == 0)) |
|
853
|
|
|
{ |
|
854
|
|
|
// We change percent to match status, not status to match percent |
|
855
|
|
|
$values['info_percent'] = 10; |
|
856
|
|
|
} |
|
857
|
|
|
if ((int)$values['info_percent'] == 100 && !in_array($values['info_status'],array('done','billed','cancelled','archive'))) |
|
858
|
|
|
{ |
|
859
|
|
|
//echo "check_defaults:"; _debug_array($this->status[$values['info_type']]); |
|
860
|
|
|
//$values['info_status'] = 'done'; |
|
861
|
|
|
$status = 'done'; |
|
862
|
|
|
if (isset($values['info_type'])) { |
|
863
|
|
|
if (isset($this->status[$values['info_type']]['done'])) { |
|
864
|
|
|
$status = 'done'; |
|
865
|
|
|
} elseif (isset($this->status[$values['info_type']]['billed'])) { |
|
866
|
|
|
$status = 'billed'; |
|
867
|
|
|
} elseif (isset($this->status[$values['info_type']]['cancelled'])) { |
|
868
|
|
|
$status = 'cancelled'; |
|
869
|
|
|
} else { |
|
870
|
|
|
// since the comlete stati above do not exist for that type, dont change it |
|
871
|
|
|
$status = $values['info_status']; |
|
872
|
|
|
} |
|
873
|
|
|
} |
|
874
|
|
|
$values['info_status'] = $status; |
|
875
|
|
|
} |
|
876
|
|
|
if ($values['info_responsible'] && $values['info_status'] == 'offer') |
|
877
|
|
|
{ |
|
878
|
|
|
$values['info_status'] = 'not-started'; // have to match if not finished |
|
879
|
|
|
} |
|
880
|
|
|
if (isset($values['info_subject']) && empty($values['info_subject'])) |
|
881
|
|
|
{ |
|
882
|
|
|
$values['info_subject'] = $this->subject_from_des($values['info_des']); |
|
883
|
|
|
} |
|
884
|
|
|
|
|
885
|
|
|
// Check required custom fields |
|
886
|
|
|
if($throw_exception) |
|
887
|
|
|
{ |
|
888
|
|
|
$custom = Api\Storage\Customfields::get('infolog'); |
|
889
|
|
|
foreach($custom as $c_name => $c_field) |
|
890
|
|
|
{ |
|
891
|
|
|
if($c_field['type2']) $type2 = is_array($c_field['type2']) ? $c_field['type2'] : explode(',',$c_field['type2']); |
|
892
|
|
|
if($c_field['needed'] && (!$c_field['type2'] || $c_field['type2'] && in_array($values['info_type'],$type2))) |
|
893
|
|
|
{ |
|
894
|
|
|
// Required custom field |
|
895
|
|
|
if(!$values['#'.$c_name]) |
|
896
|
|
|
{ |
|
897
|
|
|
throw new Api\Exception\WrongUserinput(lang('For infolog type %1, %2 is required',lang($values['info_type']),$c_field['label'])); |
|
|
|
|
|
|
898
|
|
|
} |
|
899
|
|
|
} |
|
900
|
|
|
} |
|
901
|
|
|
} |
|
902
|
|
|
} |
|
903
|
|
|
if (isset($this->group_owners[$values['info_type']])) |
|
904
|
|
|
{ |
|
905
|
|
|
$values['info_owner'] = $this->group_owners[$values['info_type']]; |
|
906
|
|
|
if (!$ignore_acl && !($this->grants[$this->group_owners[$values['info_type']]] & Acl::EDIT)) |
|
907
|
|
|
{ |
|
908
|
|
|
if (!$this->check_access($values['info_id'],Acl::EDIT) || |
|
909
|
|
|
!$values['info_id'] && !$this->check_access($values,Acl::ADD) |
|
910
|
|
|
) |
|
911
|
|
|
{ |
|
912
|
|
|
return false; // no edit rights from the group-owner and no implicit rights (delegated and sufficient rights) |
|
913
|
|
|
} |
|
914
|
|
|
} |
|
915
|
|
|
$values['info_access'] = 'public'; // group-owners are allways public |
|
916
|
|
|
} |
|
917
|
|
|
elseif (!$values['info_id'] && !$values['info_owner'] || $GLOBALS['egw']->accounts->get_type($values['info_owner']) == 'g') |
|
|
|
|
|
|
918
|
|
|
{ |
|
919
|
|
|
$values['info_owner'] = $this->so->user; |
|
920
|
|
|
} |
|
921
|
|
|
|
|
922
|
|
|
$to_write = $values; |
|
923
|
|
|
if ($user2server) |
|
924
|
|
|
{ |
|
925
|
|
|
// convert user- to server-time |
|
926
|
|
|
$this->time2time($to_write, null, false); |
|
927
|
|
|
} |
|
928
|
|
|
else |
|
929
|
|
|
{ |
|
930
|
|
|
// convert server- to user-time |
|
931
|
|
|
$this->time2time($values); |
|
932
|
|
|
} |
|
933
|
|
|
|
|
934
|
|
|
if ($touch_modified && $touch_modified !== 2 || !$values['info_datemodified']) |
|
|
|
|
|
|
935
|
|
|
{ |
|
936
|
|
|
// Should only an entry be updated which includes the original modification date? |
|
937
|
|
|
// Used in the web-GUI to check against a modification by an other user while editing the entry. |
|
938
|
|
|
// It's now disabled for xmlrpc, as otherwise the xmlrpc code need to be changed! |
|
939
|
|
|
$xmlrpc = is_object($GLOBALS['server']) && $GLOBALS['server']->last_method; |
|
940
|
|
|
$check_modified = $values['info_datemodified'] && !$xmlrpc ? $to_write['info_datemodified'] : false; |
|
941
|
|
|
$values['info_datemodified'] = $this->user_time_now; |
|
942
|
|
|
$to_write['info_datemodified'] = $this->now; |
|
943
|
|
|
} |
|
944
|
|
|
if ($touch_modified || !$values['info_modifier']) |
|
945
|
|
|
{ |
|
946
|
|
|
$values['info_modifier'] = $to_write['info_modifier'] = $this->so->user; |
|
947
|
|
|
} |
|
948
|
|
|
|
|
949
|
|
|
// set created and creator for new entries |
|
950
|
|
|
if (!$values['info_id']) |
|
951
|
|
|
{ |
|
952
|
|
|
$values['info_created'] = $this->user_time_now; |
|
953
|
|
|
$to_write['info_created'] = $this->now; |
|
954
|
|
|
$values['info_creator'] = $to_write['info_creator'] = $this->so->user; |
|
955
|
|
|
} |
|
956
|
|
|
//_debug_array($values); |
|
957
|
|
|
// error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n".array2string($values)."\n",3,'/tmp/infolog'); |
|
958
|
|
|
|
|
959
|
|
|
if (($info_id = $this->so->write($to_write, $check_modified, $purge_cfs, !isset($old)))) |
|
960
|
|
|
{ |
|
961
|
|
|
if (!isset($values['info_type']) || $status_only || empty($values['caldav_url'])) |
|
962
|
|
|
{ |
|
963
|
|
|
$values = $this->read($info_id, true, 'server', $ignore_acl); |
|
964
|
|
|
} |
|
965
|
|
|
|
|
966
|
|
|
$values['info_id'] = $info_id; |
|
967
|
|
|
$to_write['info_id'] = $info_id; |
|
968
|
|
|
|
|
969
|
|
|
// if the info responbsible array is not passed, fetch it from old. |
|
970
|
|
|
if (!array_key_exists('info_responsible',$values)) $values['info_responsible'] = $old['info_responsible']; |
|
971
|
|
|
if (!is_array($values['info_responsible'])) // this should not happen, bug it does ;-) |
|
972
|
|
|
{ |
|
973
|
|
|
$values['info_responsible'] = $values['info_responsible'] ? explode(',',$values['info_responsible']) : array(); |
|
974
|
|
|
$to_write['info_responsible'] = $values['info_responsible']; |
|
975
|
|
|
} |
|
976
|
|
|
|
|
977
|
|
|
// writing links for a new entry |
|
978
|
|
|
if (!$old && is_array($to_write['link_to']['to_id']) && count($to_write['link_to']['to_id'])) |
|
979
|
|
|
{ |
|
980
|
|
|
//echo "<p>writing links for new entry $info_id</p>\n"; _debug_array($content['link_to']['to_id']); |
|
981
|
|
|
Link::link('infolog',$info_id,$to_write['link_to']['to_id']); |
|
982
|
|
|
$values['link_to']['to_id'] = $info_id; |
|
983
|
|
|
} |
|
984
|
|
|
$this->write_check_links($to_write); |
|
985
|
|
|
if(!$values['info_link_id'] || $values['info_link_id'] != $to_write['info_link_id']) |
|
986
|
|
|
{ |
|
987
|
|
|
// Just got a link ID, need to save it |
|
988
|
|
|
$this->so->write($to_write); |
|
989
|
|
|
$values['info_link_id'] = $to_write['info_link_id']; |
|
990
|
|
|
$values['info_contact'] = $to_write['info_contact']; |
|
991
|
|
|
$values['info_from'] = $to_write['info_from']; |
|
992
|
|
|
$this->link_id2from($values); |
|
993
|
|
|
} |
|
994
|
|
|
$values['pm_id'] = $to_write['pm_id']; |
|
995
|
|
|
|
|
996
|
|
|
if (($info_from_set = ($values['info_link_id'] && isset($values['info_from']) && empty($values['info_from'])))) |
|
997
|
|
|
{ |
|
998
|
|
|
$values['info_from'] = $to_write['info_from'] = $this->link_id2from($values); |
|
999
|
|
|
} |
|
1000
|
|
|
|
|
1001
|
|
|
// create (and remove) links in custom fields |
|
1002
|
|
|
if(!is_array($old)) |
|
1003
|
|
|
{ |
|
1004
|
|
|
$old = array(); |
|
1005
|
|
|
} |
|
1006
|
|
|
Api\Storage\Customfields::update_links('infolog',$values,$old,'info_id'); |
|
1007
|
|
|
|
|
1008
|
|
|
// Check for restore of deleted entry, restore held links |
|
1009
|
|
|
if($old['info_status'] == 'deleted' && $values['info_status'] != 'deleted') |
|
1010
|
|
|
{ |
|
1011
|
|
|
Link::restore('infolog', $info_id); |
|
1012
|
|
|
} |
|
1013
|
|
|
|
|
1014
|
|
|
// notify the link-class about the update, as other apps may be subscribt to it |
|
1015
|
|
|
Link::notify_update('infolog',$info_id,$values); |
|
1016
|
|
|
|
|
1017
|
|
|
// pre-cache the new values |
|
1018
|
|
|
self::set_link_cache($values); |
|
|
|
|
|
|
1019
|
|
|
|
|
1020
|
|
|
// send email notifications and do the history logging |
|
1021
|
|
|
if (!is_object($this->tracking)) |
|
1022
|
|
|
{ |
|
1023
|
|
|
$this->tracking = new infolog_tracking($this); |
|
1024
|
|
|
} |
|
1025
|
|
|
|
|
1026
|
|
|
if ($old && ($missing_fields = array_diff_key($old,$values))) |
|
1027
|
|
|
{ |
|
1028
|
|
|
// Some custom fields (multiselect with nothing selected) will be missing, |
|
1029
|
|
|
// and that's OK. Don't put them back. |
|
1030
|
|
|
foreach(array_keys($missing_fields) as $field) |
|
1031
|
|
|
{ |
|
1032
|
|
|
if(array_key_exists($field, $values_in)) |
|
1033
|
|
|
{ |
|
1034
|
|
|
unset($missing_fields[$field]); |
|
1035
|
|
|
} |
|
1036
|
|
|
} |
|
1037
|
|
|
$values = array_merge($values,$missing_fields); |
|
1038
|
|
|
} |
|
1039
|
|
|
// Add keys missing in the $to_write array |
|
1040
|
|
|
if (($missing_fields = array_diff_key($values,$to_write))) |
|
1041
|
|
|
{ |
|
1042
|
|
|
$to_write = array_merge($to_write,$missing_fields); |
|
1043
|
|
|
} |
|
1044
|
|
|
$this->tracking->track($to_write,$old,$this->user,$values['info_status'] == 'deleted' || $old['info_status'] == 'deleted', |
|
1045
|
|
|
null,$skip_notification); |
|
1046
|
|
|
|
|
1047
|
|
|
if ($info_from_set) $values['info_from'] = ''; |
|
1048
|
|
|
|
|
1049
|
|
|
// Change new values back to user time before sending them back |
|
1050
|
|
|
if($user2server) |
|
1051
|
|
|
{ |
|
1052
|
|
|
$this->time2time($values); |
|
1053
|
|
|
} |
|
1054
|
|
|
// merge changes (keeping extra values from the UI) |
|
1055
|
|
|
$values_in = array_merge($values_in,$values); |
|
1056
|
|
|
|
|
1057
|
|
|
// Update modified timestamp of parent |
|
1058
|
|
|
if($values['info_id_parent'] && $touch_modified) |
|
1059
|
|
|
{ |
|
1060
|
|
|
$parent = $this->read($values['info_id_parent'], true, 'server', true); |
|
1061
|
|
|
$this->write($parent, false, true, false, true, false, null, $ignore_acl); |
|
1062
|
|
|
} |
|
1063
|
|
|
} |
|
1064
|
|
|
return $info_id; |
|
1065
|
|
|
} |
|
1066
|
|
|
|
|
1067
|
|
|
/** |
|
1068
|
|
|
* Check links when writing an infolog entry |
|
1069
|
|
|
* |
|
1070
|
|
|
* Checks for info_contact properly linked, project properly linked and |
|
1071
|
|
|
* adds or removes to correct. |
|
1072
|
|
|
* |
|
1073
|
|
|
* @param Array $values |
|
1074
|
|
|
*/ |
|
1075
|
|
|
protected function write_check_links(&$values) |
|
1076
|
|
|
{ |
|
1077
|
|
|
$old_link_id = (int)$values['info_link_id']; |
|
1078
|
|
|
$from = $values['info_from']; |
|
1079
|
|
|
|
|
1080
|
|
|
if($values['info_contact'] && !( |
|
|
|
|
|
|
1081
|
|
|
is_array($values['info_contact']) && $values['info_contact']['id'] == 'none' |
|
1082
|
|
|
) || ( |
|
1083
|
|
|
is_array($values['info_contact']) && $values['info_contact']['id'] == 'none' && |
|
1084
|
|
|
array_key_exists('search', $values['info_contact']) |
|
1085
|
|
|
)) |
|
1086
|
|
|
{ |
|
1087
|
|
|
if(is_array($values['info_contact'])) |
|
1088
|
|
|
{ |
|
1089
|
|
|
// eTemplate2 returns the array all ready |
|
1090
|
|
|
$app = $values['info_contact']['app']; |
|
1091
|
|
|
$id = (int)$values['info_contact']['id']; |
|
1092
|
|
|
$from = $values['info_contact']['search']; |
|
1093
|
|
|
} |
|
1094
|
|
|
else if ($values['info_contact']) |
|
1095
|
|
|
{ |
|
1096
|
|
|
list($app, $id) = explode(':', $values['info_contact'], 2); |
|
1097
|
|
|
} |
|
1098
|
|
|
// if project has been removed, but is still info_contact --> also remove it |
|
1099
|
|
|
if ($app == 'projectmanager' && $id && $id == $values['old_pm_id'] && !$values['pm_id']) |
|
1100
|
|
|
{ |
|
1101
|
|
|
unset($values['info_link_id'], $id, $values['info_contact']); |
|
1102
|
|
|
} |
|
1103
|
|
|
else if ($app && $id) |
|
1104
|
|
|
{ |
|
1105
|
|
|
if(!is_array($values['link_to'])) |
|
1106
|
|
|
{ |
|
1107
|
|
|
$values['link_to'] = array(); |
|
1108
|
|
|
} |
|
1109
|
|
|
$values['info_link_id'] = (int)($info_link_id = Link::link( |
|
|
|
|
|
|
1110
|
|
|
'infolog', |
|
1111
|
|
|
$values['info_id'], |
|
1112
|
|
|
$app,$id |
|
1113
|
|
|
)); |
|
1114
|
|
|
$values['info_from'] = Link::title($app, $id); |
|
1115
|
|
|
if($values['pm_id']) |
|
1116
|
|
|
{ |
|
1117
|
|
|
// They just changed the contact, don't clear the project |
|
1118
|
|
|
unset($old_link_id); |
|
1119
|
|
|
} |
|
1120
|
|
|
} |
|
1121
|
|
|
else if ($from) |
|
1122
|
|
|
{ |
|
1123
|
|
|
$values['info_from'] = $from; |
|
1124
|
|
|
} |
|
1125
|
|
|
else |
|
1126
|
|
|
{ |
|
1127
|
|
|
unset($values['info_link_id']); |
|
1128
|
|
|
$values['info_from'] = null; |
|
1129
|
|
|
} |
|
1130
|
|
|
} |
|
1131
|
|
|
else if ($values['pm_id'] && $values['info_id'] && !$values['old_pm_id']) |
|
1132
|
|
|
{ |
|
1133
|
|
|
// Set for new entry with no contact |
|
1134
|
|
|
$app = 'projectmanager'; |
|
1135
|
|
|
$id = $values['pm_id']; |
|
1136
|
|
|
$values['info_link_id'] = (int)($info_link_id = Link::link( |
|
1137
|
|
|
'infolog', |
|
1138
|
|
|
$values['info_id'], |
|
1139
|
|
|
$app,$id |
|
1140
|
|
|
)); |
|
1141
|
|
|
} |
|
1142
|
|
|
else |
|
1143
|
|
|
{ |
|
1144
|
|
|
unset($values['info_link_id']); |
|
1145
|
|
|
unset($values['info_contact']); |
|
1146
|
|
|
$values['info_from'] = $from ? $from : null; |
|
1147
|
|
|
} |
|
1148
|
|
|
if($values['info_id'] && $values['old_pm_id'] !== $values['pm_id']) |
|
1149
|
|
|
{ |
|
1150
|
|
|
Link::unlink(0,'infolog',$values['info_id'],0,'projectmanager',$values['old_pm_id']); |
|
1151
|
|
|
// Project has changed, but link is not to project |
|
1152
|
|
|
if($values['pm_id']) |
|
1153
|
|
|
{ |
|
1154
|
|
|
$link_id = Link::link('infolog', $values['info_id'], 'projectmanager', $values['pm_id']); |
|
1155
|
|
|
if(!$values['info_link_id']) |
|
1156
|
|
|
{ |
|
1157
|
|
|
$values['info_link_id'] = $link_id; |
|
1158
|
|
|
} |
|
1159
|
|
|
} |
|
1160
|
|
|
else |
|
1161
|
|
|
{ |
|
1162
|
|
|
// Project removed, but primary link is not to project |
|
1163
|
|
|
$values['pm_id'] = null; |
|
1164
|
|
|
} |
|
1165
|
|
|
} |
|
1166
|
|
|
if ($old_link_id && $old_link_id != $values['info_link_id']) |
|
1167
|
|
|
{ |
|
1168
|
|
|
$link = Link::get_link($old_link_id); |
|
1169
|
|
|
// remove selected project, if removed link is that project |
|
1170
|
|
|
if($link['link_app2'] == 'projectmanager' && $link['link_id2'] == $values['old_pm_id']) |
|
1171
|
|
|
{ |
|
1172
|
|
|
unset($values['pm_id'], $values['old_pm_id']); |
|
1173
|
|
|
} |
|
1174
|
|
|
Link::unlink($old_link_id); |
|
1175
|
|
|
} |
|
1176
|
|
|
// if linked to a project and no other project selected, also add as project |
|
1177
|
|
|
$links = Link::get_links('infolog', $values['info_id'], 'projectmanager'); |
|
1178
|
|
|
if (!$values['pm_id'] && count($links)) |
|
1179
|
|
|
{ |
|
1180
|
|
|
$values['old_pm_id'] = $values['pm_id'] = array_pop($links); |
|
1181
|
|
|
} |
|
1182
|
|
|
} |
|
1183
|
|
|
|
|
1184
|
|
|
/** |
|
1185
|
|
|
* Query the number of children / subs for one or more info_id's |
|
1186
|
|
|
* |
|
1187
|
|
|
* @param int|array $info_id id |
|
1188
|
|
|
* @return int|array number of subs |
|
1189
|
|
|
*/ |
|
1190
|
|
|
function anzSubs( $info_id ) |
|
1191
|
|
|
{ |
|
1192
|
|
|
return $this->so->anzSubs( $info_id ); |
|
1193
|
|
|
} |
|
1194
|
|
|
|
|
1195
|
|
|
/** |
|
1196
|
|
|
* searches InfoLog for a certain pattern in $query |
|
1197
|
|
|
* |
|
1198
|
|
|
* @param $query[order] column-name to sort after |
|
|
|
|
|
|
1199
|
|
|
* @param $query[sort] sort-order DESC or ASC |
|
1200
|
|
|
* @param $query[filter] string with combination of acl-, date- and status-filters, eg. 'own-open-today' or '' |
|
1201
|
|
|
* @param $query[cat_id] category to use or 0 or unset |
|
1202
|
|
|
* @param $query[search] pattern to search, search is done in info_from, info_subject and info_des |
|
1203
|
|
|
* @param $query[action] / $query[action_id] if only entries linked to a specified app/entry show be used |
|
1204
|
|
|
* @param &$query[start], &$query[total] nextmatch-parameters will be used and set if query returns less entries |
|
1205
|
|
|
* @param $query[col_filter] array with column-name - data pairs, data == '' means no filter (!) |
|
1206
|
|
|
* @param boolean $no_acl =false true: ignore all acl |
|
1207
|
|
|
* @return array with id's as key of the matching log-entries |
|
1208
|
|
|
*/ |
|
1209
|
|
|
function &search(&$query, $no_acl=false) |
|
1210
|
|
|
{ |
|
1211
|
|
|
//error_log(__METHOD__.'('.array2string($query).')'); |
|
1212
|
|
|
|
|
1213
|
|
|
if($query['filter'] == 'bydate') |
|
1214
|
|
|
{ |
|
1215
|
|
|
if (is_int($query['startdate'])) $query['col_filter'][] = 'info_startdate >= '.$GLOBALS['egw']->db->quote($query['startdate']); |
|
1216
|
|
|
if (is_int($query['enddate'])) $query['col_filter'][] = 'info_startdate <= '.$GLOBALS['egw']->db->quote($query['enddate']+(60*60*24)-1); |
|
1217
|
|
|
} |
|
1218
|
|
|
elseif ($query['filter'] == 'duedate') |
|
1219
|
|
|
{ |
|
1220
|
|
|
if (is_int($query['startdate'])) $query['col_filter'][] = 'info_enddate >= '.$GLOBALS['egw']->db->quote($query['startdate']); |
|
1221
|
|
|
if (is_int($query['enddate'])) $query['col_filter'][] = 'info_enddate <= '.$GLOBALS['egw']->db->quote($query['enddate']+(60*60*24)-1); |
|
1222
|
|
|
} |
|
1223
|
|
|
elseif ($query['filter'] == 'private') |
|
1224
|
|
|
{ |
|
1225
|
|
|
$query['col_filter'][] = 'info_access = ' . $GLOBALS['egw']->db->quote('private'); |
|
1226
|
|
|
} |
|
1227
|
|
|
if (!isset($query['date_format']) || $query['date_format'] != 'server') |
|
1228
|
|
|
{ |
|
1229
|
|
|
if (isset($query['col_filter'])) |
|
1230
|
|
|
{ |
|
1231
|
|
|
foreach ($this->timestamps as $key) |
|
1232
|
|
|
{ |
|
1233
|
|
|
if (!empty($query['col_filter'][$key])) |
|
1234
|
|
|
{ |
|
1235
|
|
|
$query['col_filter'][$key] = Api\DateTime::user2server($query['col_filter'][$key],'ts'); |
|
1236
|
|
|
} |
|
1237
|
|
|
} |
|
1238
|
|
|
} |
|
1239
|
|
|
} |
|
1240
|
|
|
|
|
1241
|
|
|
$ret = $this->so->search($query, $no_acl); |
|
1242
|
|
|
$this->total = $query['total']; |
|
1243
|
|
|
|
|
1244
|
|
|
if (is_array($ret)) |
|
|
|
|
|
|
1245
|
|
|
{ |
|
1246
|
|
|
foreach ($ret as $id => &$data) |
|
1247
|
|
|
{ |
|
1248
|
|
|
if (!$no_acl && !$this->check_access($data,Acl::READ)) |
|
1249
|
|
|
{ |
|
1250
|
|
|
unset($ret[$id]); |
|
1251
|
|
|
continue; |
|
1252
|
|
|
} |
|
1253
|
|
|
// convert system- to user-time |
|
1254
|
|
|
foreach ($this->timestamps as $key) |
|
1255
|
|
|
{ |
|
1256
|
|
|
if ($data[$key]) |
|
1257
|
|
|
{ |
|
1258
|
|
|
$time = new Api\DateTime($data[$key], Api\DateTime::$server_timezone); |
|
1259
|
|
|
if (!isset($query['date_format']) || $query['date_format'] != 'server') |
|
1260
|
|
|
{ |
|
1261
|
|
|
if ($time->format('Hi') == '0000') |
|
1262
|
|
|
{ |
|
1263
|
|
|
// we keep dates the same in user-time |
|
1264
|
|
|
$arr = Api\DateTime::to($time,'array'); |
|
1265
|
|
|
$time = new Api\DateTime($arr, Api\DateTime::$user_timezone); |
|
1266
|
|
|
} |
|
1267
|
|
|
else |
|
1268
|
|
|
{ |
|
1269
|
|
|
$time->setTimezone(Api\DateTime::$user_timezone); |
|
1270
|
|
|
} |
|
1271
|
|
|
} |
|
1272
|
|
|
$data[$key] = Api\DateTime::to($time,'ts'); |
|
1273
|
|
|
} |
|
1274
|
|
|
} |
|
1275
|
|
|
// pre-cache title and file access |
|
1276
|
|
|
self::set_link_cache($data); |
|
|
|
|
|
|
1277
|
|
|
} |
|
1278
|
|
|
} |
|
1279
|
|
|
//echo "<p>boinfolog::search(".print_r($query,True).")=<pre>".print_r($ret,True)."</pre>\n"; |
|
1280
|
|
|
return $ret; |
|
1281
|
|
|
} |
|
1282
|
|
|
|
|
1283
|
|
|
/** |
|
1284
|
|
|
* Query ctag for infolog |
|
1285
|
|
|
* |
|
1286
|
|
|
* @param array $filter = array('filter'=>'own','info_type'=>'task') |
|
1287
|
|
|
* @return string |
|
1288
|
|
|
*/ |
|
1289
|
|
|
public function getctag(array $filter=array('filter'=>'own','info_type'=>'task')) |
|
1290
|
|
|
{ |
|
1291
|
|
|
$filter += array( |
|
1292
|
|
|
'order' => 'info_datemodified', |
|
1293
|
|
|
'sort' => 'DESC', |
|
1294
|
|
|
'date_format' => 'server', |
|
1295
|
|
|
'start' => 0, |
|
1296
|
|
|
'num_rows' => 1, |
|
1297
|
|
|
); |
|
1298
|
|
|
// we need to query deleted entries too for a ctag! |
|
1299
|
|
|
$filter['filter'] .= '+deleted'; |
|
1300
|
|
|
|
|
1301
|
|
|
$result =& $this->search($filter); |
|
1302
|
|
|
|
|
1303
|
|
|
if (empty($result)) return 'EGw-empty-wGE'; |
|
1304
|
|
|
|
|
1305
|
|
|
$entry = array_shift($result); |
|
1306
|
|
|
|
|
1307
|
|
|
return $entry['info_datemodified']; |
|
1308
|
|
|
} |
|
1309
|
|
|
|
|
1310
|
|
|
/** |
|
1311
|
|
|
* imports a mail identified by uid as infolog |
|
1312
|
|
|
* |
|
1313
|
|
|
* @author Cornelius Weiss <[email protected]> |
|
1314
|
|
|
* @todo search if infolog with from and subject allready exists ->appned body & inform user |
|
1315
|
|
|
* @param array $_addresses array of addresses |
|
1316
|
|
|
* - array (email,name) |
|
1317
|
|
|
* @param string $_subject |
|
1318
|
|
|
* @param string $_message |
|
1319
|
|
|
* @param array $_attachments |
|
1320
|
|
|
* @param string $_date |
|
1321
|
|
|
* @return array $content array for uiinfolog |
|
1322
|
|
|
*/ |
|
1323
|
|
|
function import_mail($_addresses,$_subject,$_message,$_attachments,$_date) |
|
1324
|
|
|
{ |
|
1325
|
|
|
foreach($_addresses as $address) |
|
1326
|
|
|
{ |
|
1327
|
|
|
$names[] = $address['name']; |
|
1328
|
|
|
$emails[] =$address['email']; |
|
1329
|
|
|
} |
|
1330
|
|
|
|
|
1331
|
|
|
$type = isset($this->enums['type']['email']) ? 'email' : 'note'; |
|
1332
|
|
|
$status = isset($this->status['defaults'][$type]) ? $this->status['defaults'][$type] : 'done'; |
|
1333
|
|
|
$info = array( |
|
1334
|
|
|
'info_id' => 0, |
|
1335
|
|
|
'info_type' => $type, |
|
1336
|
|
|
'info_from' => implode(', ',$names) . implode(', ', $emails), |
|
|
|
|
|
|
1337
|
|
|
'info_subject' => $_subject, |
|
1338
|
|
|
'info_des' => $_message, |
|
1339
|
|
|
'info_startdate' => Api\DateTime::server2user($_date), |
|
1340
|
|
|
'info_status' => $status, |
|
1341
|
|
|
'info_priority' => 1, |
|
1342
|
|
|
'info_percent' => $status == 'done' ? 100 : 0, |
|
1343
|
|
|
'referer' => false, |
|
1344
|
|
|
'link_to' => array( |
|
1345
|
|
|
'to_app' => 'infolog', |
|
1346
|
|
|
'to_id' => 0, |
|
1347
|
|
|
), |
|
1348
|
|
|
); |
|
1349
|
|
|
if ($GLOBALS['egw_info']['user']['preferences']['infolog']['cat_add_default']) $info['info_cat'] = $GLOBALS['egw_info']['user']['preferences']['infolog']['cat_add_default']; |
|
1350
|
|
|
// find the addressbookentry to link with |
|
1351
|
|
|
$addressbook = new Api\Contacts(); |
|
1352
|
|
|
$contacts = array(); |
|
1353
|
|
|
foreach ($emails as $mailadr) |
|
1354
|
|
|
{ |
|
1355
|
|
|
$contacts = array_merge($contacts,(array)$addressbook->search( |
|
1356
|
|
|
array( |
|
1357
|
|
|
'email' => $mailadr, |
|
1358
|
|
|
'email_home' => $mailadr |
|
1359
|
|
|
),True,'','','',false,'OR',false,null,'',false)); |
|
1360
|
|
|
} |
|
1361
|
|
|
if (!$contacts || !is_array($contacts) || !is_array($contacts[0])) |
|
1362
|
|
|
{ |
|
1363
|
|
|
$info['msg'] = lang('Attention: No Contact with address %1 found.',$info['info_from']); |
|
|
|
|
|
|
1364
|
|
|
$info['info_custom_from'] = true; // show the info_from line and NOT only the link |
|
1365
|
|
|
} |
|
1366
|
|
|
else |
|
1367
|
|
|
{ |
|
1368
|
|
|
// create the first address as info_contact |
|
1369
|
|
|
$contact = array_shift($contacts); |
|
1370
|
|
|
$info['info_contact'] = 'addressbook:'.$contact['id']; |
|
1371
|
|
|
// create the rest a "ordinary" links |
|
1372
|
|
|
foreach ($contacts as $contact) |
|
1373
|
|
|
{ |
|
1374
|
|
|
Link::link('infolog',$info['link_to']['to_id'],'addressbook',$contact['id']); |
|
1375
|
|
|
} |
|
1376
|
|
|
} |
|
1377
|
|
|
if (is_array($_attachments)) |
|
|
|
|
|
|
1378
|
|
|
{ |
|
1379
|
|
|
foreach ($_attachments as $attachment) |
|
1380
|
|
|
{ |
|
1381
|
|
|
if($attachment['egw_data']) |
|
1382
|
|
|
{ |
|
1383
|
|
|
Link::link('infolog',$info['link_to']['to_id'],Link::DATA_APPNAME, $attachment); |
|
1384
|
|
|
} |
|
1385
|
|
|
else if(is_readable($attachment['tmp_name']) || |
|
1386
|
|
|
(Vfs::is_readable($attachment['tmp_name']) && parse_url($attachment['tmp_name'], PHP_URL_SCHEME) === 'vfs')) |
|
1387
|
|
|
{ |
|
1388
|
|
|
Link::link('infolog',$info['link_to']['to_id'],'file', $attachment); |
|
1389
|
|
|
} |
|
1390
|
|
|
} |
|
1391
|
|
|
} |
|
1392
|
|
|
return $info; |
|
1393
|
|
|
} |
|
1394
|
|
|
|
|
1395
|
|
|
/** |
|
1396
|
|
|
* get title for an infolog entry identified by $info |
|
1397
|
|
|
* |
|
1398
|
|
|
* Is called as hook to participate in the linking |
|
1399
|
|
|
* |
|
1400
|
|
|
* @param int|array $info int info_id or array with infolog entry |
|
1401
|
|
|
* @return string|boolean string with the title, null if $info not found, false if no perms to view |
|
1402
|
|
|
*/ |
|
1403
|
|
|
function link_title($info) |
|
1404
|
|
|
{ |
|
1405
|
|
|
if (!is_array($info)) |
|
1406
|
|
|
{ |
|
1407
|
|
|
$info = $this->read( $info,false ); |
|
1408
|
|
|
} |
|
1409
|
|
|
if (!$info) |
|
1410
|
|
|
{ |
|
1411
|
|
|
return $info; |
|
1412
|
|
|
} |
|
1413
|
|
|
$title = !empty($info['info_subject']) ? $info['info_subject'] :self::subject_from_des($info['info_descr']); |
|
1414
|
|
|
return $title.($GLOBALS['egw_info']['user']['preferences']['infolog']['show_id']?' (#'.$info['info_id'].')':''); |
|
1415
|
|
|
} |
|
1416
|
|
|
|
|
1417
|
|
|
/** |
|
1418
|
|
|
* Return multiple titles fetched by a single query |
|
1419
|
|
|
* |
|
1420
|
|
|
* @param array $ids |
|
1421
|
|
|
*/ |
|
1422
|
|
|
function link_titles(array $ids) |
|
1423
|
|
|
{ |
|
1424
|
|
|
$titles = array(); |
|
1425
|
|
|
foreach ($this->search($params=array( |
|
|
|
|
|
|
1426
|
|
|
'col_filter' => array('info_id' => $ids), |
|
1427
|
|
|
)) as $info) |
|
1428
|
|
|
{ |
|
1429
|
|
|
$titles[$info['info_id']] = $this->link_title($info); |
|
1430
|
|
|
} |
|
1431
|
|
|
foreach (array_diff($ids,array_keys($titles)) as $id) |
|
1432
|
|
|
{ |
|
1433
|
|
|
$titles[$id] = false; // we assume every not returned entry to be not readable, as we notify the link class about all deletes |
|
1434
|
|
|
} |
|
1435
|
|
|
return $titles; |
|
1436
|
|
|
} |
|
1437
|
|
|
|
|
1438
|
|
|
/** |
|
1439
|
|
|
* query infolog for entries matching $pattern |
|
1440
|
|
|
* |
|
1441
|
|
|
* Is called as hook to participate in the linking |
|
1442
|
|
|
* |
|
1443
|
|
|
* @param string $pattern pattern to search |
|
1444
|
|
|
* @param array $options Array of options for the search |
|
1445
|
|
|
* @return array with info_id - title pairs of the matching entries |
|
1446
|
|
|
*/ |
|
1447
|
|
|
function link_query($pattern, Array &$options = array()) |
|
1448
|
|
|
{ |
|
1449
|
|
|
$query = array( |
|
1450
|
|
|
'search' => $pattern, |
|
1451
|
|
|
'start' => $options['start'], |
|
1452
|
|
|
'num_rows' => $options['num_rows'], |
|
1453
|
|
|
'subs' => true, |
|
1454
|
|
|
); |
|
1455
|
|
|
$ids = $this->search($query); |
|
1456
|
|
|
$options['total'] = $query['total']; |
|
1457
|
|
|
$content = array(); |
|
1458
|
|
|
if (is_array($ids)) |
|
|
|
|
|
|
1459
|
|
|
{ |
|
1460
|
|
|
foreach(array_keys($ids) as $id) |
|
1461
|
|
|
{ |
|
1462
|
|
|
$content[$id] = $this->link_title($id); |
|
1463
|
|
|
} |
|
1464
|
|
|
} |
|
1465
|
|
|
return $content; |
|
1466
|
|
|
} |
|
1467
|
|
|
|
|
1468
|
|
|
/** |
|
1469
|
|
|
* Check access to the file store |
|
1470
|
|
|
* |
|
1471
|
|
|
* @param int|array $id id of entry or entry array |
|
1472
|
|
|
* @param int $check Acl::READ for read and Acl::EDIT for write or delete access |
|
1473
|
|
|
* @param string $rel_path = null currently not used in InfoLog |
|
1474
|
|
|
* @param int $user = null for which user to check, default current user |
|
1475
|
|
|
* @return boolean true if access is granted or false otherwise |
|
1476
|
|
|
*/ |
|
1477
|
|
|
function file_access($id,$check,$rel_path=null,$user=null) |
|
1478
|
|
|
{ |
|
1479
|
|
|
unset($rel_path); // not used |
|
1480
|
|
|
return $this->check_access($id,$check,0,$user); |
|
1481
|
|
|
} |
|
1482
|
|
|
|
|
1483
|
|
|
/** |
|
1484
|
|
|
* Set the cache of the link class (title, file_access) for the given infolog entry |
|
1485
|
|
|
* |
|
1486
|
|
|
* @param array $info |
|
1487
|
|
|
*/ |
|
1488
|
|
|
function set_link_cache(array $info) |
|
1489
|
|
|
{ |
|
1490
|
|
|
Link::set_cache('infolog',$info['info_id'], |
|
1491
|
|
|
$this->link_title($info), |
|
1492
|
|
|
$this->file_access($info,Acl::EDIT) ? EGW_ACL_READ|EGW_ACL_EDIT : |
|
1493
|
|
|
($this->file_access($info,Acl::READ) ? Acl::READ : 0)); |
|
1494
|
|
|
} |
|
1495
|
|
|
|
|
1496
|
|
|
/** |
|
1497
|
|
|
* hook called be calendar to include events or todos in the cal-dayview |
|
1498
|
|
|
* |
|
1499
|
|
|
* @param int $args[year], $args[month], $args[day] date of the events |
|
1500
|
|
|
* @param int $args[owner] owner of the events |
|
1501
|
|
|
* @param string $args[location] calendar_include_{events|todos} |
|
1502
|
|
|
* @return array of events (array with keys starttime, endtime, title, view, icon, content) |
|
1503
|
|
|
*/ |
|
1504
|
|
|
function cal_to_include($args) |
|
1505
|
|
|
{ |
|
1506
|
|
|
//echo "<p>cal_to_include("; print_r($args); echo ")</p>\n"; |
|
1507
|
|
|
$user = (int) $args['owner']; |
|
1508
|
|
|
if ($user <= 0 && !checkdate($args['month'],$args['day'],$args['year'])) |
|
1509
|
|
|
{ |
|
1510
|
|
|
return False; |
|
1511
|
|
|
} |
|
1512
|
|
|
Api\Translation::add_app('infolog'); |
|
1513
|
|
|
|
|
1514
|
|
|
$do_events = $args['location'] == 'calendar_include_events'; |
|
1515
|
|
|
$to_include = array(); |
|
1516
|
|
|
$date_wanted = sprintf('%04d/%02d/%02d',$args['year'],$args['month'],$args['day']); |
|
1517
|
|
|
$query = array( |
|
1518
|
|
|
'order' => $args['order'] ? $args['order'] : 'info_startdate', |
|
1519
|
|
|
'sort' => $args['sort'] ? $args['sort'] : ($do_events ? 'ASC' : 'DESC'), |
|
1520
|
|
|
'filter'=> "user$user".($do_events ? 'date' : 'opentoday').$date_wanted, |
|
1521
|
|
|
'start' => 0, |
|
1522
|
|
|
); |
|
1523
|
|
|
if ($GLOBALS['egw_info']['user']['preferences']['infolog']['cal_show'] || $GLOBALS['egw_info']['user']['preferences']['infolog']['cal_show'] === '0') |
|
1524
|
|
|
{ |
|
1525
|
|
|
$query['col_filter']['info_type'] = explode(',',$GLOBALS['egw_info']['user']['preferences']['infolog']['cal_show']); |
|
1526
|
|
|
} |
|
1527
|
|
|
elseif ($this->customfields && !$GLOBALS['egw_info']['user']['preferences']['infolog']['cal_show_custom']) |
|
|
|
|
|
|
1528
|
|
|
{ |
|
1529
|
|
|
$query['col_filter']['info_type'] = array('task','phone','note','email'); |
|
1530
|
|
|
} |
|
1531
|
|
|
while ($infos = $this->search($query)) |
|
1532
|
|
|
{ |
|
1533
|
|
|
foreach ($infos as $info) |
|
1534
|
|
|
{ |
|
1535
|
|
|
$start = new Api\DateTime($info['info_startdate'],Api\DateTime::$user_timezone); |
|
1536
|
|
|
$title = ($do_events ? $start->format(false).' ' : ''). |
|
1537
|
|
|
$info['info_subject']; |
|
1538
|
|
|
$view = Link::view('infolog',$info['info_id']); |
|
1539
|
|
|
$size = null; |
|
1540
|
|
|
$edit = Link::edit('infolog',$info['info_id'], $size); |
|
1541
|
|
|
$edit['size'] = $size; |
|
1542
|
|
|
$content=array(); |
|
1543
|
|
|
$status = $this->status[$info['info_type']][$info['info_status']]; |
|
1544
|
|
|
$icons = array(); |
|
1545
|
|
|
foreach(array( |
|
1546
|
|
|
$info['info_type'] => 'navbar', |
|
1547
|
|
|
$status => 'status' |
|
1548
|
|
|
) as $icon => $default) |
|
1549
|
|
|
{ |
|
1550
|
|
|
$icons[Api\Image::find('infolog',$icon) ? $icon : $default] = $icon; |
|
1551
|
|
|
} |
|
1552
|
|
|
$content[] = Api\Html::a_href($title,$view); |
|
1553
|
|
|
$html = Api\Html::table(array(1 => $content)); |
|
1554
|
|
|
|
|
1555
|
|
|
$to_include[] = array( |
|
1556
|
|
|
'starttime' => $info['info_startdate'], |
|
1557
|
|
|
'endtime' => ($info['info_enddate'] ? $info['info_enddate'] : $info['info_startdate']), |
|
1558
|
|
|
'title' => $title, |
|
1559
|
|
|
'view' => $view, |
|
1560
|
|
|
'edit' => $edit, |
|
1561
|
|
|
'icons' => $icons, |
|
1562
|
|
|
'content' => $html, |
|
1563
|
|
|
); |
|
1564
|
|
|
} |
|
1565
|
|
|
if ($query['total'] <= ($query['start']+=count($infos))) |
|
1566
|
|
|
{ |
|
1567
|
|
|
break; // no more availible |
|
1568
|
|
|
} |
|
1569
|
|
|
} |
|
1570
|
|
|
//echo "boinfolog::cal_to_include("; print_r($args); echo ")<pre>"; print_r($to_include); echo "</pre>\n"; |
|
1571
|
|
|
return $to_include; |
|
1572
|
|
|
} |
|
1573
|
|
|
|
|
1574
|
|
|
/** |
|
1575
|
|
|
* Returm InfoLog (custom) information for projectmanager: status icon, type icon, css class |
|
1576
|
|
|
* |
|
1577
|
|
|
* @param array $args array with id's in $args['infolog'] |
|
1578
|
|
|
* @return array with id => array with values for keys 'status', 'icon', 'class' |
|
1579
|
|
|
*/ |
|
1580
|
|
|
function pm_icons($args) |
|
1581
|
|
|
{ |
|
1582
|
|
|
if (isset($args['infolog']) && count($args['infolog'])) |
|
1583
|
|
|
{ |
|
1584
|
|
|
$query = array( |
|
1585
|
|
|
'col_filter' => array('info_id' => $args['infolog']), |
|
1586
|
|
|
'subs' => true, |
|
1587
|
|
|
'cols' => 'main.info_id,info_type,info_status,info_percent,info_id_parent', |
|
1588
|
|
|
); |
|
1589
|
|
|
$infos = array(); |
|
1590
|
|
|
foreach($this->search($query) as $row) |
|
1591
|
|
|
{ |
|
1592
|
|
|
$infos[$row['info_id']] = array( |
|
1593
|
|
|
'status' => $row['info_type'] != 'phone' && $row['info_status'] == 'ongoing' ? |
|
1594
|
|
|
$row['info_percent'].'%' : 'infolog/'.$this->status[$row['info_type']][$row['info_status']], |
|
1595
|
|
|
'status_icon' => $row['info_type'] != 'phone' && $row['info_status'] == 'ongoing' ? |
|
1596
|
|
|
'ongoing' : 'infolog/'.$row['info_status'], |
|
1597
|
|
|
'class' => $row['info_id_parent'] ? 'infolog_rowHasParent' : null, |
|
1598
|
|
|
); |
|
1599
|
|
|
if (Api\Image::find('infolog', $icon=$row['info_type'].'_element') || |
|
1600
|
|
|
Api\Image::find('infolog', $icon=$row['info_type'])) |
|
1601
|
|
|
{ |
|
1602
|
|
|
$infos[$row['info_id']]['icon'] = 'infolog/'.$icon; |
|
1603
|
|
|
} |
|
1604
|
|
|
} |
|
1605
|
|
|
$anzSubs = $this->anzSubs(array_keys($infos)); |
|
1606
|
|
|
if($anzSubs && is_array($anzSubs)) |
|
1607
|
|
|
{ |
|
1608
|
|
|
foreach($anzSubs as $info_id => $subs) |
|
1609
|
|
|
{ |
|
1610
|
|
|
if ($subs) $infos[$info_id]['class'] .= ' infolog_rowHasSubs'; |
|
1611
|
|
|
} |
|
1612
|
|
|
} |
|
1613
|
|
|
} |
|
1614
|
|
|
return $infos; |
|
1615
|
|
|
} |
|
1616
|
|
|
|
|
1617
|
|
|
var $categories; |
|
1618
|
|
|
|
|
1619
|
|
|
/** |
|
1620
|
|
|
* Find existing categories in database by name or add categories that do not exist yet |
|
1621
|
|
|
* currently used for ical/sif import |
|
1622
|
|
|
* |
|
1623
|
|
|
* @param array $catname_list names of the categories which should be found or added |
|
1624
|
|
|
* @param int $info_id = -1 match against existing infolog and expand the returned category ids |
|
1625
|
|
|
* by the ones the user normally does not see due to category permissions - used to preserve categories |
|
1626
|
|
|
* @return array category ids (found, added and preserved categories) |
|
1627
|
|
|
*/ |
|
1628
|
|
|
function find_or_add_categories($catname_list, $info_id=-1) |
|
1629
|
|
|
{ |
|
1630
|
|
|
if (!is_object($this->categories)) |
|
1631
|
|
|
{ |
|
1632
|
|
|
$this->categories = new Api\Categories($this->user,'infolog'); |
|
1633
|
|
|
} |
|
1634
|
|
|
$old_cats_preserve = array(); |
|
1635
|
|
|
if ($info_id && $info_id > 0) |
|
1636
|
|
|
{ |
|
1637
|
|
|
// preserve Api\Categories without users read access |
|
1638
|
|
|
$old_infolog = $this->read($info_id); |
|
1639
|
|
|
$old_categories = explode(',',$old_infolog['info_cat']); |
|
1640
|
|
|
if (is_array($old_categories) && count($old_categories) > 0) |
|
1641
|
|
|
{ |
|
1642
|
|
|
foreach ($old_categories as $cat_id) |
|
1643
|
|
|
{ |
|
1644
|
|
|
if ($cat_id && !$this->categories->check_perms(Acl::READ, $cat_id)) |
|
1645
|
|
|
{ |
|
1646
|
|
|
$old_cats_preserve[] = $cat_id; |
|
1647
|
|
|
} |
|
1648
|
|
|
} |
|
1649
|
|
|
} |
|
1650
|
|
|
} |
|
1651
|
|
|
|
|
1652
|
|
|
$cat_id_list = array(); |
|
1653
|
|
|
foreach ((array)$catname_list as $cat_name) |
|
1654
|
|
|
{ |
|
1655
|
|
|
$cat_name = trim($cat_name); |
|
1656
|
|
|
$cat_id = $this->categories->name2id($cat_name, 'X-'); |
|
1657
|
|
|
|
|
1658
|
|
|
if (!$cat_id) |
|
1659
|
|
|
{ |
|
1660
|
|
|
// some SyncML clients (mostly phones) add an X- to the category names |
|
1661
|
|
|
if (strncmp($cat_name, 'X-', 2) == 0) |
|
1662
|
|
|
{ |
|
1663
|
|
|
$cat_name = substr($cat_name, 2); |
|
1664
|
|
|
} |
|
1665
|
|
|
$cat_id = $this->categories->add(array('name' => $cat_name, 'descr' => $cat_name, 'access' => 'private')); |
|
1666
|
|
|
} |
|
1667
|
|
|
|
|
1668
|
|
|
if ($cat_id) |
|
1669
|
|
|
{ |
|
1670
|
|
|
$cat_id_list[] = $cat_id; |
|
1671
|
|
|
} |
|
1672
|
|
|
} |
|
1673
|
|
|
|
|
1674
|
|
|
if (count($old_cats_preserve) > 0) |
|
1675
|
|
|
{ |
|
1676
|
|
|
$cat_id_list = array_merge($old_cats_preserve, $cat_id_list); |
|
1677
|
|
|
} |
|
1678
|
|
|
|
|
1679
|
|
|
if (count($cat_id_list) > 1) |
|
1680
|
|
|
{ |
|
1681
|
|
|
$cat_id_list = array_unique($cat_id_list); |
|
1682
|
|
|
// disable sorting until infolog supports multiple categories |
|
1683
|
|
|
// to make sure that the preserved category takes precedence over a new one from the client |
|
1684
|
|
|
/* sort($cat_id_list, SORT_NUMERIC); */ |
|
1685
|
|
|
} |
|
1686
|
|
|
|
|
1687
|
|
|
return $cat_id_list; |
|
1688
|
|
|
} |
|
1689
|
|
|
|
|
1690
|
|
|
/** |
|
1691
|
|
|
* Get names for categories specified by their id's |
|
1692
|
|
|
* |
|
1693
|
|
|
* @param array|string $cat_id_list array or comma-sparated list of id's |
|
1694
|
|
|
* @return array with names |
|
1695
|
|
|
*/ |
|
1696
|
|
|
function get_categories($cat_id_list) |
|
1697
|
|
|
{ |
|
1698
|
|
|
if (!is_object($this->categories)) |
|
1699
|
|
|
{ |
|
1700
|
|
|
$this->categories = new Api\Categories($this->user,'infolog'); |
|
1701
|
|
|
} |
|
1702
|
|
|
|
|
1703
|
|
|
if (!is_array($cat_id_list)) |
|
1704
|
|
|
{ |
|
1705
|
|
|
$cat_id_list = explode(',',$cat_id_list); |
|
1706
|
|
|
} |
|
1707
|
|
|
$cat_list = array(); |
|
1708
|
|
|
foreach($cat_id_list as $cat_id) |
|
1709
|
|
|
{ |
|
1710
|
|
|
if ($cat_id && $this->categories->check_perms(Acl::READ, $cat_id) && |
|
1711
|
|
|
($cat_name = $this->categories->id2name($cat_id)) && $cat_name != '--') |
|
1712
|
|
|
{ |
|
1713
|
|
|
$cat_list[] = $cat_name; |
|
1714
|
|
|
} |
|
1715
|
|
|
} |
|
1716
|
|
|
|
|
1717
|
|
|
return $cat_list; |
|
1718
|
|
|
} |
|
1719
|
|
|
|
|
1720
|
|
|
/** |
|
1721
|
|
|
* Send all async infolog notification |
|
1722
|
|
|
* |
|
1723
|
|
|
* Called via the async service job 'infolog-async-notification' |
|
1724
|
|
|
*/ |
|
1725
|
|
|
function async_notification() |
|
1726
|
|
|
{ |
|
1727
|
|
|
if (!($users = $this->so->users_with_open_entries())) |
|
1728
|
|
|
{ |
|
1729
|
|
|
return; |
|
1730
|
|
|
} |
|
1731
|
|
|
//error_log(__METHOD__."() users with open entries: ".implode(', ',$users)); |
|
1732
|
|
|
|
|
1733
|
|
|
$save_account_id = $GLOBALS['egw_info']['user']['account_id']; |
|
1734
|
|
|
$save_prefs = $GLOBALS['egw_info']['user']['preferences']; |
|
1735
|
|
|
foreach($users as $user) |
|
1736
|
|
|
{ |
|
1737
|
|
|
if (!($email = $GLOBALS['egw']->accounts->id2name($user,'account_email'))) continue; |
|
1738
|
|
|
// create the enviroment for $user |
|
1739
|
|
|
$this->user = $GLOBALS['egw_info']['user']['account_id'] = $user; |
|
1740
|
|
|
$GLOBALS['egw']->preferences->__construct($user); |
|
1741
|
|
|
$GLOBALS['egw_info']['user']['preferences'] = $GLOBALS['egw']->preferences->read_repository(); |
|
1742
|
|
|
$GLOBALS['egw']->acl->__construct($user); |
|
1743
|
|
|
$this->grants = $GLOBALS['egw']->acl->get_grants('infolog',$this->group_owners ? $this->group_owners : true); |
|
|
|
|
|
|
1744
|
|
|
$this->so = new infolog_so($this->grants); // so caches it's filters |
|
1745
|
|
|
|
|
1746
|
|
|
$notified_info_ids = array(); |
|
1747
|
|
|
foreach(array( |
|
1748
|
|
|
'notify_due_responsible' => 'open-responsible-enddate', |
|
1749
|
|
|
'notify_due_delegated' => 'open-delegated-enddate', |
|
1750
|
|
|
'notify_start_responsible' => 'open-responsible-date', |
|
1751
|
|
|
'notify_start_delegated' => 'open-delegated-date', |
|
1752
|
|
|
) as $pref => $filter) |
|
1753
|
|
|
{ |
|
1754
|
|
|
if (!($pref_value = $GLOBALS['egw_info']['user']['preferences']['infolog'][$pref])) continue; |
|
1755
|
|
|
|
|
1756
|
|
|
$filter .= date('Y-m-d',time()+24*60*60*(int)$pref_value); |
|
1757
|
|
|
//error_log(__METHOD__."() checking with filter '$filter' ($pref_value) for user $user ($email)"); |
|
1758
|
|
|
|
|
1759
|
|
|
$params = array('filter' => $filter, 'custom_fields' => true, 'subs' => true); |
|
1760
|
|
|
foreach($this->so->search($params) as $info) |
|
1761
|
|
|
{ |
|
1762
|
|
|
// check if we already send a notification for that infolog entry, eg. starting and due on same day |
|
1763
|
|
|
if (in_array($info['info_id'],$notified_info_ids)) continue; |
|
1764
|
|
|
|
|
1765
|
|
|
if (is_null($this->tracking) || $this->tracking->user != $user) |
|
1766
|
|
|
{ |
|
1767
|
|
|
$this->tracking = new infolog_tracking($this); |
|
1768
|
|
|
} |
|
1769
|
|
|
switch($pref) |
|
1770
|
|
|
{ |
|
1771
|
|
|
case 'notify_due_responsible': |
|
1772
|
|
|
$info['prefix'] = lang('Due %1',$this->enums['type'][$info['info_type']]); |
|
|
|
|
|
|
1773
|
|
|
$info['message'] = lang('%1 you are responsible for is due at %2',$this->enums['type'][$info['info_type']], |
|
1774
|
|
|
$this->tracking->datetime($info['info_enddate'],false)); |
|
1775
|
|
|
break; |
|
1776
|
|
|
case 'notify_due_delegated': |
|
1777
|
|
|
$info['prefix'] = lang('Due %1',$this->enums['type'][$info['info_type']]); |
|
1778
|
|
|
$info['message'] = lang('%1 you delegated is due at %2',$this->enums['type'][$info['info_type']], |
|
1779
|
|
|
$this->tracking->datetime($info['info_enddate'],false)); |
|
1780
|
|
|
break; |
|
1781
|
|
|
case 'notify_start_responsible': |
|
1782
|
|
|
$info['prefix'] = lang('Starting %1',$this->enums['type'][$info['info_type']]); |
|
1783
|
|
|
$info['message'] = lang('%1 you are responsible for is starting at %2',$this->enums['type'][$info['info_type']], |
|
1784
|
|
|
$this->tracking->datetime($info['info_startdate'],null)); |
|
1785
|
|
|
break; |
|
1786
|
|
|
case 'notify_start_delegated': |
|
1787
|
|
|
$info['prefix'] = lang('Starting %1',$this->enums['type'][$info['info_type']]); |
|
1788
|
|
|
$info['message'] = lang('%1 you delegated is starting at %2',$this->enums['type'][$info['info_type']], |
|
1789
|
|
|
$this->tracking->datetime($info['info_startdate'],null)); |
|
1790
|
|
|
break; |
|
1791
|
|
|
} |
|
1792
|
|
|
//error_log("notifiying $user($email) about $info[info_subject]: $info[message]"); |
|
1793
|
|
|
$this->tracking->send_notification($info,null,$email,$user,$pref); |
|
1794
|
|
|
|
|
1795
|
|
|
$notified_info_ids[] = $info['info_id']; |
|
1796
|
|
|
} |
|
1797
|
|
|
} |
|
1798
|
|
|
} |
|
1799
|
|
|
|
|
1800
|
|
|
$GLOBALS['egw_info']['user']['account_id'] = $save_account_id; |
|
1801
|
|
|
$GLOBALS['egw_info']['user']['preferences'] = $save_prefs; |
|
1802
|
|
|
} |
|
1803
|
|
|
|
|
1804
|
|
|
/** conversion of infolog status to vtodo status |
|
1805
|
|
|
* @private |
|
1806
|
|
|
* @var array |
|
1807
|
|
|
*/ |
|
1808
|
|
|
var $_status2vtodo = array( |
|
1809
|
|
|
'offer' => 'NEEDS-ACTION', |
|
1810
|
|
|
'not-started' => 'NEEDS-ACTION', |
|
1811
|
|
|
'ongoing' => 'IN-PROCESS', |
|
1812
|
|
|
'done' => 'COMPLETED', |
|
1813
|
|
|
'cancelled' => 'CANCELLED', |
|
1814
|
|
|
'billed' => 'COMPLETED', |
|
1815
|
|
|
'template' => 'CANCELLED', |
|
1816
|
|
|
'nonactive' => 'CANCELLED', |
|
1817
|
|
|
'archive' => 'CANCELLED', |
|
1818
|
|
|
'deferred' => 'NEEDS-ACTION', |
|
1819
|
|
|
'waiting' => 'IN-PROCESS', |
|
1820
|
|
|
); |
|
1821
|
|
|
|
|
1822
|
|
|
/** conversion of vtodo status to infolog status |
|
1823
|
|
|
* @private |
|
1824
|
|
|
* @var array |
|
1825
|
|
|
*/ |
|
1826
|
|
|
var $_vtodo2status = array( |
|
1827
|
|
|
'NEEDS-ACTION' => 'not-started', |
|
1828
|
|
|
'NEEDS ACTION' => 'not-started', |
|
1829
|
|
|
'IN-PROCESS' => 'ongoing', |
|
1830
|
|
|
'IN PROCESS' => 'ongoing', |
|
1831
|
|
|
'COMPLETED' => 'done', |
|
1832
|
|
|
'CANCELLED' => 'cancelled', |
|
1833
|
|
|
); |
|
1834
|
|
|
|
|
1835
|
|
|
/** |
|
1836
|
|
|
* Converts an infolog status into a vtodo status |
|
1837
|
|
|
* |
|
1838
|
|
|
* @param string $status see $this->status |
|
1839
|
|
|
* @return string {CANCELLED|NEEDS-ACTION|COMPLETED|IN-PROCESS} |
|
1840
|
|
|
*/ |
|
1841
|
|
|
function status2vtodo($status) |
|
1842
|
|
|
{ |
|
1843
|
|
|
return isset($this->_status2vtodo[$status]) ? $this->_status2vtodo[$status] : 'NEEDS-ACTION'; |
|
1844
|
|
|
} |
|
1845
|
|
|
|
|
1846
|
|
|
/** |
|
1847
|
|
|
* Converts a vtodo status into an infolog status using the optional X-INFOLOG-STATUS |
|
1848
|
|
|
* |
|
1849
|
|
|
* X-INFOLOG-STATUS is only used, if translated to the vtodo-status gives the identical vtodo status |
|
1850
|
|
|
* --> the user did not changed it |
|
1851
|
|
|
* |
|
1852
|
|
|
* @param string $_vtodo_status {CANCELLED|NEEDS-ACTION|COMPLETED|IN-PROCESS} |
|
1853
|
|
|
* @param string $x_infolog_status preserved original infolog status |
|
1854
|
|
|
* @return string |
|
1855
|
|
|
*/ |
|
1856
|
|
|
function vtodo2status($_vtodo_status,$x_infolog_status=null) |
|
1857
|
|
|
{ |
|
1858
|
|
|
$vtodo_status = strtoupper($_vtodo_status); |
|
1859
|
|
|
|
|
1860
|
|
|
if ($x_infolog_status && $this->status2vtodo($x_infolog_status) == $vtodo_status) |
|
1861
|
|
|
{ |
|
1862
|
|
|
$status = $x_infolog_status; |
|
1863
|
|
|
} |
|
1864
|
|
|
else |
|
1865
|
|
|
{ |
|
1866
|
|
|
$status = isset($this->_vtodo2status[$vtodo_status]) ? $this->_vtodo2status[$vtodo_status] : 'not-started'; |
|
1867
|
|
|
} |
|
1868
|
|
|
return $status; |
|
1869
|
|
|
} |
|
1870
|
|
|
|
|
1871
|
|
|
/** |
|
1872
|
|
|
* Get status of a single or all types |
|
1873
|
|
|
* |
|
1874
|
|
|
* As status value can have different translations depending on type, we list all translations |
|
1875
|
|
|
* |
|
1876
|
|
|
* @param string $type = null |
|
1877
|
|
|
* @param array &$icons = null on return name of icons |
|
1878
|
|
|
* @return array value => (commaseparated) translations |
|
1879
|
|
|
*/ |
|
1880
|
|
|
function get_status($type=null, array &$icons=null) |
|
1881
|
|
|
{ |
|
1882
|
|
|
// if filtered by type, show only the stati of the filtered type |
|
1883
|
|
|
if ($type && isset($this->status[$type])) |
|
1884
|
|
|
{ |
|
1885
|
|
|
$statis = $icons = $this->status[$type]; |
|
1886
|
|
|
} |
|
1887
|
|
|
else // show all stati |
|
1888
|
|
|
{ |
|
1889
|
|
|
$statis = $icons = array(); |
|
1890
|
|
|
foreach($this->status as $t => $stati) |
|
1891
|
|
|
{ |
|
1892
|
|
|
if ($t === 'defaults') continue; |
|
1893
|
|
|
foreach($stati as $val => $label) |
|
1894
|
|
|
{ |
|
1895
|
|
|
$statis[$val][$label] = lang($label); |
|
1896
|
|
|
if (!isset($icons[$val])) $icons[$val] = $label; |
|
1897
|
|
|
} |
|
1898
|
|
|
} |
|
1899
|
|
|
foreach($statis as $val => &$labels) |
|
1900
|
|
|
{ |
|
1901
|
|
|
$labels = implode(', ', $labels); |
|
1902
|
|
|
} |
|
1903
|
|
|
} |
|
1904
|
|
|
return $statis; |
|
1905
|
|
|
} |
|
1906
|
|
|
|
|
1907
|
|
|
/** |
|
1908
|
|
|
* Activates an InfoLog entry (setting it's status from template or inactive depending on the completed percentage) |
|
1909
|
|
|
* |
|
1910
|
|
|
* @param array $info |
|
1911
|
|
|
* @return string new status |
|
1912
|
|
|
*/ |
|
1913
|
|
|
function activate($info) |
|
1914
|
|
|
{ |
|
1915
|
|
|
switch((int)$info['info_percent']) |
|
1916
|
|
|
{ |
|
1917
|
|
|
case 0: return 'not-started'; |
|
1918
|
|
|
case 100: return 'done'; |
|
1919
|
|
|
} |
|
1920
|
|
|
return 'ongoing'; |
|
1921
|
|
|
} |
|
1922
|
|
|
|
|
1923
|
|
|
/** |
|
1924
|
|
|
* Get the Parent ID of an InfoLog entry |
|
1925
|
|
|
* |
|
1926
|
|
|
* @param string $_guid |
|
1927
|
|
|
* @return string parentID |
|
1928
|
|
|
*/ |
|
1929
|
|
|
function getParentID($_guid) |
|
1930
|
|
|
{ |
|
1931
|
|
|
#Horde::logMessage("getParentID($_guid)", __FILE__, __LINE__, PEAR_LOG_DEBUG); |
|
1932
|
|
|
|
|
1933
|
|
|
$parentID = False; |
|
1934
|
|
|
$myfilter = array('col_filter' => array('info_uid'=>$_guid)) ; |
|
1935
|
|
|
if ($_guid && ($found=$this->search($myfilter)) && ($uidmatch = array_shift($found))) |
|
1936
|
|
|
{ |
|
1937
|
|
|
$parentID = $uidmatch['info_id']; |
|
1938
|
|
|
} |
|
1939
|
|
|
return $parentID; |
|
1940
|
|
|
} |
|
1941
|
|
|
|
|
1942
|
|
|
/** |
|
1943
|
|
|
* Try to find a matching db entry |
|
1944
|
|
|
* This expects timestamps to be in server-time. |
|
1945
|
|
|
* |
|
1946
|
|
|
* @param array $infoData the infolog data we try to find |
|
1947
|
|
|
* @param boolean $relax = false if asked to relax, we only match against some key fields |
|
1948
|
|
|
* @param string $tzid = null timezone, null => user time |
|
1949
|
|
|
* |
|
1950
|
|
|
* @return array of infolog_ids of matching entries |
|
1951
|
|
|
*/ |
|
1952
|
|
|
function findInfo($infoData, $relax=false, $tzid=null) |
|
1953
|
|
|
{ |
|
1954
|
|
|
$foundInfoLogs = array(); |
|
1955
|
|
|
$filter = array(); |
|
1956
|
|
|
|
|
1957
|
|
|
if ($this->log) |
|
1958
|
|
|
{ |
|
1959
|
|
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ |
|
1960
|
|
|
. '('. ($relax ? 'RELAX, ': 'EXACT, ') . $tzid . ')[InfoData]:' |
|
1961
|
|
|
. array2string($infoData)); |
|
1962
|
|
|
} |
|
1963
|
|
|
|
|
1964
|
|
|
if ($infoData['info_id'] |
|
1965
|
|
|
&& ($egwData = $this->read($infoData['info_id'], true, 'server'))) |
|
1966
|
|
|
{ |
|
1967
|
|
|
// we only do a simple consistency check |
|
1968
|
|
|
if (!$relax || strpos($egwData['info_subject'], $infoData['info_subject']) === 0) |
|
1969
|
|
|
{ |
|
1970
|
|
|
return array($egwData['info_id']); |
|
1971
|
|
|
} |
|
1972
|
|
|
if (!$relax) return array(); |
|
|
|
|
|
|
1973
|
|
|
} |
|
1974
|
|
|
unset($infoData['info_id']); |
|
1975
|
|
|
|
|
1976
|
|
|
if (!$relax && !empty($infoData['info_uid'])) |
|
1977
|
|
|
{ |
|
1978
|
|
|
$filter = array('col_filter' => array('info_uid' => $infoData['info_uid'])); |
|
1979
|
|
|
foreach($this->so->search($filter) as $egwData) |
|
1980
|
|
|
{ |
|
1981
|
|
|
if (!$this->check_access($egwData,Acl::READ)) continue; |
|
1982
|
|
|
$foundInfoLogs[$egwData['info_id']] = $egwData['info_id']; |
|
1983
|
|
|
} |
|
1984
|
|
|
return $foundInfoLogs; |
|
1985
|
|
|
} |
|
1986
|
|
|
unset($infoData['info_uid']); |
|
1987
|
|
|
|
|
1988
|
|
|
if (empty($infoData['info_des'])) |
|
1989
|
|
|
{ |
|
1990
|
|
|
$description = false; |
|
1991
|
|
|
} |
|
1992
|
|
|
else |
|
1993
|
|
|
{ |
|
1994
|
|
|
// ignore meta information appendices |
|
1995
|
|
|
$description = trim(preg_replace('/\s*\[[A-Z_]+:.*\].*/im', '', $infoData['info_des'])); |
|
1996
|
|
|
$text = trim(preg_replace('/\s*\[[A-Z_]+:.*\]/im', '', $infoData['info_des'])); |
|
1997
|
|
|
if ($this->log) |
|
1998
|
|
|
{ |
|
1999
|
|
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ |
|
2000
|
|
|
. "()[description]: $description"); |
|
2001
|
|
|
} |
|
2002
|
|
|
// Avoid quotation problems |
|
2003
|
|
|
$matches = null; |
|
2004
|
|
|
if (preg_match_all('/[\x20-\x7F]*/m', $text, $matches, PREG_SET_ORDER)) |
|
2005
|
|
|
{ |
|
2006
|
|
|
$text = ''; |
|
2007
|
|
|
foreach ($matches as $chunk) |
|
2008
|
|
|
{ |
|
2009
|
|
|
if (strlen($text) < strlen($chunk[0])) |
|
2010
|
|
|
{ |
|
2011
|
|
|
$text = $chunk[0]; |
|
2012
|
|
|
} |
|
2013
|
|
|
} |
|
2014
|
|
|
if ($this->log) |
|
2015
|
|
|
{ |
|
2016
|
|
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ |
|
2017
|
|
|
. "()[search]: $text"); |
|
2018
|
|
|
} |
|
2019
|
|
|
$filter['search'] = $text; |
|
2020
|
|
|
} |
|
2021
|
|
|
} |
|
2022
|
|
|
$this->time2time($infoData, $tzid, false); |
|
2023
|
|
|
|
|
2024
|
|
|
$filter['col_filter'] = $infoData; |
|
2025
|
|
|
// priority does not need to match |
|
2026
|
|
|
unset($filter['col_filter']['info_priority']); |
|
2027
|
|
|
// we ignore description and location first |
|
2028
|
|
|
unset($filter['col_filter']['info_des']); |
|
2029
|
|
|
unset($filter['col_filter']['info_location']); |
|
2030
|
|
|
|
|
2031
|
|
|
foreach ($this->so->search($filter) as $itemID => $egwData) |
|
2032
|
|
|
{ |
|
2033
|
|
|
if (!$this->check_access($egwData,Acl::READ)) continue; |
|
2034
|
|
|
|
|
2035
|
|
|
switch ($infoData['info_type']) |
|
2036
|
|
|
{ |
|
2037
|
|
|
case 'task': |
|
2038
|
|
|
if (!empty($egwData['info_location'])) |
|
2039
|
|
|
{ |
|
2040
|
|
|
$egwData['info_location'] = str_replace("\r\n", "\n", $egwData['info_location']); |
|
2041
|
|
|
} |
|
2042
|
|
|
if (!$relax && |
|
2043
|
|
|
!empty($infoData['info_location']) && (empty($egwData['info_location']) |
|
2044
|
|
|
|| strpos($egwData['info_location'], $infoData['info_location']) !== 0)) |
|
2045
|
|
|
{ |
|
2046
|
|
|
if ($this->log) |
|
2047
|
|
|
{ |
|
2048
|
|
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ |
|
2049
|
|
|
. '()[location mismatch]: ' |
|
2050
|
|
|
. $infoData['info_location'] . ' <> ' . $egwData['info_location']); |
|
2051
|
|
|
} |
|
2052
|
|
|
continue 2; // +1 for switch |
|
2053
|
|
|
} |
|
2054
|
|
|
default: |
|
2055
|
|
|
if (!empty($egwData['info_des'])) |
|
2056
|
|
|
{ |
|
2057
|
|
|
$egwData['info_des'] = str_replace("\r\n", "\n", $egwData['info_des']); |
|
2058
|
|
|
} |
|
2059
|
|
|
if (!$relax && ($description && empty($egwData['info_des']) |
|
2060
|
|
|
|| !empty($egwData['info_des']) && empty($infoData['info_des']) |
|
2061
|
|
|
|| strpos($egwData['info_des'], $description) === false)) |
|
2062
|
|
|
{ |
|
2063
|
|
|
if ($this->log) |
|
2064
|
|
|
{ |
|
2065
|
|
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ |
|
2066
|
|
|
. '()[description mismatch]: ' |
|
2067
|
|
|
. $infoData['info_des'] . ' <> ' . $egwData['info_des']); |
|
2068
|
|
|
} |
|
2069
|
|
|
continue 2; // +1 for switch |
|
2070
|
|
|
} |
|
2071
|
|
|
// no further criteria to match |
|
2072
|
|
|
$foundInfoLogs[$egwData['info_id']] = $egwData['info_id']; |
|
2073
|
|
|
} |
|
2074
|
|
|
} |
|
2075
|
|
|
|
|
2076
|
|
|
if (!$relax && !empty($foundInfoLogs)) |
|
2077
|
|
|
{ |
|
2078
|
|
|
if ($this->log) |
|
2079
|
|
|
{ |
|
2080
|
|
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ |
|
2081
|
|
|
. '()[FOUND]:' . array2string($foundInfoLogs)); |
|
2082
|
|
|
} |
|
2083
|
|
|
return $foundInfoLogs; |
|
2084
|
|
|
} |
|
2085
|
|
|
|
|
2086
|
|
|
if ($relax) |
|
2087
|
|
|
{ |
|
2088
|
|
|
unset($filter['search']); |
|
2089
|
|
|
} |
|
2090
|
|
|
|
|
2091
|
|
|
// search for matches by date only |
|
2092
|
|
|
unset($filter['col_filter']['info_startdate']); |
|
2093
|
|
|
unset($filter['col_filter']['info_enddate']); |
|
2094
|
|
|
unset($filter['col_filter']['info_datecompleted']); |
|
2095
|
|
|
// Some devices support lesser stati |
|
2096
|
|
|
unset($filter['col_filter']['info_status']); |
|
2097
|
|
|
|
|
2098
|
|
|
// try tasks without category |
|
2099
|
|
|
unset($filter['col_filter']['info_cat']); |
|
2100
|
|
|
|
|
2101
|
|
|
// Horde::logMessage("findVTODO Filter\n" |
|
2102
|
|
|
// . print_r($filter, true), |
|
2103
|
|
|
// __FILE__, __LINE__, PEAR_LOG_DEBUG); |
|
2104
|
|
|
foreach ($this->so->search($filter) as $itemID => $egwData) |
|
2105
|
|
|
{ |
|
2106
|
|
|
if (!$this->check_access($egwData,Acl::READ)) continue; |
|
2107
|
|
|
// Horde::logMessage("findVTODO Trying\n" |
|
2108
|
|
|
// . print_r($egwData, true), |
|
2109
|
|
|
// __FILE__, __LINE__, PEAR_LOG_DEBUG); |
|
2110
|
|
|
if (isset($infoData['info_cat']) |
|
2111
|
|
|
&& isset($egwData['info_cat']) && $egwData['info_cat'] |
|
2112
|
|
|
&& $infoData['info_cat'] != $egwData['info_cat']) |
|
2113
|
|
|
{ |
|
2114
|
|
|
if ($this->log) |
|
2115
|
|
|
{ |
|
2116
|
|
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ |
|
2117
|
|
|
. '()[category mismatch]: ' |
|
2118
|
|
|
. $infoData['info_cat'] . ' <> ' . $egwData['info_cat']); |
|
2119
|
|
|
} |
|
2120
|
|
|
continue; |
|
2121
|
|
|
} |
|
2122
|
|
|
if (isset($infoData['info_startdate']) && $infoData['info_startdate']) |
|
2123
|
|
|
{ |
|
2124
|
|
|
// We got a startdate from client |
|
2125
|
|
|
if (isset($egwData['info_startdate']) && $egwData['info_startdate']) |
|
2126
|
|
|
{ |
|
2127
|
|
|
// We compare the date only |
|
2128
|
|
|
$taskTime = new Api\DateTime($infoData['info_startdate'],Api\DateTime::$server_timezone); |
|
2129
|
|
|
$egwTime = new Api\DateTime($egwData['info_startdate'],Api\DateTime::$server_timezone); |
|
2130
|
|
|
if ($taskTime->format('Ymd') != $egwTime->format('Ymd')) |
|
2131
|
|
|
{ |
|
2132
|
|
|
if ($this->log) |
|
2133
|
|
|
{ |
|
2134
|
|
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ |
|
2135
|
|
|
. '()[start mismatch]: ' |
|
2136
|
|
|
. $taskTime->format('Ymd') . ' <> ' . $egwTime->format('Ymd')); |
|
2137
|
|
|
} |
|
2138
|
|
|
continue; |
|
2139
|
|
|
} |
|
2140
|
|
|
} |
|
2141
|
|
|
elseif (!$relax) |
|
2142
|
|
|
{ |
|
2143
|
|
|
if ($this->log) |
|
2144
|
|
|
{ |
|
2145
|
|
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ |
|
2146
|
|
|
. '()[start mismatch]'); |
|
2147
|
|
|
} |
|
2148
|
|
|
continue; |
|
2149
|
|
|
} |
|
2150
|
|
|
} |
|
2151
|
|
|
if ($infoData['info_type'] == 'task') |
|
2152
|
|
|
{ |
|
2153
|
|
|
if (isset($infoData['info_status']) && isset($egwData['info_status']) |
|
|
|
|
|
|
2154
|
|
|
&& $egwData['info_status'] == 'done' |
|
2155
|
|
|
&& $infoData['info_status'] != 'done' || |
|
2156
|
|
|
$egwData['info_status'] != 'done' |
|
2157
|
|
|
&& $infoData['info_status'] == 'done') |
|
2158
|
|
|
{ |
|
2159
|
|
|
if ($this->log) |
|
2160
|
|
|
{ |
|
2161
|
|
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ |
|
2162
|
|
|
. '()[status mismatch]: ' |
|
2163
|
|
|
. $infoData['info_status'] . ' <> ' . $egwData['info_status']); |
|
2164
|
|
|
} |
|
2165
|
|
|
continue; |
|
2166
|
|
|
} |
|
2167
|
|
|
if (isset($infoData['info_enddate']) && $infoData['info_enddate']) |
|
2168
|
|
|
{ |
|
2169
|
|
|
// We got a enddate from client |
|
2170
|
|
|
if (isset($egwData['info_enddate']) && $egwData['info_enddate']) |
|
2171
|
|
|
{ |
|
2172
|
|
|
// We compare the date only |
|
2173
|
|
|
$taskTime = new Api\DateTime($infoData['info_enddate'],Api\DateTime::$server_timezone); |
|
2174
|
|
|
$egwTime = new Api\DateTime($egwData['info_enddate'],Api\DateTime::$server_timezone); |
|
2175
|
|
|
if ($taskTime->format('Ymd') != $egwTime->format('Ymd')) |
|
2176
|
|
|
{ |
|
2177
|
|
|
if ($this->log) |
|
2178
|
|
|
{ |
|
2179
|
|
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ |
|
2180
|
|
|
. '()[DUE mismatch]: ' |
|
2181
|
|
|
. $taskTime->format('Ymd') . ' <> ' . $egwTime->format('Ymd')); |
|
2182
|
|
|
} |
|
2183
|
|
|
continue; |
|
2184
|
|
|
} |
|
2185
|
|
|
} |
|
2186
|
|
|
elseif (!$relax) |
|
2187
|
|
|
{ |
|
2188
|
|
|
if ($this->log) |
|
2189
|
|
|
{ |
|
2190
|
|
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ |
|
2191
|
|
|
. '()[DUE mismatch]'); |
|
2192
|
|
|
} |
|
2193
|
|
|
continue; |
|
2194
|
|
|
} |
|
2195
|
|
|
} |
|
2196
|
|
|
if (isset($infoData['info_datecompleted']) && $infoData['info_datecompleted']) |
|
2197
|
|
|
{ |
|
2198
|
|
|
// We got a completed date from client |
|
2199
|
|
|
if (isset($egwData['info_datecompleted']) && $egwData['info_datecompleted']) |
|
2200
|
|
|
{ |
|
2201
|
|
|
// We compare the date only |
|
2202
|
|
|
$taskTime = new Api\DateTime($infoData['info_datecompleted'],Api\DateTime::$server_timezone); |
|
2203
|
|
|
$egwTime = new Api\DateTime($egwData['info_datecompleted'],Api\DateTime::$server_timezone); |
|
2204
|
|
|
if ($taskTime->format('Ymd') != $egwTime->format('Ymd')) |
|
2205
|
|
|
{ |
|
2206
|
|
|
if ($this->log) |
|
2207
|
|
|
{ |
|
2208
|
|
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ |
|
2209
|
|
|
. '()[completed mismatch]: ' |
|
2210
|
|
|
. $taskTime->format('Ymd') . ' <> ' . $egwTime->format('Ymd')); |
|
2211
|
|
|
} |
|
2212
|
|
|
continue; |
|
2213
|
|
|
} |
|
2214
|
|
|
} |
|
2215
|
|
|
elseif (!$relax) |
|
2216
|
|
|
{ |
|
2217
|
|
|
if ($this->log) |
|
2218
|
|
|
{ |
|
2219
|
|
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ |
|
2220
|
|
|
. '()[completed mismatch]'); |
|
2221
|
|
|
} |
|
2222
|
|
|
continue; |
|
2223
|
|
|
} |
|
2224
|
|
|
} |
|
2225
|
|
|
elseif (!$relax && isset($egwData['info_datecompleted']) && $egwData['info_datecompleted']) |
|
2226
|
|
|
{ |
|
2227
|
|
|
if ($this->log) |
|
2228
|
|
|
{ |
|
2229
|
|
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ |
|
2230
|
|
|
. '()[completed mismatch]'); |
|
2231
|
|
|
} |
|
2232
|
|
|
continue; |
|
2233
|
|
|
} |
|
2234
|
|
|
} |
|
2235
|
|
|
$foundInfoLogs[$itemID] = $itemID; |
|
2236
|
|
|
} |
|
2237
|
|
|
if ($this->log) |
|
2238
|
|
|
{ |
|
2239
|
|
|
error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ |
|
2240
|
|
|
. '()[FOUND]:' . array2string($foundInfoLogs)); |
|
2241
|
|
|
} |
|
2242
|
|
|
return $foundInfoLogs; |
|
2243
|
|
|
} |
|
2244
|
|
|
} |
|
2245
|
|
|
|