Issues (1098)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/dbo/records.php (15 issues)

Encourage use of @property annotation when providing magic access

Documentation Minor

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

Code
1
<?php
2
3
//------------------------------------------------------------------------------
4
//
5
//  eTraxis - Records tracking web-based system
6
//  Copyright (C) 2005-2012  Artem Rodygin
7
//
8
//  This program is free software: you can redistribute it and/or modify
9
//  it under the terms of the GNU General Public License as published by
10
//  the Free Software Foundation, either version 3 of the License, or
11
//  (at your option) any later version.
12
//
13
//  This program is distributed in the hope that it will be useful,
14
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
//  GNU General Public License for more details.
17
//
18
//  You should have received a copy of the GNU General Public License
19
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
//
21
//------------------------------------------------------------------------------
22
23
/**
24
 * Records
25
 *
26
 * This module provides API to work with records.
27
 * See also {@link https://github.com/etraxis/etraxis-obsolete/wiki/tbl_records tbl_records} database table.
28
 *
29
 * @package DBO
30
 * @subpackage Records
31
 */
32
33
/**#@+
34
 * Dependency.
35
 */
36
require_once('../engine/engine.php');
37
require_once('../dbo/accounts.php');
38
require_once('../dbo/filters.php');
39
require_once('../dbo/fields.php');
40
require_once('../dbo/values.php');
41
require_once('../dbo/events.php');
42
/**#@-*/
43
44
//------------------------------------------------------------------------------
45
//  Definitions.
46
//------------------------------------------------------------------------------
47
48
/**#@+
49
 * Data restrictions.
50
 */
51
define('MAX_RECORD_SUBJECT',  250);
52
define('MAX_COMMENT_BODY',    4000);
53
define('MAX_ATTACHMENT_NAME', 100);
54
define('MAX_SEARCH_TEXT',     100);
55
define('MAX_SEARCH_WORDS',    5);
56
/**#@-*/
57
58
/**#@+
59
 * Record operations.
60
 */
61
define('OPERATION_CREATE_RECORD', 1);
62
define('OPERATION_MODIFY_RECORD', 2);
63
define('OPERATION_CHANGE_STATE',  3);
64
/**#@-*/
65
66
/**#@+
67
 * Tabs on record view page.
68
 */
69
define('RECORD_TAB_MAIN',           1);
70
define('RECORD_TAB_HISTORY',        2);
71
define('RECORD_TAB_CHANGES',        3);
72
define('RECORD_TAB_FIELDS',         4);
73
define('RECORD_TAB_COMMENTS',       5);
74
define('RECORD_TAB_ATTACHMENTS',    6);
75
define('RECORD_TAB_PARENTS',        7);
76
define('RECORD_TAB_SUBRECORDS',     8);
77
/**#@-*/
78
79
//------------------------------------------------------------------------------
80
//  Functions.
81
//------------------------------------------------------------------------------
82
83
/**
84
 * Formats specified record ID, adding template prefix if specified and leading zeroes if required.
85
 *
86
 * @param int $id Record ID.
87
 * @param string $prefix Template prefix.
88
 * @return string Formatted record ID.
89
 */
90
function record_id ($id, $prefix = NULL)
91
{
92
    debug_write_log(DEBUG_TRACE, '[record_id]');
93
    debug_write_log(DEBUG_DUMP,  '[record_id] $id     = ' . $id);
94
    debug_write_log(DEBUG_DUMP,  '[record_id] $prefix = ' . $prefix);
95
96
    return ustr2html((is_null($prefix) ? NULL : $prefix . '-') . str_pad($id, 3, '0', STR_PAD_LEFT));
97
}
98
99
/**
100
 * Finds in database and returns the information about specified record.
101
 *
102
 * @param int $id Record ID.
103
 * @return array Array with data if record is found in database, FALSE otherwise.
104
 */
105
function record_find ($id)
106
{
107
    debug_write_log(DEBUG_TRACE, '[record_find]');
108
    debug_write_log(DEBUG_DUMP,  '[record_find] $id = ' . $id);
109
110
    $rs = dal_query('records/fndid.sql', $id, time());
111
112
    return ($rs->rows == 0 ? FALSE : $rs->fetch());
0 ignored issues
show
The property $rows is declared protected in CRecordset. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
113
}
114
115
/**
116
 * Returns {@link CRecordset DAL recordset} which contains all records, allowed to be displayed
117
 * in accordance to current set of filters, search mode, user permissions, etc.
118
 * Recordset is sorted in accordance with current sort mode.
119
 *
120
 * @param array $columns List of columns (see {@link column_list}).
121
 * @param int &$sort Sort mode (used as output only). The function retrieves current sort mode from
122
 * client cookie ({@link COOKIE_RECORDS_SORT}) and updates it, if it's out of valid range.
123
 * @param int &$page Number of current page tab (used as output only). The function retrieves current
124
 * page from client cookie ({@link COOKIE_RECORDS_PAGE}) and updates it, if it's out of valid range.
125
 * @param bool $search_mode Whether the search mode is on.
126
 * @param string $search_text Text to be searched (ignored when search mode is off).
127
 * @return CRecordset Recordset with list of records.
128
 */
129
function records_list ($columns, &$sort, &$page, $search_mode = FALSE, $search_text = NULL)
130
{
131
    debug_write_log(DEBUG_TRACE, '[records_list]');
132
133
    $sort = try_request('sort', try_cookie(COOKIE_RECORDS_SORT . $_SESSION[VAR_VIEW]));
134
    $sort = ustr2int($sort, -count($columns), count($columns));
135
136
    $page = try_request('page', try_cookie(COOKIE_RECORDS_PAGE . $_SESSION[VAR_VIEW]));
137
    $page = ustr2int($page, 1, MAXINT);
138
139
    $date = getdate();
140
    $time = mktime(23, 59, 59, $date['mon'], $date['mday'], $date['year']);
141
142
    $clause_select = array('r.record_id',
143
                           'r.subject',
144
                           'r.responsible_id',
145
                           'r.creator_id',
146
                           'r.creation_time',
147
                           'r.change_time',
148
                           'r.closure_time',
149
                           'r.postpone_time',
150
                           'p.project_name',
151
                           't.template_name',
152
                           't.template_prefix',
153
                           't.critical_age',
154
                           's.state_name',
155
                           's.state_abbr');
156
157
    $clause_from   = array('tbl_projects p',
158
                           'tbl_states s');
159
160
    $clause_join   = array('tbl_records r');
161
162
    $clause_where  = array('p.project_id = t.project_id',
163
                           't.template_id = s.template_id',
164
                           's.state_id = r.state_id');
165
166
    save_cookie(COOKIE_RECORDS_SORT . $_SESSION[VAR_VIEW], $sort);
167
    save_cookie(COOKIE_RECORDS_PAGE . $_SESSION[VAR_VIEW], $page);
168
169
    // Add default access conditions for guests and registered users.
170
171
    if (get_user_level() == USER_LEVEL_GUEST)
172
    {
173
        array_push($clause_select, '0 as read_time');
174
        array_push($clause_from,   'tbl_templates t');
175
        array_push($clause_where,  't.guest_access = 1');
176
        array_push($clause_where,  'r.closure_time is null');
177
    }
178
    else
179
    {
180
        $perms = 'select gp.template_id, count(gp.group_id) as gnum '
181
               . 'from tbl_membership ms, tbl_group_perms gp '
182
               . 'where ms.account_id = %1 and ms.group_id = gp.group_id and '
183
               . (DATABASE_DRIVER == DRIVER_ORACLE9 ? 'mod(floor(gp.perms / %2), 2) = 1 ' : '(gp.perms & %2) <> 0 ')
184
               . 'group by template_id';
185
186
        $perms = ustrprocess($perms, $_SESSION[VAR_USERID], PERMIT_VIEW_RECORD);
187
188
        array_push($clause_from,  "tbl_templates t left outer join ({$perms}) perms on t.template_id = perms.template_id");
189
        array_push($clause_where, ustrprocess('(perms.gnum is not null' .
190
                                              ' or r.creator_id = %1' .
191
                                              ' or r.responsible_id = %1' .
192
                                              ' or ' . (DATABASE_DRIVER == DRIVER_ORACLE9 ? 'mod(floor(t.registered_perm / %2), 2) = 1' : '(t.registered_perm & %2) <> 0') .
193
                                              ' or t.guest_access = 1)',
194
                                              $_SESSION[VAR_USERID], PERMIT_VIEW_RECORD));
195
196
        array_push($clause_select, 'rd.read_time');
197
        array_push($clause_join,   'left outer join tbl_reads rd on rd.record_id = r.record_id and rd.account_id = ' . $_SESSION[VAR_USERID]);
198
    }
199
200
    // Add search conditions if search is activated.
201
202
    if ($search_mode)
203
    {
204
        debug_write_log(DEBUG_NOTICE, '[records_list] Search mode is turned on.');
205
        debug_write_log(DEBUG_DUMP,   '[records_list] $search_text = ' . $search_text);
206
207
        $search = array();
208
209
        if (is_intvalue($search_text))
210
        {
211
            array_push($search,
212
                       'select e.record_id ' .
213
                       'from tbl_events e, tbl_field_values fv ' .
214
                       'where e.event_id = fv.event_id and fv.field_type = ' . FIELD_TYPE_NUMBER . ' and fv.is_latest = 1 and fv.value_id = ' . $search_text);
215
        }
216
217
        $search_text = "'%" . ustr2sql(ustrtolower($search_text)) . "%'";
218
219
        $search_in_subject  = (in_array(DATABASE_DRIVER, array(DRIVER_ORACLE9, DRIVER_PGSQL80)) ? 'lower' : NULL) . '(r.subject)       like ' . $search_text;
220
        $search_in_svalues  = (in_array(DATABASE_DRIVER, array(DRIVER_ORACLE9, DRIVER_PGSQL80)) ? 'lower' : NULL) . '(sv.string_value) like ' . $search_text;
221
        $search_in_tvalues  = (in_array(DATABASE_DRIVER, array(DRIVER_ORACLE9, DRIVER_PGSQL80)) ? 'lower' : NULL) . '(tv.text_value)   like ' . $search_text;
222
        $search_in_comments = (in_array(DATABASE_DRIVER, array(DRIVER_ORACLE9, DRIVER_PGSQL80)) ? 'lower' : NULL) . '(c.comment_body)  like ' . $search_text;
223
224
        array_push($search,
225
                   'select r.record_id ' .
226
                   'from tbl_records r ' .
227
                   'where ' . $search_in_subject);
228
229
        array_push($search,
230
                   'select e.record_id ' .
231
                   'from tbl_events e, tbl_field_values fv, tbl_string_values sv ' .
232
                   'where e.event_id = fv.event_id and fv.field_type = ' . FIELD_TYPE_STRING . ' and fv.value_id = sv.value_id and fv.is_latest = 1 and ' .
233
                   $search_in_svalues);
234
235
        array_push($search,
236
                   'select e.record_id ' .
237
                   'from tbl_events e, tbl_field_values fv, tbl_text_values tv ' .
238
                   'where e.event_id = fv.event_id and fv.field_type = ' . FIELD_TYPE_MULTILINED . ' and fv.value_id = tv.value_id and fv.is_latest = 1 and ' .
239
                   $search_in_tvalues);
240
241
        array_push($search,
242
                   'select e.record_id ' .
243
                   'from tbl_events e, tbl_comments c ' .
244
                   'where e.event_id = c.event_id and ' .
245
                   $search_in_comments);
246
247
        $sql = 'select record_id from (' . implode(' union ', $search) . ') data';
248
249
        $ids = array();
250
        $rsx = new CRecordset($sql);
251
252
        while (($row = $rsx->fetch()))
253
        {
254
            array_push($ids, $row['record_id']);
255
        }
256
257
        $search_ids = (count($ids) == 0)
258
                   ? 'NULL'
259
                   : implode(', ', $ids);
260
261
        debug_write_log(DEBUG_NOTICE, '[records_list] $search_ids = [' . $search_ids . ']' );
262
263
        array_push($clause_where, 'r.record_id in (' . $search_ids . ')');
264
    }
265
266
    // Add filters, if it's not a search or if it's a filtered search.
267
268
    if (!$search_mode || $_SESSION[VAR_USE_FILTERS])
269
    {
270
        debug_write_log(DEBUG_NOTICE, '[records_list] Filters are in use.');
271
272
        $filters = array();
273
274
        $fsort = $fpage = NULL;
275
        $rs = filters_list($_SESSION[VAR_USERID], TRUE, $fsort, $fpage);
276
277
        while (($filter = $rs->fetch()))
278
        {
279
            if ($filter['filter_flags'] == 0 &&
280
                $filter['filter_type']  == FILTER_TYPE_ALL_PROJECTS)
281
            {
282
                continue;
283
            }
284
285
            $clause_filter = array();
286
287
            switch ($filter['filter_type'])
288
            {
289
                case FILTER_TYPE_ALL_PROJECTS:
290
                    break;
291
292
                case FILTER_TYPE_ALL_TEMPLATES:
293
                    array_push($clause_select, 'p.project_id');
294
                    array_push($clause_filter, 'p.project_id = ' . $filter['filter_param']);
295
                    break;
296
297
                case FILTER_TYPE_ALL_STATES:
298
                    array_push($clause_select, 't.template_id');
299
                    array_push($clause_filter, 't.template_id = ' . $filter['filter_param']);
300
                    break;
301
302
                case FILTER_TYPE_SEL_STATES:
303
                    $states = filter_states_get($filter['filter_id'], $filter['filter_param']);
304
                    array_push($clause_select, 's.state_id');
305
                    array_push($clause_filter, 's.state_id in (' . implode(',', array_unique($states)) . ')');
306
                    break;
307
308
                default:
309
                    debug_write_log(DEBUG_WARNING, '[records_list] Unknown filter type = ' . $filter['filter_type']);
310
            }
311
312 View Code Duplication
            if ($filter['filter_flags'] & FILTER_FLAG_CREATED_BY)
313
            {
314
                array_push($clause_select, 'r.creator_id');
315
                array_push($clause_filter,
316
                           'r.creator_id in ' .
317
                           '(select account_id ' .
318
                           'from tbl_filter_accounts ' .
319
                           'where filter_id = ' . $filter['filter_id'] . ' and filter_flag = ' . FILTER_FLAG_CREATED_BY . ')');
320
            }
321
322 View Code Duplication
            if ($filter['filter_flags'] & FILTER_FLAG_ASSIGNED_TO)
323
            {
324
                array_push($clause_select, 'r.responsible_id');
325
                array_push($clause_filter,
326
                           'r.responsible_id in ' .
327
                           '(select account_id ' .
328
                           'from tbl_filter_accounts ' .
329
                           'where filter_id = ' . $filter['filter_id'] . ' and filter_flag = ' . FILTER_FLAG_ASSIGNED_TO . ')');
330
            }
331
332
            if ($filter['filter_flags'] & FILTER_FLAG_UNASSIGNED)
333
            {
334
                array_push($clause_select, 'r.responsible_id');
335
                array_push($clause_filter, 'r.responsible_id is null');
336
            }
337
338
            if (($filter['filter_type'] != FILTER_TYPE_SEL_STATES) &&
339
                ($filter['filter_flags'] & FILTER_FLAG_UNCLOSED))
340
            {
341
                array_push($clause_filter, 'r.closure_time is null');
342
            }
343
344
            if ($filter['filter_flags'] & FILTER_FLAG_POSTPONED)
345
            {
346
                array_push($clause_filter, 'r.postpone_time > ' . $time);
347
            }
348
349
            if ($filter['filter_flags'] & FILTER_FLAG_ACTIVE)
350
            {
351
                array_push($clause_filter, 'r.postpone_time <=' . $time);
352
            }
353
354
            if ($filter['filter_type'] == FILTER_TYPE_ALL_STATES ||
355
                $filter['filter_type'] == FILTER_TYPE_SEL_STATES)
356
            {
357
                $rs2 = dal_query('filters/ftlist.sql', $filter['filter_id']);
358
359
                while (($row = $rs2->fetch()))
360
                {
361
                    array_push($clause_filter,
362
                               'r.record_id in ' .
363
                               '(select record_id ' .
364
                               'from tbl_events ' .
365
                               'where (event_type = ' . EVENT_RECORD_CREATED . ' or event_type = ' . EVENT_RECORD_STATE_CHANGED . ') and ' .
366
                               'event_time >= ' . $row['date1'] . ' and ' .
367
                               'event_time <= ' . $row['date2'] . ' and ' .
368
                               'event_param = ' . $row['state_id'] . ')');
369
                }
370
371
                $rs2 = dal_query('filters/fflist.sql', $filter['filter_id']);
372
373
                while (($row = $rs2->fetch()))
374
                {
375
                    switch ($row['field_type'])
376
                    {
377
                        case FIELD_TYPE_CHECKBOX:
378
                        case FIELD_TYPE_LIST:
379
                        case FIELD_TYPE_RECORD:
380
381
                            $value = (is_null($row['param1'])
382
                                      ? 'fv.value_id is null'
383
                                      : 'fv.value_id = ' . $row['param1']);
384
385
                            array_push($clause_filter,
386
                                       'r.record_id in ' .
387
                                       '(select e.record_id ' .
388
                                       'from tbl_events e, tbl_field_values fv ' .
389
                                       'where fv.event_id = e.event_id and ' .
390
                                       'fv.field_id = ' . $row['field_id'] . ' and ' .
391
                                       $value . ' and ' .
392
                                       'fv.is_latest = 1)');
393
394
                            break;
395
396
                        case FIELD_TYPE_NUMBER:
397
                        case FIELD_TYPE_DATE:
398
                        case FIELD_TYPE_DURATION:
399
400
                            $range = (is_null($row['param1']) && is_null($row['param2']) ? 'fv.value_id is null and ' : NULL);
401
402
                            if (!is_null($row['param1']))
403
                            {
404
                                $range .= 'fv.value_id >= ' . $row['param1'] . ' and ';
405
                            }
406
407
                            if (!is_null($row['param2']))
408
                            {
409
                                $range .= 'fv.value_id <= ' . $row['param2'] . ' and ';
410
                            }
411
412
                            array_push($clause_filter,
413
                                       'r.record_id in ' .
414
                                       '(select e.record_id ' .
415
                                       'from tbl_events e, tbl_field_values fv ' .
416
                                       'where fv.event_id = e.event_id and ' .
417
                                       'fv.field_id = '  . $row['field_id'] . ' and ' .
418
                                       $range .
419
                                       'fv.is_latest = 1)');
420
421
                            break;
422
423
                        case FIELD_TYPE_FLOAT:
424
425
                            $range = (is_null($row['param1']) && is_null($row['param2']) ? 'fv.value_id is null and ' : NULL);
426
427
                            if (!is_null($row['param1']))
428
                            {
429
                                $range .= 'fl1.float_value >= fl2.float_value and ';
430
                            }
431
432
                            if (!is_null($row['param2']))
433
                            {
434
                                $range .= 'fl1.float_value <= fl3.float_value and ';
435
                            }
436
437
                            array_push($clause_filter,
438
                                       'r.record_id in ' .
439
                                       '(select e.record_id ' .
440
                                       'from tbl_events e, tbl_field_values fv, tbl_float_values fl1, tbl_float_values fl2, tbl_float_values fl3 ' .
441
                                       'where fv.event_id = e.event_id and ' .
442
                                       'fv.field_id = '  . $row['field_id'] . ' and ' .
443
                                       'fv.value_id = fl1.value_id and ' .
444
                                       $range .
445
                                       'fl2.value_id = ' . $row['param1'] . ' and ' .
446
                                       'fl3.value_id = ' . $row['param2'] . ' and ' .
447
                                       'fv.is_latest = 1)');
448
449
                            break;
450
451 View Code Duplication
                        case FIELD_TYPE_STRING:
452
453
                            if (is_null($row['param1']))
454
                            {
455
                                array_push($clause_filter,
456
                                           'r.record_id in ' .
457
                                           '(select e.record_id ' .
458
                                           'from tbl_events e, tbl_field_values fv ' .
459
                                           'where fv.event_id = e.event_id and ' .
460
                                           'fv.field_id = ' . $row['field_id'] . ' and ' .
461
                                           'fv.value_id is null and ' .
462
                                           'fv.is_latest = 1)');
463
                            }
464
                            else
465
                            {
466
                                switch (DATABASE_DRIVER)
467
                                {
468
                                    case DRIVER_MYSQL50:
469
                                        $concat = "concat('%', sf.string_value, '%')";
470
                                        break;
471
                                    case DRIVER_ORACLE9:
472
                                        $concat = "concat(concat('%', sf.string_value), '%')";
473
                                        break;
474
                                    case DRIVER_MSSQL2K:
475
                                        $concat = "'%' + sf.string_value + '%'";
476
                                        break;
477
                                    case DRIVER_PGSQL80:
478
                                        $concat = "'%' || sf.string_value || '%'";
479
                                        break;
480
                                    default: ;  // nop
481
                                }
482
483
                                array_push($clause_filter,
484
                                           'r.record_id in ' .
485
                                           '(select e.record_id ' .
486
                                           'from tbl_events e, tbl_field_values fv, tbl_string_values sv, tbl_string_values sf ' .
487
                                           'where fv.event_id = e.event_id and ' .
488
                                           'fv.field_id = ' . $row['field_id'] . ' and ' .
489
                                           'fv.value_id = sv.value_id and ' .
490
                                           'sf.value_id = ' . $row['param1'] . ' and ' .
491
                                           'sv.string_value like ' . $concat . ' and ' .
492
                                           'fv.is_latest = 1)');
493
                            }
494
495
                            break;
496
497 View Code Duplication
                        case FIELD_TYPE_MULTILINED:
498
499
                            if (is_null($row['param1']))
500
                            {
501
                                array_push($clause_filter,
502
                                           'r.record_id in ' .
503
                                           '(select e.record_id ' .
504
                                           'from tbl_events e, tbl_field_values fv ' .
505
                                           'where fv.event_id = e.event_id and ' .
506
                                           'fv.field_id = ' . $row['field_id'] . ' and ' .
507
                                           'fv.value_id is null and ' .
508
                                           'fv.is_latest = 1)');
509
                            }
510
                            else
511
                            {
512
                                switch (DATABASE_DRIVER)
513
                                {
514
                                    case DRIVER_MYSQL50:
515
                                        $concat = "concat('%', sf.string_value, '%')";
516
                                        break;
517
                                    case DRIVER_ORACLE9:
518
                                        $concat = "concat(concat('%', sf.string_value), '%')";
519
                                        break;
520
                                    case DRIVER_MSSQL2K:
521
                                        $concat = "'%' + sf.string_value + '%'";
522
                                        break;
523
                                    case DRIVER_PGSQL80:
524
                                        $concat = "'%' || sf.string_value || '%'";
525
                                        break;
526
                                    default: ;  // nop
527
                                }
528
529
                                array_push($clause_filter,
530
                                           'r.record_id in ' .
531
                                           '(select e.record_id ' .
532
                                           'from tbl_events e, tbl_field_values fv, tbl_text_values tv, tbl_string_values sf ' .
533
                                           'where fv.event_id = e.event_id and ' .
534
                                           'fv.field_id = ' . $row['field_id'] . ' and ' .
535
                                           'fv.value_id = tv.value_id and ' .
536
                                           'sf.value_id = ' . $row['param1'] . ' and ' .
537
                                           'tv.text_value like ' . $concat . ' and ' .
538
                                           'fv.is_latest = 1)');
539
                            }
540
541
                            break;
542
                    }
543
                }
544
            }
545
546
            array_push($filters, implode(' and ', array_unique($clause_filter)));
547
        }
548
549
        if (count($filters) != 0)
550
        {
551
            array_push($clause_where, '(' . implode(' or ', array_unique($filters)) . ')');
552
        }
553
    }
554
555
    // Create basic query.
556
557
    $sql = 'select ' . implode(', ',    array_unique($clause_select)) .
558
           ' from '  . implode(', ',    array_unique($clause_from))   .
559
           ', '      . implode(' ',     array_unique($clause_join))   .
560
           ' where ' . implode(' and ', array_unique($clause_where));
561
562
    $clause_select = array('r.*');
563
    $clause_select = array('r.record_id',
564
                           'r.subject',
565
                           'r.responsible_id',
566
                           'r.creator_id',
567
                           'r.creation_time',
568
                           'r.change_time',
569
                           'r.closure_time',
570
                           'r.postpone_time',
571
                           'r.project_name',
572
                           'r.template_name',
573
                           'r.template_prefix',
574
                           'r.critical_age',
575
                           'r.state_name',
576
                           'r.state_abbr',
577
                           'r.read_time');
578
    $clause_from   = array();
579
    $clause_join   = array();
580
    $clause_where  = array();
581
    $clause_order  = array();
582
583
    // SQL condition to check that current user is allowed to read the field.
584
585
    $sql_field_perms = <<<SQL
586
587
        f.field_id in (select f.field_id
588
                       from
589
                           tbl_group_perms  gp,
590
                           tbl_membership   ms,
591
                           tbl_field_perms  fp,
592
                           tbl_fields f
593
                       where
594
                           fp.field_id   = f.field_id  and
595
                           fp.group_id   = gp.group_id and
596
                           ms.group_id   = gp.group_id and
597
                           ms.account_id = {$_SESSION[VAR_USERID]} and
598
                           fp.perms      = 1)
599
600
        or r.creator_id = {$_SESSION[VAR_USERID]} and
601
           f.field_id in (select f.field_id
602
                          from tbl_fields f
603
                          where f.author_perm >= 1)
604
605
        or r.responsible_id = {$_SESSION[VAR_USERID]} and
606
           f.field_id in (select f.field_id
607
                          from tbl_fields f
608
                          where f.responsible_perm >= 1)
609
610
        or f.field_id in (select f.field_id
611
                          from tbl_fields f
612
                          where f.registered_perm >= 1 or f.guest_access = 1)
613
SQL;
614
615
    // Generate columns of the current view.
616
617
    foreach ($columns as $i => $column)
618
    {
619
        $i += 1;
620
621
        switch ($column['column_type'])
622
        {
623 View Code Duplication
            case COLUMN_TYPE_ID:
624
625
                array_push($clause_select, 'r.template_prefix');
626
627
                if ($i == $sort)
628
                {
629
                    array_push($clause_order, 'record_id asc');
630
                }
631
                elseif (-$i == $sort)
632
                {
633
                    array_push($clause_order, 'record_id desc');
634
                }
635
636
                break;
637
638 View Code Duplication
            case COLUMN_TYPE_STATE_ABBR:
639
640
                array_push($clause_select, 'r.state_abbr');
641
642
                if ($i == $sort)
643
                {
644
                    array_push($clause_order, 'state_abbr asc');
645
                }
646
                elseif (-$i == $sort)
647
                {
648
                    array_push($clause_order, 'state_abbr desc');
649
                }
650
651
                break;
652
653 View Code Duplication
            case COLUMN_TYPE_PROJECT:
654
655
                array_push($clause_select, 'r.project_name');
656
657
                if ($i == $sort)
658
                {
659
                    array_push($clause_order, 'project_name asc');
660
                }
661
                elseif (-$i == $sort)
662
                {
663
                    array_push($clause_order, 'project_name desc');
664
                }
665
666
                break;
667
668 View Code Duplication
            case COLUMN_TYPE_SUBJECT:
669
670
                array_push($clause_select, 'r.subject');
671
672
                if ($i == $sort)
673
                {
674
                    array_push($clause_order, 'subject asc');
675
                }
676
                elseif (-$i == $sort)
677
                {
678
                    array_push($clause_order, 'subject desc');
679
                }
680
681
                break;
682
683 View Code Duplication
            case COLUMN_TYPE_AUTHOR:
684
685
                array_push($clause_select, 'ac.fullname as author_fullname');
686
                array_push($clause_join,   'left outer join tbl_accounts ac on ac.account_id = r.creator_id');
687
688
                if ($i == $sort)
689
                {
690
                    array_push($clause_order, 'author_fullname asc');
691
                }
692
                elseif (-$i == $sort)
693
                {
694
                    array_push($clause_order, 'author_fullname desc');
695
                }
696
697
                break;
698
699 View Code Duplication
            case COLUMN_TYPE_RESPONSIBLE:
700
701
                array_push($clause_select, 'ar.fullname as responsible_fullname');
702
                array_push($clause_join,   'left outer join tbl_accounts ar on ar.account_id = r.responsible_id');
703
704
                if ($i == $sort)
705
                {
706
                    array_push($clause_order, 'responsible_fullname asc');
707
                }
708
                elseif (-$i == $sort)
709
                {
710
                    array_push($clause_order, 'responsible_fullname desc');
711
                }
712
713
                break;
714
715 View Code Duplication
            case COLUMN_TYPE_LAST_EVENT:
716
717
                if ($i == $sort)
718
                {
719
                    array_push($clause_order, 'change_time asc');
720
                }
721
                elseif (-$i == $sort)
722
                {
723
                    array_push($clause_order, 'change_time desc');
724
                }
725
726
                break;
727
728 View Code Duplication
            case COLUMN_TYPE_AGE:
729
730
                array_push($clause_select, '(' . $time . ' - r.creation_time) as opened_age');
731
                array_push($clause_select, '(r.closure_time - r.creation_time) as closed_age');
732
733
                if ($i == $sort)
734
                {
735
                    array_push($clause_order, 'closed_age asc');
736
                    array_push($clause_order, 'opened_age asc');
737
                }
738
                elseif (-$i == $sort)
739
                {
740
                    array_push($clause_order, 'closed_age desc');
741
                    array_push($clause_order, 'opened_age desc');
742
                }
743
744
                break;
745
746 View Code Duplication
            case COLUMN_TYPE_CREATION_DATE:
747
748
                array_push($clause_select, 'r.creation_time');
749
750
                if ($i == $sort)
751
                {
752
                    array_push($clause_order, 'creation_time asc');
753
                }
754
                elseif (-$i == $sort)
755
                {
756
                    array_push($clause_order, 'creation_time desc');
757
                }
758
759
                break;
760
761 View Code Duplication
            case COLUMN_TYPE_TEMPLATE:
762
763
                array_push($clause_select, 'r.template_name');
764
765
                if ($i == $sort)
766
                {
767
                    array_push($clause_order, 'template_name asc');
768
                }
769
                elseif (-$i == $sort)
770
                {
771
                    array_push($clause_order, 'template_name desc');
772
                }
773
774
                break;
775
776 View Code Duplication
            case COLUMN_TYPE_STATE_NAME:
777
778
                array_push($clause_select, 'r.state_name');
779
780
                if ($i == $sort)
781
                {
782
                    array_push($clause_order, 'state_name asc');
783
                }
784
                elseif (-$i == $sort)
785
                {
786
                    array_push($clause_order, 'state_name desc');
787
                }
788
789
                break;
790
791 View Code Duplication
            case COLUMN_TYPE_LAST_STATE:
792
793
                array_push($clause_select, 'st.state_time');
794
                array_push($clause_from,   '(select record_id, max(event_time) as state_time' .
795
                                           ' from tbl_events' .
796
                                           ' where event_type = 1 or event_type = 4' .
797
                                           ' group by record_id) st');
798
                array_push($clause_where,  'r.record_id = st.record_id');
799
800
                if ($i == $sort)
801
                {
802
                    array_push($clause_order, 'state_time asc');
803
                }
804
                elseif (-$i == $sort)
805
                {
806
                    array_push($clause_order, 'state_time desc');
807
                }
808
809
                break;
810
811 View Code Duplication
            case COLUMN_TYPE_FLOAT:
812
813
                array_push($clause_select, "v{$column['column_id']}.value{$column['column_id']}");
814
815
                array_push($clause_join,
816
                           "left outer join " .
817
                           "(select r.record_id, flv.float_value as value{$column['column_id']} " .
818
                           "from tbl_records r, tbl_states s, tbl_fields f, tbl_events e, tbl_field_values fv " .
819
                           "left outer join tbl_float_values flv on fv.value_id = flv.value_id " .
820
                           "where r.record_id = e.record_id and s.state_id = f.state_id and s.state_name = '{$column['state_name']}' and f.field_id = fv.field_id and f.field_name = '{$column['field_name']}' and f.field_type = " . FIELD_TYPE_FLOAT . " and e.event_id = fv.event_id and fv.is_latest = 1 and ({$sql_field_perms})) v{$column['column_id']} " .
821
                           "on r.record_id = v{$column['column_id']}.record_id");
822
823
                if ($i == $sort)
824
                {
825
                    array_push($clause_order, "value{$column['column_id']} asc");
826
                }
827
                elseif (-$i == $sort)
828
                {
829
                    array_push($clause_order, "value{$column['column_id']} desc");
830
                }
831
832
                break;
833
834 View Code Duplication
            case COLUMN_TYPE_STRING:
835
836
                array_push($clause_select, "v{$column['column_id']}.value{$column['column_id']}");
837
838
                array_push($clause_join,
839
                           "left outer join " .
840
                           "(select r.record_id, sv.string_value as value{$column['column_id']} " .
841
                           "from tbl_records r, tbl_states s, tbl_fields f, tbl_events e, tbl_field_values fv " .
842
                           "left outer join tbl_string_values sv on fv.value_id = sv.value_id " .
843
                           "where r.record_id = e.record_id and s.state_id = f.state_id and s.state_name = '{$column['state_name']}' and f.field_id = fv.field_id and f.field_name = '{$column['field_name']}' and f.field_type = " . FIELD_TYPE_STRING . " and e.event_id = fv.event_id and fv.is_latest = 1 and ({$sql_field_perms})) v{$column['column_id']} " .
844
                           "on r.record_id = v{$column['column_id']}.record_id");
845
846
                if ($i == $sort)
847
                {
848
                    array_push($clause_order, "value{$column['column_id']} asc");
849
                }
850
                elseif (-$i == $sort)
851
                {
852
                    array_push($clause_order, "value{$column['column_id']} desc");
853
                }
854
855
                break;
856
857
            case COLUMN_TYPE_MULTILINED:
858
859
                $txtval = (DATABASE_DRIVER == DRIVER_MSSQL2K ? 'substring(tv.text_value, 1, 4000)' : 'substr(tv.text_value, 1, 4000)');
860
861
                if (DATABASE_DRIVER == DRIVER_ORACLE9)
862
                {
863
                    array_push($clause_select, "to_char(v{$column['column_id']}.value{$column['column_id']}) as \"value{$column['column_id']}\"");
864
                }
865
                else
866
                {
867
                    array_push($clause_select, "v{$column['column_id']}.value{$column['column_id']}");
868
                }
869
870
                array_push($clause_join,
871
                           "left outer join " .
872
                           "(select r.record_id, {$txtval} as value{$column['column_id']} " .
873
                           "from tbl_records r, tbl_states s, tbl_fields f, tbl_events e, tbl_field_values fv " .
874
                           "left outer join tbl_text_values tv on fv.value_id = tv.value_id " .
875
                           "where r.record_id = e.record_id and s.state_id = f.state_id and s.state_name = '{$column['state_name']}' and f.field_id = fv.field_id and f.field_name = '{$column['field_name']}' and f.field_type = " . FIELD_TYPE_MULTILINED . " and e.event_id = fv.event_id and fv.is_latest = 1 and ({$sql_field_perms})) v{$column['column_id']} " .
876
                           "on r.record_id = v{$column['column_id']}.record_id");
877
878
                if ($i == $sort)
879
                {
880
                    array_push($clause_order, DATABASE_DRIVER == DRIVER_ORACLE9
881
                                                    ? "to_char(value{$column['column_id']}) asc"
882
                                                    : "value{$column['column_id']} asc");
883
                }
884
                elseif (-$i == $sort)
885
                {
886
                    array_push($clause_order, DATABASE_DRIVER == DRIVER_ORACLE9
887
                                                    ? "to_char(value{$column['column_id']}) desc"
888
                                                    : "value{$column['column_id']} desc");
889
                }
890
891
                break;
892
893 View Code Duplication
            case COLUMN_TYPE_LIST_STRING:
894
895
                array_push($clause_select, "v{$column['column_id']}.value{$column['column_id']}");
896
897
                array_push($clause_join,
898
                           "left outer join " .
899
                           "(select r.record_id, lv.str_value as value{$column['column_id']} " .
900
                           "from tbl_records r, tbl_states s, tbl_fields f, tbl_events e, tbl_field_values fv " .
901
                           "left outer join tbl_list_values lv on fv.field_id = lv.field_id and fv.value_id = lv.int_value " .
902
                           "where r.record_id = e.record_id and s.state_id = f.state_id and s.state_name = '{$column['state_name']}' and f.field_id = fv.field_id and f.field_name = '{$column['field_name']}' and f.field_type = " . FIELD_TYPE_LIST . " and e.event_id = fv.event_id and fv.is_latest = 1 and ({$sql_field_perms})) v{$column['column_id']} " .
903
                           "on r.record_id = v{$column['column_id']}.record_id");
904
905
                if ($i == $sort)
906
                {
907
                    array_push($clause_order, "value{$column['column_id']} asc");
908
                }
909
                elseif (-$i == $sort)
910
                {
911
                    array_push($clause_order, "value{$column['column_id']} desc");
912
                }
913
914
                break;
915
916
            case COLUMN_TYPE_NUMBER:
917
            case COLUMN_TYPE_CHECKBOX:
918
            case COLUMN_TYPE_LIST_NUMBER:
919
            case COLUMN_TYPE_RECORD:
920
            case COLUMN_TYPE_DATE:
921
            case COLUMN_TYPE_DURATION:
922
923
                $types = array
924
                (
925
                    COLUMN_TYPE_NUMBER      => FIELD_TYPE_NUMBER,
926
                    COLUMN_TYPE_CHECKBOX    => FIELD_TYPE_CHECKBOX,
927
                    COLUMN_TYPE_LIST_NUMBER => FIELD_TYPE_LIST,
928
                    COLUMN_TYPE_RECORD      => FIELD_TYPE_RECORD,
929
                    COLUMN_TYPE_DATE        => FIELD_TYPE_DATE,
930
                    COLUMN_TYPE_DURATION    => FIELD_TYPE_DURATION,
931
                );
932
933
                array_push($clause_select, "v{$column['column_id']}.value{$column['column_id']}");
934
935
                array_push($clause_join,
936
                           "left outer join " .
937
                           "(select r.record_id, fv.value_id as value{$column['column_id']} " .
938
                           "from tbl_records r, tbl_states s, tbl_fields f, tbl_events e, tbl_field_values fv " .
939
                           "where r.record_id = e.record_id and s.state_id = f.state_id and s.state_name = '{$column['state_name']}' and f.field_id = fv.field_id and f.field_name = '{$column['field_name']}' and f.field_type = {$types[$column['column_type']]} and e.event_id = fv.event_id and fv.is_latest = 1 and ({$sql_field_perms})) v{$column['column_id']} " .
940
                           "on r.record_id = v{$column['column_id']}.record_id");
941
942
                if ($i == $sort)
943
                {
944
                    array_push($clause_order, "value{$column['column_id']} asc");
945
                }
946
                elseif (-$i == $sort)
947
                {
948
                    array_push($clause_order, "value{$column['column_id']} desc");
949
                }
950
951
                break;
952
953
            default:
954
                debug_write_log(DEBUG_WARNING, '[records_list] Unknown column type = ' . $column['column_type']);
955
        }
956
    }
957
958
    // Add default sorting.
959
960
    if ($sort < 0)
961
    {
962
        array_push($clause_order, 'record_id desc');
963
    }
964
    else
965
    {
966
        array_push($clause_order, 'record_id asc');
967
    }
968
969
    // Bring it all together.
970
971
    array_push($clause_from, "({$sql}) r");
972
973
    $sql = 'select '    . implode(', ',    array_unique($clause_select)) .
974
           ' from '     . implode(', ',    array_unique($clause_from))   .
975
           ' '          . implode(' ',     array_unique($clause_join));
976
977
    if (count($clause_where) != 0)
978
    {
979
        $sql .= ' where ' . implode(' and ', array_unique($clause_where));
980
    }
981
982
    $sql .= ' order by ' . implode(', ', array_unique($clause_order));
983
984
    return new CRecordset($sql);
985
}
986
987
/**
988
 * Returns {@link CRecordset DAL recordset} which contains number of created records per week for specified project.
989
 * Each row of returned recordset contains two fields:
990
 * <ul>
991
 * <li><b>week</b> - number of week after {@link http://en.wikipedia.org/wiki/Unix_time Unix Epoch}.</li>
992
 * <li><b>amount</b> - number of records, created during this week</li>
993
 * </ul>
994
 *
995
 * @param int $id ID of project which records should be counted.
996
 * @return CRecordset Recordset with list of counts.
997
 */
998 View Code Duplication
function record_opened ($id)
999
{
1000
    debug_write_log(DEBUG_TRACE, '[record_opened]');
1001
    debug_write_log(DEBUG_DUMP,  '[record_opened] $id = ' . $id);
1002
1003
    return dal_query('records/opened.sql', $id, date('Z'), (DATABASE_DRIVER == DRIVER_ORACLE9 ? 'ceil' : 'ceiling'));
1004
}
1005
1006
/**
1007
 * Returns {@link CRecordset DAL recordset} which contains number of closed records per week for specified project.
1008
 * Each row of returned recordset contains two fields:
1009
 * <ul>
1010
 * <li><b>week</b> - number of week after {@link http://en.wikipedia.org/wiki/Unix_time Unix Epoch}.</li>
1011
 * <li><b>amount</b> - number of records, closed during this week</li>
1012
 * </ul>
1013
 *
1014
 * @param int $id ID of project which records should be counted.
1015
 * @return CRecordset Recordset with list of counts.
1016
 */
1017 View Code Duplication
function record_closed ($id)
1018
{
1019
    debug_write_log(DEBUG_TRACE, '[record_closed]');
1020
    debug_write_log(DEBUG_DUMP,  '[record_closed] $id = ' . $id);
1021
1022
    return dal_query('records/closed.sql', $id, date('Z'), (DATABASE_DRIVER == DRIVER_ORACLE9 ? 'ceil' : 'ceiling'));
1023
}
1024
1025
/**
1026
 * Validates record information (including all custom fields) before creation, modification, or changing state.
1027
 *
1028
 * @param int $operation Code of operation:
1029
 * <ul>
1030
 * <li>{@link OPERATION_CREATE_RECORD} - new record is going to be created</li>
1031
 * <li>{@link OPERATION_MODIFY_RECORD} - record is going to be modified</li>
1032
 * <li>{@link OPERATION_CHANGE_STATE} - state of record is going to be changed</li>
1033
 * </ul>
1034
 * @param string $subject Subject of the record (ignored on state change).
1035
 * @param int $record_id Record ID (should be NULL on creation).
1036
 * @param int $state_id ID of new state (current on modification).
1037
 * @param int $creator_id Author of record (used only on modification, otherwise ignored).
1038
 * @param int $responsible_id Responsible of record (used only on modification, otherwise ignored).
1039
 * @return int Error code:
1040
 * <ul>
1041
 * <li>{@link NO_ERROR} - data are valid</li>
1042
 * <li>{@link ERROR_INCOMPLETE_FORM} - at least one of required field is empty</li>
1043
 * <li>{@link ERROR_INVALID_INTEGER_VALUE} - value of some custom field of {@link FIELD_TYPE_NUMBER}, {@link FIELD_TYPE_LIST}, or {@link FIELD_TYPE_RECORD} type is not an integer</li>
1044
 * <li>{@link ERROR_INTEGER_VALUE_OUT_OF_RANGE} - value of some custom field of {@link FIELD_TYPE_NUMBER} or {@link FIELD_TYPE_LIST} type is out of valid range</li>
1045
 * <li>{@link ERROR_VALUE_FAILS_REGEX_CHECK} - value of some custom field of {@link FIELD_TYPE_STRING} or {@link FIELD_TYPE_MULTILINED} type fails the custom PCRE check</li>
1046
 * <li>{@link ERROR_RECORD_NOT_FOUND} - value of some custom field of {@link FIELD_TYPE_RECORD} type is not an ID of existing record</li>
1047
 * <li>{@link ERROR_INVALID_DATE_VALUE} - value of some custom field of {@link FIELD_TYPE_DATE} type is not a valid date value</li>
1048
 * <li>{@link ERROR_DATE_VALUE_OUT_OF_RANGE} - value of some custom field of {@link FIELD_TYPE_DATE} type is out of valid range</li>
1049
 * <li>{@link ERROR_INVALID_TIME_VALUE} - value of some custom field of {@link FIELD_TYPE_DURATION} type is not a valid duration value</li>
1050
 * <li>{@link ERROR_TIME_VALUE_OUT_OF_RANGE} - value of some custom field of {@link FIELD_TYPE_DURATION} type is out of valid range</li>
1051
 * </ul>
1052
 */
1053
function record_validate ($operation, $subject, $record_id, $state_id, $creator_id = 0, $responsible_id = 0)
1054
{
1055
    debug_write_log(DEBUG_TRACE, '[record_validate]');
1056
    debug_write_log(DEBUG_DUMP,  '[record_validate] $operation      = ' . $operation);
1057
    debug_write_log(DEBUG_DUMP,  '[record_validate] $subject        = ' . $subject);
1058
    debug_write_log(DEBUG_DUMP,  '[record_validate] $record_id      = ' . $record_id);
1059
    debug_write_log(DEBUG_DUMP,  '[record_validate] $state_id       = ' . $state_id);
1060
    debug_write_log(DEBUG_DUMP,  '[record_validate] $creator_id     = ' . $creator_id);
1061
    debug_write_log(DEBUG_DUMP,  '[record_validate] $responsible_id = ' . $responsible_id);
1062
1063
    // Check the subject.
1064
    if ($operation != OPERATION_CHANGE_STATE &&
1065
        ustrlen($subject) == 0)
1066
    {
1067
        debug_write_log(DEBUG_NOTICE, '[record_validate] At least one required field is empty.');
1068
        return ERROR_INCOMPLETE_FORM;
1069
    }
1070
1071
    // Get the list of custom fields.
1072
    if ($operation != OPERATION_MODIFY_RECORD)
1073
    {
1074
        $rs = dal_query('fields/list.sql', $state_id, 'field_order');
1075
    }
1076
    else
1077
    {
1078
        $rs = dal_query('records/flist.sql',
1079
                        $record_id,
1080
                        $state_id,
1081
                        $creator_id,
1082
                        is_null($responsible_id) ? 0 : $responsible_id,
1083
                        $_SESSION[VAR_USERID],
1084
                        FIELD_ALLOW_TO_WRITE);
1085
    }
1086
1087
    // Check value candidates of all fields.
1088
    // Values of all custom fields are passed to the function via $_REQUEST.
1089
    while (($row = $rs->fetch()))
1090
    {
1091
        $name  = 'field' . $row['field_id'];
1092
        $value = ($row['field_type'] == FIELD_TYPE_CHECKBOX ? isset($_REQUEST[$name]) : trim(try_request($name)));
1093
1094
        // Required custom fields must be filled in.
1095
        if ($row['is_required'] &&
1096
            $row['field_type'] != FIELD_TYPE_CHECKBOX &&
1097
            ustrlen($value) == 0)
1098
        {
1099
            debug_write_log(DEBUG_NOTICE, '[record_validate] At least one required field is empty.');
1100
            return ERROR_INCOMPLETE_FORM;
1101
        }
1102
1103
        // Check value candidate of this field in correspondence with its type and template configuration.
1104
        switch ($row['field_type'])
1105
        {
1106
            case FIELD_TYPE_NUMBER:
1107
1108 View Code Duplication
                if (ustrlen($value) != 0)
1109
                {
1110
                    if (!is_intvalue($value))
1111
                    {
1112
                        debug_write_log(DEBUG_NOTICE, '[record_validate] Invalid integer value.');
1113
                        return ERROR_INVALID_INTEGER_VALUE;
1114
                    }
1115
1116
                    $intvalue = intval($value);
1117
1118
                    if ($intvalue < $row['param1'] ||
1119
                        $intvalue > $row['param2'])
1120
                    {
1121
                        $_SESSION['FIELD_NAME']        = $row['field_name'];
1122
                        $_SESSION['MIN_FIELD_INTEGER'] = $row['param1'];
1123
                        $_SESSION['MAX_FIELD_INTEGER'] = $row['param2'];
1124
                        debug_write_log(DEBUG_NOTICE, '[record_validate] Integer value is out of range.');
1125
                        return ERROR_INTEGER_VALUE_OUT_OF_RANGE;
1126
                    }
1127
                }
1128
1129
                break;
1130
1131
            case FIELD_TYPE_FLOAT:
1132
1133
                if (ustrlen($value) != 0)
1134
                {
1135
                    if (!is_floatvalue($value))
1136
                    {
1137
                        debug_write_log(DEBUG_NOTICE, '[record_validate] Invalid float value.');
1138
                        return ERROR_INVALID_FLOAT_VALUE;
1139
                    }
1140
1141
                    $minFieldFloat = value_find(FIELD_TYPE_FLOAT, $row['param1']);
1142
                    $maxFieldFloat = value_find(FIELD_TYPE_FLOAT, $row['param2']);
1143
1144
                    if (bccomp($value, $minFieldFloat) < 0 || bccomp($value, $maxFieldFloat) > 0)
1145
                    {
1146
                        $_SESSION['FIELD_NAME']        = $row['field_name'];
1147
                        $_SESSION['MIN_FIELD_INTEGER'] = $minFieldFloat;
1148
                        $_SESSION['MAX_FIELD_INTEGER'] = $maxFieldFloat;
1149
                        debug_write_log(DEBUG_NOTICE, '[record_validate] Float value is out of range.');
1150
                        return ERROR_FLOAT_VALUE_OUT_OF_RANGE;
1151
                    }
1152
                }
1153
1154
                break;
1155
1156
            case FIELD_TYPE_STRING:
1157
            case FIELD_TYPE_MULTILINED:
1158
1159
                $value = ustrcut($value, $row['param1']);
1160
1161
                // if regexp to check is set - check if value matches it
1162
                if (!is_null($row['regex_check']) && ustrlen($value) != 0)
1163
                {
1164
                    if (preg_match("/{$row['regex_check']}/", $value) == 0)
1165
                    {
1166
                        debug_write_log(DEBUG_NOTICE, '[record_validate] Field value fails regex check.');
1167
1168
                        $_SESSION['FIELD_NAME']  = $row['field_name'];
1169
                        $_SESSION['FIELD_VALUE'] = $value;
1170
1171
                        return ERROR_VALUE_FAILS_REGEX_CHECK;
1172
                    }
1173
                }
1174
1175
                break;
1176
1177
            case FIELD_TYPE_CHECKBOX:
1178
                break;  // nop
1179
1180
            case FIELD_TYPE_LIST:
1181
1182
                if (ustrlen($value) != 0)
1183
                {
1184
                    if (!is_intvalue($value))
1185
                    {
1186
                        debug_write_log(DEBUG_NOTICE, '[record_validate] Invalid list value.');
1187
                        return ERROR_INVALID_INTEGER_VALUE;
1188
                    }
1189
1190
                    $intvalue = intval($value);
1191
1192
                    if ($intvalue < 1 ||
1193
                        $intvalue > MAXINT)
1194
                    {
1195
                        $_SESSION['FIELD_NAME']        = $row['field_name'];
1196
                        $_SESSION['MIN_FIELD_INTEGER'] = 1;
1197
                        $_SESSION['MAX_FIELD_INTEGER'] = MAXINT;
1198
                        debug_write_log(DEBUG_NOTICE, '[record_validate] List value is out of range.');
1199
                        return ERROR_INTEGER_VALUE_OUT_OF_RANGE;
1200
                    }
1201
1202
                    $rsl = dal_query('values/lvfndk1.sql', $row['field_id'], $value);
1203
1204
                    if ($rsl->rows == 0)
0 ignored issues
show
The property $rows is declared protected in CRecordset. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1205
                    {
1206
                        debug_write_log(DEBUG_NOTICE, '[record_validate] List value not found.');
1207
                        return ERROR_INVALID_INTEGER_VALUE;
1208
                    }
1209
                }
1210
1211
                break;
1212
1213
            case FIELD_TYPE_RECORD:
1214
1215
                if (ustrlen($value) != 0)
1216
                {
1217
                    if (!is_intvalue($value))
1218
                    {
1219
                        debug_write_log(DEBUG_NOTICE, '[record_validate] Invalid record ID.');
1220
                        return ERROR_INVALID_INTEGER_VALUE;
1221
                    }
1222
1223
                    if (intval($value) == $record_id)
1224
                    {
1225
                        debug_write_log(DEBUG_NOTICE, '[record_validate] The same record ID cannot be specified.');
1226
                        return ERROR_RECORD_NOT_FOUND;
1227
                    }
1228
1229
                    $record = record_find(intval($value));
1230
1231
                    if (!$record)
1232
                    {
1233
                        debug_write_log(DEBUG_NOTICE, '[record_validate] Record not found.');
1234
                        return ERROR_RECORD_NOT_FOUND;
1235
                    }
1236
                }
1237
1238
                break;
1239
1240
            case FIELD_TYPE_DATE:
1241
1242
                if (ustrlen($value) != 0)
1243
                {
1244
                    $today = date_floor($operation == OPERATION_MODIFY_RECORD ? $row['event_time'] : time());
1245
1246
                    $row['param1'] = date_offset($today, $row['param1']);
1247
                    $row['param2'] = date_offset($today, $row['param2']);
1248
1249
                    $date = ustr2date($value);
1250
1251
                    if ($date == -1)
1252
                    {
1253
                        debug_write_log(DEBUG_NOTICE, '[record_validate] Invalid date value.');
1254
                        return ERROR_INVALID_DATE_VALUE;
1255
                    }
1256
1257
                    $date += $_SESSION[VAR_TIMEZONE] - intval(date('Z'));
1258
1259
                    if ($date < $row['param1'] ||
1260
                        $date > $row['param2'])
1261
                    {
1262
                        debug_write_log(DEBUG_NOTICE, '[record_validate] Date value is out of range.');
1263
1264
                        $_SESSION['FIELD_NAME']        = $row['field_name'];
1265
                        $_SESSION['MIN_FIELD_INTEGER'] = get_date($row['param1']);
1266
                        $_SESSION['MAX_FIELD_INTEGER'] = get_date($row['param2']);
1267
1268
                        return ERROR_DATE_VALUE_OUT_OF_RANGE;
1269
                    }
1270
                }
1271
1272
                break;
1273
1274
            case FIELD_TYPE_DURATION:
1275
1276 View Code Duplication
                if (ustrlen($value) != 0)
1277
                {
1278
                    $duration = ustr2time($value);
1279
1280
                    if ($duration == -1)
1281
                    {
1282
                        debug_write_log(DEBUG_NOTICE, '[record_validate] Invalid duration value.');
1283
                        return ERROR_INVALID_TIME_VALUE;
1284
                    }
1285
1286
                    if ($duration < $row['param1'] ||
1287
                        $duration > $row['param2'])
1288
                    {
1289
                        $_SESSION['FIELD_NAME']        = $row['field_name'];
1290
                        $_SESSION['MIN_FIELD_INTEGER'] = time2ustr($row['param1']);
1291
                        $_SESSION['MAX_FIELD_INTEGER'] = time2ustr($row['param2']);
1292
                        debug_write_log(DEBUG_NOTICE, '[record_validate] Duration value is out of range.');
1293
                        return ERROR_TIME_VALUE_OUT_OF_RANGE;
1294
                    }
1295
                }
1296
1297
                break;
1298
1299
            default:
1300
                debug_write_log(DEBUG_WARNING, '[record_validate] Unknown field type = ' . $row['field_type']);
1301
        }
1302
    }
1303
1304
    return NO_ERROR;
1305
}
1306
1307
/**
1308
 * Creates new record.
1309
 *
1310
 * @param int &$id ID of newly created record (used as output only).
1311
 * @param string $subject Subject of new record.
1312
 * @param int $state_id ID of initial state of new record.
1313
 * @param int $responsible_id If record should be assigned on creation, then ID of responsible of new record; NULL (default) otherwise.
1314
 * @param int $clone_id If record is being cloned from another, ID of original record, 0 (default) otherwise.
1315
 * @return int Error code:
1316
 * <ul>
1317
 * <li>{@link NO_ERROR} - record is successfully created</li>
1318
 * <li>{@link ERROR_NOT_FOUND} - failure on attempt to create new record</li>
1319
 * </ul>
1320
 */
1321
function record_create (&$id, $subject, $state_id, $responsible_id = NULL, $clone_id = 0)
1322
{
1323
    debug_write_log(DEBUG_TRACE, '[record_create]');
1324
    debug_write_log(DEBUG_DUMP,  '[record_create] $subject        = ' . $subject);
1325
    debug_write_log(DEBUG_DUMP,  '[record_create] $state_id       = ' . $state_id);
1326
    debug_write_log(DEBUG_DUMP,  '[record_create] $responsible_id = ' . $responsible_id);
1327
1328
    $time = time();
1329
1330
    // Create a record.
1331
    dal_query('records/create.sql',
1332
              $state_id,
1333
              $subject,
1334
              is_null($responsible_id) ? NULL : $responsible_id,
1335
              $_SESSION[VAR_USERID],
1336
              $time);
1337
1338
    // Find newly created record.
1339
    $rs = dal_query('records/fndk.sql',
1340
                    $_SESSION[VAR_USERID],
1341
                    $time);
1342
1343
    if ($rs->rows == 0)
0 ignored issues
show
The property $rows is declared protected in CRecordset. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1344
    {
1345
        debug_write_log(DEBUG_ERROR, '[record_create] Record cannot be found.');
1346
        return ERROR_NOT_FOUND;
1347
    }
1348
1349
    $id = $rs->fetch('record_id');
1350
1351
    if ($clone_id != 0)
1352
    {
1353
        debug_write_log(DEBUG_NOTICE, '[record_create] Record is being cloned.');
1354
        event_create($id, EVENT_RECORD_CLONED, $time - 1, $clone_id);
1355
    }
1356
1357
    $event = event_create($id, EVENT_RECORD_CREATED, $time, $state_id);
1358
1359
    if (!is_null($responsible_id))
1360
    {
1361
        debug_write_log(DEBUG_NOTICE, '[record_create] Responsible is being set.');
1362
        $event2 = event_create($id, EVENT_RECORD_ASSIGNED, time(), $responsible_id);
1363
    }
1364
1365
    // Create current values of all custom fields of new record.
1366
    $rs = dal_query('fields/list.sql', $state_id, 'field_order');
1367
1368
    while (($row = $rs->fetch()))
1369
    {
1370
        $name  = 'field' . $row['field_id'];
1371
        $value = ($row['field_type'] == FIELD_TYPE_CHECKBOX ? isset($_REQUEST[$name]) : trim(try_request($name)));
1372
1373 View Code Duplication
        switch ($row['field_type'])
1374
        {
1375
            case FIELD_TYPE_NUMBER:
1376
            case FIELD_TYPE_LIST:
1377
            case FIELD_TYPE_RECORD:
1378
                value_create_number($event['event_id'], $row['field_id'], $row['field_type'], (ustrlen($value) == 0 ? NULL : intval($value)));
1379
                break;
1380
            case FIELD_TYPE_FLOAT:
1381
                value_create_float($event['event_id'], $row['field_id'], $row['field_type'], (ustrlen($value) == 0 ? NULL : ustrcut($value, ustrlen(MAX_FIELD_FLOAT))));
1382
                break;
1383
            case FIELD_TYPE_STRING:
1384
                value_create_string($event['event_id'], $row['field_id'], $row['field_type'], (ustrlen($value) == 0 ? NULL : ustrcut($value, $row['param1'])));
1385
                break;
1386
            case FIELD_TYPE_MULTILINED:
1387
                value_create_multilined($event['event_id'], $row['field_id'], $row['field_type'], (ustrlen($value) == 0 ? NULL : ustrcut($value, $row['param1'])));
1388
                break;
1389
            case FIELD_TYPE_CHECKBOX:
1390
                value_create_number($event['event_id'], $row['field_id'], $row['field_type'], bool2sql((bool)(ustrlen($value) == 0 ? 0 : intval($value))));
1391
                break;
1392
            case FIELD_TYPE_DATE:
1393
                value_create_number($event['event_id'], $row['field_id'], $row['field_type'], (ustrlen($value) == 0 ? NULL : ustr2date($value)));
1394
                break;
1395
            case FIELD_TYPE_DURATION:
1396
                value_create_number($event['event_id'], $row['field_id'], $row['field_type'], (ustrlen($value) == 0 ? NULL : ustr2time($value)));
1397
                break;
1398
            default:
1399
                debug_write_log(DEBUG_WARNING, '[record_create] Unknown field type = ' . $row['field_type']);
1400
        }
1401
    }
1402
1403
    event_mail($event);
1404
1405
    if (!is_null($responsible_id))
1406
    {
1407
        debug_write_log(DEBUG_NOTICE, '[record_create] Responsible is set.');
1408
        event_mail($event2);
1409
    }
1410
1411
    return NO_ERROR;
1412
}
1413
1414
/**
1415
 * Modifies specified record.
1416
 *
1417
 * @param int $id ID of record to be modified.
1418
 * @param string $subject New subject of the record.
1419
 * @param int $creator_id Current author of record.
1420
 * @param int $responsible_id Current responsible of record (NULL, if record is not assigned).
1421
 * @return int Error code:
1422
 * <ul>
1423
 * <li>{@link NO_ERROR} - record is successfully modified</li>
1424
 * <li>{@link ERROR_NOT_FOUND} - record cannot be found</li>
1425
 * </ul>
1426
 */
1427
function record_modify ($id, $subject, $creator_id, $responsible_id)
1428
{
1429
    debug_write_log(DEBUG_TRACE, '[record_modify]');
1430
    debug_write_log(DEBUG_DUMP,  '[record_modify] $id             = ' . $id);
1431
    debug_write_log(DEBUG_DUMP,  '[record_modify] $subject        = ' . $subject);
1432
    debug_write_log(DEBUG_DUMP,  '[record_modify] $creator_id     = ' . $creator_id);
1433
    debug_write_log(DEBUG_DUMP,  '[record_modify] $responsible_id = ' . $responsible_id);
1434
1435
    $rs = dal_query('records/fndsubj.sql', $id);
1436
1437
    if ($rs->rows == 0)
0 ignored issues
show
The property $rows is declared protected in CRecordset. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1438
    {
1439
        debug_write_log(DEBUG_ERROR, '[record_modify] Record cannot be found.');
1440
        return ERROR_NOT_FOUND;
1441
    }
1442
1443
    $event = event_create($id, EVENT_RECORD_MODIFIED, time());
1444
1445
    $row = $rs->fetch();
1446
1447
    if ($row['subject'] != $subject)
1448
    {
1449
        debug_write_log(DEBUG_NOTICE, '[record_modify] Subject is being changed.');
1450
1451
        $old_value_id = value_find_string($row['subject']);
1452
        $new_value_id = value_find_string($subject);
1453
1454
        dal_query('changes/create.sql',
1455
                  $event['event_id'],
1456
                  NULL,
1457
                  is_null($old_value_id) ? NULL : $old_value_id,
1458
                  is_null($new_value_id) ? NULL : $new_value_id);
1459
1460
        dal_query('records/modify.sql',
1461
                  $id,
1462
                  $subject);
1463
    }
1464
1465
    $rs = dal_query('records/elist.sql', $id);
1466
1467
    while (($row = $rs->fetch()))
1468
    {
1469
        $rsf = dal_query('records/flist.sql',
1470
                         $id,
1471
                         $row['state_id'],
1472
                         $creator_id,
1473
                         is_null($responsible_id) ? 0 : $responsible_id,
1474
                         $_SESSION[VAR_USERID],
1475
                         FIELD_ALLOW_TO_WRITE);
1476
1477
        while (($row = $rsf->fetch()))
1478
        {
1479
            $name  = 'field' . $row['field_id'];
1480
            $value = ($row['field_type'] == FIELD_TYPE_CHECKBOX ? isset($_REQUEST[$name]) : trim(try_request($name)));
1481
1482
            switch ($row['field_type'])
1483
            {
1484
                case FIELD_TYPE_NUMBER:
1485
                case FIELD_TYPE_LIST:
1486
                case FIELD_TYPE_RECORD:
1487
                    value_modify_number($id, $event['event_id'], $row['field_id'], (ustrlen($value) == 0 ? NULL : intval($value)));
1488
                    break;
1489
                case FIELD_TYPE_FLOAT:
1490
                    value_modify_float($id, $event['event_id'], $row['field_id'], (ustrlen($value) == 0 ? NULL : ustrcut($value, ustrlen(MAX_FIELD_FLOAT))));
1491
                    break;
1492
                case FIELD_TYPE_STRING:
1493
                    value_modify_string($id, $event['event_id'], $row['field_id'], (ustrlen($value) == 0 ? NULL : ustrcut($value, $row['param1'])));
1494
                    break;
1495
                case FIELD_TYPE_MULTILINED:
1496
                    value_modify_multilined($id, $event['event_id'], $row['field_id'], (ustrlen($value) == 0 ? NULL : ustrcut($value, $row['param1'])));
1497
                    break;
1498
                case FIELD_TYPE_CHECKBOX:
1499
                    value_modify_number($id, $event['event_id'], $row['field_id'], bool2sql((bool)(ustrlen($value) == 0 ? 0 : intval($value))));
1500
                    break;
1501
                case FIELD_TYPE_DATE:
1502
                    value_modify_number($id, $event['event_id'], $row['field_id'], (ustrlen($value) == 0 ? NULL : ustr2date($value)));
1503
                    break;
1504
                case FIELD_TYPE_DURATION:
1505
                    value_modify_number($id, $event['event_id'], $row['field_id'], (ustrlen($value) == 0 ? NULL : ustr2time($value)));
1506
                    break;
1507
                default:
1508
                    debug_write_log(DEBUG_WARNING, '[record_modify] Unknown field type = ' . $row['field_type']);
1509
            }
1510
        }
1511
    }
1512
1513
    $rs = dal_query('changes/count.sql', $event['event_id']);
1514
1515
    if ($rs->fetch(0) == 0)
1516
    {
1517
        event_destroy($event['event_id']);
1518
    }
1519
    else
1520
    {
1521
        event_mail($event);
1522
    }
1523
1524
    return NO_ERROR;
1525
}
1526
1527
/**
1528
 * Deletes specified record.
1529
 *
1530
 * @param int $id ID of record to be deleted.
1531
 * @return int Always {@link NO_ERROR}.
1532
 */
1533
function record_delete ($id)
1534
{
1535
    debug_write_log(DEBUG_TRACE, '[record_delete]');
1536
    debug_write_log(DEBUG_DUMP,  '[record_delete] $id = ' . $id);
1537
1538
    $rs = dal_query('attachs/list.sql', $id, 'attachment_id');
1539
1540
    while (($row = $rs->fetch()))
1541
    {
1542
        @unlink(ATTACHMENTS_PATH . $row['attachment_id']);
1543
    }
1544
1545
    dal_query('comments/delall.sql',      $id);
1546
    dal_query('attachs/delall.sql',       $id);
1547
    dal_query('changes/delall.sql',       $id);
1548
    dal_query('values/delall.sql',        $id);
1549
    dal_query('events/delall.sql',        $id);
1550
    dal_query('depends/delall.sql',       $id);
1551
    dal_query('records/unreadall2.sql',   $id);
1552
    dal_query('records/unsubscribe3.sql', $id);
1553
    dal_query('records/delete.sql',       $id);
1554
1555
    return NO_ERROR;
1556
}
1557
1558
/**
1559
 * Postpones specified record.
1560
 *
1561
 * @param int $id ID of record to be postponed.
1562
 * @param int $date Unix timestamp of the date when record will be resumed automatically.
1563
 * @return int Always {@link NO_ERROR}.
1564
 */
1565
function record_postpone ($id, $date)
1566
{
1567
    debug_write_log(DEBUG_TRACE, '[record_postpone]');
1568
    debug_write_log(DEBUG_DUMP,  '[record_postpone] $id   = ' . $id);
1569
    debug_write_log(DEBUG_DUMP,  '[record_postpone] $date = ' . $date);
1570
1571
    dal_query('records/postpone.sql', $id, $date);
1572
1573
    return NO_ERROR;
1574
}
1575
1576
/**
1577
 * Resumes specified postponed record.
1578
 *
1579
 * @param int $id ID of record to be resumed.
1580
 * @return int Always {@link NO_ERROR}.
1581
 */
1582
function record_resume ($id)
1583
{
1584
    debug_write_log(DEBUG_TRACE, '[record_resume]');
1585
    debug_write_log(DEBUG_DUMP,  '[record_resume] $id = ' . $id);
1586
1587
    dal_query('records/postpone.sql', $id, 0);
1588
1589
    return NO_ERROR;
1590
}
1591
1592
/**
1593
 * Assigns specified record.
1594
 *
1595
 * @param int $rid ID of record to be assigned.
1596
 * @param int $aid ID of new responsible.
1597
 * @return int Always {@link NO_ERROR}.
1598
 */
1599
function record_assign ($rid, $aid)
1600
{
1601
    debug_write_log(DEBUG_TRACE, '[record_assign]');
1602
    debug_write_log(DEBUG_DUMP,  '[record_assign] $rid = ' . $rid);
1603
    debug_write_log(DEBUG_DUMP,  '[record_assign] $aid = ' . $aid);
1604
1605
    dal_query('records/assign.sql', $rid, $aid);
1606
1607
    return NO_ERROR;
1608
}
1609
1610
/**
1611
 * Marks specified record as read (for current user).
1612
 *
1613
 * @param int $id ID of record to be marked as read.
1614
 * @return int Always {@link NO_ERROR}.
1615
 */
1616
function record_read ($id)
1617
{
1618
    debug_write_log(DEBUG_TRACE, '[record_read]');
1619
    debug_write_log(DEBUG_DUMP,  '[record_read] $id = ' . $id);
1620
1621
    if (get_user_level() != USER_LEVEL_GUEST)
1622
    {
1623
        dal_query('records/unread.sql', $id, $_SESSION[VAR_USERID]);
1624
        dal_query('records/read.sql',   $id, $_SESSION[VAR_USERID], time());
1625
    }
1626
1627
    return NO_ERROR;
1628
}
1629
1630
/**
1631
 * Marks specified record as unread (for current user).
1632
 *
1633
 * @param int $id ID of record to be marked as unread.
1634
 * @return int Always {@link NO_ERROR}.
1635
 */
1636 View Code Duplication
function record_unread ($id)
1637
{
1638
    debug_write_log(DEBUG_TRACE, '[record_unread]');
1639
    debug_write_log(DEBUG_DUMP,  '[record_unread] $id = ' . $id);
1640
1641
    if (get_user_level() != USER_LEVEL_GUEST)
1642
    {
1643
        dal_query('records/unread.sql', $id, $_SESSION[VAR_USERID]);
1644
    }
1645
1646
    return NO_ERROR;
1647
}
1648
1649
/**
1650
 * Change state of specified record.
1651
 *
1652
 * @param int $id ID of record which state should be changed.
1653
 * @param int $state_id New state of the record.
1654
 * @param int $responsible_id ID of new responsible:
1655
 * <ul>
1656
 * <li>account ID, if the record should be assigned</li>
1657
 * <li>NULL, if the current assignment should be removed</li>
1658
 * <li>0, if current assignment should be remained as is</li>
1659
 * </ul>
1660
 * @param bool $close TRUE if new state is final; FALSE otherwise.
1661
 * @param bool $reopen TRUE if the record is closed and being reopened; FALSE otherwise.
1662
 * @return int Always {@link NO_ERROR}.
1663
 */
1664
function state_change ($id, $state_id, $responsible_id, $close = FALSE, $reopen = FALSE)
1665
{
1666
    debug_write_log(DEBUG_TRACE, '[state_change]');
1667
    debug_write_log(DEBUG_DUMP,  '[state_change] $id             = ' . $id);
1668
    debug_write_log(DEBUG_DUMP,  '[state_change] $state_id       = ' . $state_id);
1669
    debug_write_log(DEBUG_DUMP,  '[state_change] $responsible_id = ' . $responsible_id);
1670
    debug_write_log(DEBUG_DUMP,  '[state_change] $close          = ' . $close);
1671
    debug_write_log(DEBUG_DUMP,  '[state_change] $reopen         = ' . $reopen);
1672
1673
    dal_query('records/state.sql',
1674
              $id,
1675
              $state_id);
1676
1677
    $event = event_create($id,
1678
                          $reopen ? EVENT_RECORD_REOPENED : EVENT_RECORD_STATE_CHANGED,
1679
                          time(),
1680
                          $state_id);
1681
1682
    $rs = dal_query('fields/list.sql', $state_id, 'field_order');
1683
1684
    while (($row = $rs->fetch()))
1685
    {
1686
        $name  = 'field' . $row['field_id'];
1687
        $value = ($row['field_type'] == FIELD_TYPE_CHECKBOX ? isset($_REQUEST[$name]) : trim(try_request($name)));
1688
1689
        dal_query('values/latest.sql', $id, $row['field_id']);
1690
1691 View Code Duplication
        switch ($row['field_type'])
1692
        {
1693
            case FIELD_TYPE_NUMBER:
1694
            case FIELD_TYPE_LIST:
1695
            case FIELD_TYPE_RECORD:
1696
                value_create_number($event['event_id'], $row['field_id'], $row['field_type'], (ustrlen($value) == 0 ? NULL : intval($value)));
1697
                break;
1698
            case FIELD_TYPE_FLOAT:
1699
                value_create_float($event['event_id'], $row['field_id'], $row['field_type'], (ustrlen($value) == 0 ? NULL : ustrcut($value, ustrlen(MAX_FIELD_FLOAT))));
1700
                break;
1701
            case FIELD_TYPE_STRING:
1702
                value_create_string($event['event_id'], $row['field_id'], $row['field_type'], (ustrlen($value) == 0 ? NULL : ustrcut($value, $row['param1'])));
1703
                break;
1704
            case FIELD_TYPE_MULTILINED:
1705
                value_create_multilined($event['event_id'], $row['field_id'], $row['field_type'], (ustrlen($value) == 0 ? NULL : ustrcut($value, $row['param1'])));
1706
                break;
1707
            case FIELD_TYPE_CHECKBOX:
1708
                value_create_number($event['event_id'], $row['field_id'], $row['field_type'], bool2sql((bool)(ustrlen($value) == 0 ? 0 : intval($value))));
1709
                break;
1710
            case FIELD_TYPE_DATE:
1711
                value_create_number($event['event_id'], $row['field_id'], $row['field_type'], (ustrlen($value) == 0 ? NULL : ustr2date($value)));
1712
                break;
1713
            case FIELD_TYPE_DURATION:
1714
                value_create_number($event['event_id'], $row['field_id'], $row['field_type'], (ustrlen($value) == 0 ? NULL : ustr2time($value)));
1715
                break;
1716
            default:
1717
                debug_write_log(DEBUG_WARNING, '[state_change] Unknown field type = ' . $row['field_type']);
1718
        }
1719
    }
1720
1721
    event_mail($event);
1722
1723
    if ($close)
1724
    {
1725
        debug_write_log(DEBUG_NOTICE, '[state_change] Close record.');
1726
        dal_query('records/close.sql', $id, time());
1727
    }
1728
    else
1729
    {
1730
        if ($reopen)
1731
        {
1732
            debug_write_log(DEBUG_NOTICE, '[state_change] Reopen record.');
1733
            dal_query('records/open.sql', $id);
1734
        }
1735
1736
        if (is_null($responsible_id))
1737
        {
1738
            debug_write_log(DEBUG_NOTICE, '[state_change] Remove responsible.');
1739
            dal_query('records/assign.sql', $id, NULL);
1740
        }
1741
        elseif ($responsible_id != 0)
1742
        {
1743
            debug_write_log(DEBUG_NOTICE, '[state_change] Assign responsible.');
1744
            dal_query('records/assign.sql', $id, $responsible_id);
1745
            $event = event_create($id, EVENT_RECORD_ASSIGNED, time(), $responsible_id);
1746
            event_mail($event);
1747
        }
1748
    }
1749
1750
    return NO_ERROR;
1751
}
1752
1753
/**
1754
 * Returns {@link CRecordset DAL recordset} which contains all events of specified record,
1755
 * sorted in accordance with current sort mode.
1756
 *
1757
 * @param int $id Record ID.
1758
 * @param int $permissions User permissions (see also {@link record_get_permissions}).
1759
 * @param int &$sort Sort mode (used as output only). The function retrieves current sort mode from
1760
 * client cookie ({@link COOKIE_EVENTS_SORT}) and updates it, if it's out of valid range.
1761
 * @param int &$page Number of current page tab (used as output only). The function retrieves current
1762
 * page from client cookie ({@link COOKIE_EVENTS_PAGE}) and updates it, if it's out of valid range.
1763
 * @return CRecordset Recordset with list of events.
1764
 */
1765
function history_list ($id, $permissions, &$sort, &$page)
1766
{
1767
    debug_write_log(DEBUG_TRACE, '[history_list]');
1768
    debug_write_log(DEBUG_DUMP,  '[history_list] $id          = ' . $id);
1769
    debug_write_log(DEBUG_DUMP,  '[history_list] $permissions = ' . $permissions);
1770
1771
    $sort_modes = array
1772
    (
1773
        1 => 'event_time asc, event_type asc',
1774
        2 => 'fullname asc, username asc, event_time asc, event_type asc',
1775
        3 => 'event_type asc, event_time asc',
1776
        4 => 'event_time desc, event_type desc',
1777
        5 => 'fullname desc, username desc, event_time desc, event_type desc',
1778
        6 => 'event_type desc, event_time desc',
1779
    );
1780
1781
    $sort = try_request('sort', try_cookie(COOKIE_EVENTS_SORT));
1782
    $sort = ustr2int($sort, 1, count($sort_modes));
1783
1784
    $page = try_request('page', try_cookie(COOKIE_EVENTS_PAGE));
1785
    $page = ustr2int($page, 1, MAXINT);
1786
1787
    save_cookie(COOKIE_EVENTS_SORT, $sort);
1788
    save_cookie(COOKIE_EVENTS_PAGE, $page);
1789
1790
    return dal_query('events/list.sql', $id, ($permissions & PERMIT_CONFIDENTIAL_COMMENTS) ? EVENT_UNUSED : EVENT_CONFIDENTIAL_COMMENT, $sort_modes[$sort]);
1791
}
1792
1793
/**
1794
 * Returns {@link CRecordset DAL recordset} which contains all changes of specified record,
1795
 * sorted in accordance with current sort mode.
1796
 *
1797
 * @param int $id Record ID.
1798
 * @param int $creator_id Current author of the record.
1799
 * @param int $responsible_id Current responsible of the record (NULL, if record is not assigned).
1800
 * @param int &$sort Sort mode (used as output only). The function retrieves current sort mode from
1801
 * client cookie ({@link COOKIE_CHANGES_SORT}) and updates it, if it's out of valid range.
1802
 * @param int &$page Number of current page tab (used as output only). The function retrieves current
1803
 * page from client cookie ({@link COOKIE_CHANGES_PAGE}) and updates it, if it's out of valid range.
1804
 * @return CRecordset Recordset with list of changes.
1805
 */
1806
function changes_list ($id, $creator_id, $responsible_id, &$sort, &$page)
1807
{
1808
    debug_write_log(DEBUG_TRACE, '[changes_list]');
1809
    debug_write_log(DEBUG_DUMP,  '[changes_list] $id             = ' . $id);
1810
    debug_write_log(DEBUG_DUMP,  '[changes_list] $creator_id     = ' . $creator_id);
1811
    debug_write_log(DEBUG_DUMP,  '[changes_list] $responsible_id = ' . $responsible_id);
1812
1813
    $sort_modes = array
1814
    (
1815
        1  => 'event_time asc, field_order asc',
1816
        2  => 'fullname asc, username asc, event_time asc, field_order asc',
1817
        3  => 'field_name asc, event_time asc',
1818
        4  => '',
1819
        5  => '',
1820
        6  => 'event_time desc, field_order desc',
1821
        7  => 'fullname desc, username desc, event_time desc, field_order desc',
1822
        8  => 'field_name desc, event_time desc',
1823
        9  => '',
1824
        10 => '',
1825
    );
1826
1827
    $sort = try_request('sort', try_cookie(COOKIE_CHANGES_SORT));
1828
    $sort = ustr2int($sort, 1, count($sort_modes));
1829
1830
    $page = try_request('page', try_cookie(COOKIE_CHANGES_PAGE));
1831
    $page = ustr2int($page, 1, MAXINT);
1832
1833
    save_cookie(COOKIE_CHANGES_SORT, $sort);
1834
    save_cookie(COOKIE_CHANGES_PAGE, $page);
1835
1836
    return dal_query('changes/list.sql',
1837
                     $id,
1838
                     $creator_id,
1839
                     is_null($responsible_id) ? 0 : $responsible_id,
1840
                     $_SESSION[VAR_USERID],
1841
                     $sort_modes[$sort]);
1842
}
1843
1844
/**
1845
 * Finds in database and returns the information about specified comment.
1846
 *
1847
 * @param int $event_id ID of event, registered when comment has been added.
1848
 * @param int $permissions User permissions (see also {@link record_get_permissions}).
1849
 * @return array Array with data if comment is found in database, FALSE otherwise.
1850
 */
1851
function comment_find ($event_id, $permissions)
1852
{
1853
    debug_write_log(DEBUG_TRACE, '[comment_find]');
1854
    debug_write_log(DEBUG_DUMP,  '[comment_find] $event_id    = ' . $event_id);
1855
    debug_write_log(DEBUG_DUMP,  '[comment_find] $permissions = ' . $permissions);
1856
1857
    $rs  = dal_query('comments/fndk.sql', $event_id);
1858
    $row = $rs->fetch();
1859
1860
    return ($rs->rows == 0 ? FALSE : ($row['is_confidential'] && ($permissions & PERMIT_CONFIDENTIAL_COMMENTS) == 0 ? FALSE : $row));
0 ignored issues
show
The property $rows is declared protected in CRecordset. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1861
}
1862
1863
/**
1864
 * Adds new comment to specified record.
1865
 *
1866
 * @param int $id Record ID.
1867
 * @param string $comment Text of comment.
1868
 * @param bool $is_confidential Whether the comment is confidential.
1869
 * @return int Error code:
1870
 * <ul>
1871
 * <li>{@link NO_ERROR} - template is successfully created</li>
1872
 * <li>{@link ERROR_INCOMPLETE_FORM} - at least one of required field is empty</li>
1873
 * <li>{@link ERROR_NOT_FOUND} - record cannot be found</li>
1874
 * </ul>
1875
 */
1876
function comment_add ($id, $comment, $is_confidential = FALSE)
1877
{
1878
    debug_write_log(DEBUG_TRACE, '[comment_add]');
1879
    debug_write_log(DEBUG_DUMP,  '[comment_add] $id              = ' . $id);
1880
    debug_write_log(DEBUG_DUMP,  '[comment_add] $comment         = ' . $comment);
1881
    debug_write_log(DEBUG_DUMP,  '[comment_add] $is_confidential = ' . $is_confidential);
1882
1883
    if (ustrlen($comment) == 0)
1884
    {
1885
        debug_write_log(DEBUG_NOTICE, '[comment_add] At least one required field is empty.');
1886
        return ERROR_INCOMPLETE_FORM;
1887
    }
1888
1889
    $record = record_find($id);
1890
1891
    if (!$record)
1892
    {
1893
        debug_write_log(DEBUG_ERROR, '[comment_add] Record cannot be found.');
1894
        return ERROR_NOT_FOUND;
1895
    }
1896
1897
    $event = event_create($id, ($is_confidential ? EVENT_CONFIDENTIAL_COMMENT : EVENT_COMMENT_ADDED), time());
1898
1899
    if (!$event)
1900
    {
1901
        debug_write_log(DEBUG_ERROR, '[comment_add] Event cannot be found.');
1902
        return ERROR_NOT_FOUND;
1903
    }
1904
1905
    if (DATABASE_DRIVER == DRIVER_ORACLE9)
1906
    {
1907
        $handle = CDatabase::connect()->link->handle;
1908
        $sql = file_get_contents(LOCALROOT . '/sql/comments/oracle/create.sql');
1909
1910
        $stid = ociparse($handle, $sql);
1911
        $clob = ocinewdescriptor($handle, OCI_D_LOB);
1912
1913
        ocibindbyname($stid, ":event_id",        $event['event_id']);
1914
        ocibindbyname($stid, ":comment_body",    $clob, -1, OCI_B_CLOB);
1915
        ocibindbyname($stid, ":is_confidential", bool2sql($is_confidential));
1916
1917
        ociexecute($stid, OCI_DEFAULT);
1918
        $clob->save($comment);
1919
        ocicommit($handle);
1920
    }
1921
    else
1922
    {
1923
        dal_query('comments/create.sql',
1924
                  $comment,
1925
                  $event['event_id'],
1926
                  bool2sql($is_confidential));
1927
    }
1928
1929
    event_mail($event);
1930
1931
    return NO_ERROR;
1932
}
1933
1934
/**
1935
 * Finds in database and returns the information about specified attachment.
1936
 *
1937
 * @param int $attachment_id Attachment ID.
1938
 * @return array Array with data if attachment is found in database, FALSE otherwise.
1939
 */
1940
function attachment_find ($attachment_id)
1941
{
1942
    debug_write_log(DEBUG_TRACE, '[attachment_find]');
1943
    debug_write_log(DEBUG_DUMP,  '[attachment_find] $attachment_id = ' . $attachment_id);
1944
1945
    $rs = dal_query('attachs/fndid.sql', $attachment_id);
1946
1947
    return ($rs->rows == 0 ? FALSE : $rs->fetch());
0 ignored issues
show
The property $rows is declared protected in CRecordset. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1948
}
1949
1950
/**
1951
 * Returns {@link CRecordset DAL recordset} which contains all attachments of specified record,
1952
 * sorted by attachment name.
1953
 *
1954
 * @param int $id Record ID.
1955
 * @param int &$sort Sort mode (used as output only). The function retrieves current sort mode from
1956
 * client cookie ({@link COOKIE_ATTACHMENTS_SORT}) and updates it, if it's out of valid range.
1957
 * @param int &$page Number of current page tab (used as output only). The function retrieves current
1958
 * page from client cookie ({@link COOKIE_ATTACHMENTS_PAGE}) and updates it, if it's out of valid range.
1959
 * @return CRecordset Recordset with list of attachments.
1960
 */
1961
function attachments_list ($id, &$sort, &$page)
1962
{
1963
    debug_write_log(DEBUG_TRACE, '[attachments_list]');
1964
    debug_write_log(DEBUG_DUMP,  '[attachments_list] $id = ' . $id);
1965
1966
    $sort_modes = array
1967
    (
1968
        1 => 'attachment_name asc',
1969
        2 => 'attachment_size asc, attachment_name asc',
1970
        3 => 'fullname asc, username asc, attachment_name asc',
1971
        4 => 'event_time asc, attachment_name asc',
1972
        5 => 'attachment_name desc',
1973
        6 => 'attachment_size desc, attachment_name desc',
1974
        7 => 'fullname desc, username desc, attachment_name desc',
1975
        8 => 'event_time desc, attachment_name desc',
1976
    );
1977
1978
    $sort = try_request('sort', try_cookie(COOKIE_ATTACHMENTS_SORT));
1979
    $sort = ustr2int($sort, 1, count($sort_modes));
1980
1981
    $page = try_request('page', try_cookie(COOKIE_ATTACHMENTS_PAGE));
1982
    $page = ustr2int($page, 1, MAXINT);
1983
1984
    save_cookie(COOKIE_ATTACHMENTS_SORT, $sort);
1985
    save_cookie(COOKIE_ATTACHMENTS_PAGE, $page);
1986
1987
    return dal_query('attachs/list.sql', $id, $sort_modes[$sort]);
1988
}
1989
1990
/**
1991
 * Adds new attachment to specified record.
1992
 *
1993
 * @param int $id Record ID.
1994
 * @param string $name Attachment name.
1995
 * @param array $attachfile Information about uploaded user file (see {@link http://www.php.net/features.file-upload} for details).
1996
 * @return int Error code:
1997
 * <ul>
1998
 * <li>{@link NO_ERROR} - attachment is successfully created</li>
1999
 * <li>{@link ERROR_NOT_FOUND} - record cannot be found</li>
2000
 * <li>{@link ERROR_ALREADY_EXISTS} - attachment with specified name already exists</li>
2001
 * <li>{@link ERROR_UPLOAD_INI_SIZE} - the uploaded file exceeds the {@link http://www.php.net/ini.core#ini.upload-max-filesize upload_max_filesize} directive in <i>php.ini</i></li>
2002
 * <li>{@link ERROR_UPLOAD_FORM_SIZE} - the uploaded file exceeds the {@link EMAIL_ATTACHMENTS_MAXSIZE}</li>
2003
 * <li>{@link ERROR_UPLOAD_PARTIAL} - the uploaded file was only partially uploaded</li>
2004
 * <li>{@link ERROR_UPLOAD_NO_FILE} - no file was uploaded</li>
2005
 * <li>{@link ERROR_UPLOAD_NO_TMP_DIR} - missing a temporary folder</li>
2006
 * <li>{@link ERROR_UPLOAD_CANT_WRITE} - failed to write file to disk</li>
2007
 * <li>{@link ERROR_UPLOAD_EXTENSION} - file upload stopped by extension</li>
2008
 * <li>{@link ERROR_UNKNOWN} - unknown failure</li>
2009
 * </ul>
2010
 */
2011
function attachment_add ($id, $name, $attachfile)
2012
{
2013
    debug_write_log(DEBUG_TRACE, '[attachment_add]');
2014
    debug_write_log(DEBUG_DUMP,  '[attachment_add] $id                     = ' . $id);
2015
    debug_write_log(DEBUG_DUMP,  '[attachment_add] $name                   = ' . $name);
2016
    debug_write_log(DEBUG_DUMP,  '[attachment_add] $attachfile["name"]     = ' . $attachfile['name']);
2017
    debug_write_log(DEBUG_DUMP,  '[attachment_add] $attachfile["type"]     = ' . $attachfile['type']);
2018
    debug_write_log(DEBUG_DUMP,  '[attachment_add] $attachfile["size"]     = ' . $attachfile['size']);
2019
    debug_write_log(DEBUG_DUMP,  '[attachment_add] $attachfile["tmp_name"] = ' . $attachfile['tmp_name']);
2020
    debug_write_log(DEBUG_DUMP,  '[attachment_add] $attachfile["error"]    = ' . $attachfile['error']);
2021
2022
    if (ustrlen($name) == 0)
2023
    {
2024
        debug_write_log(DEBUG_NOTICE, '[attachment_add] Attachment name is not specified.');
2025
        $name = ustrcut($attachfile['name'], MAX_ATTACHMENT_NAME);
2026
    }
2027
    else
2028
    {
2029
        if (false !== ($pos = ustrpos($attachfile['name'], '.')))
2030
        {
2031
            $extension = usubstr($attachfile['name'], $pos);
2032
2033
            if ($extension != usubstr($name, -ustrlen($extension)))
2034
            {
2035
                $name = ustrcut($name, MAX_ATTACHMENT_NAME - ustrlen($extension));
2036
                $name .= $extension;
2037
            }
2038
        }
2039
    }
2040
2041
    $record = record_find($id);
2042
2043
    if (!$record)
2044
    {
2045
        debug_write_log(DEBUG_ERROR, '[attachment_add] Record cannot be found.');
2046
        return ERROR_NOT_FOUND;
2047
    }
2048
2049
    $rs = dal_query('attachs/fndku.sql', $id, $name);
2050
2051
    if ($rs->rows != 0)
0 ignored issues
show
The property $rows is declared protected in CRecordset. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2052
    {
2053
        debug_write_log(DEBUG_WARNING, '[attachment_add] Attachment already exists.');
2054
        return ERROR_ALREADY_EXISTS;
2055
    }
2056
2057
    switch ($attachfile['error'])
2058
    {
2059
        case UPLOAD_ERR_OK:
2060
            break;  // nop
2061
        case UPLOAD_ERR_INI_SIZE:
2062
            return ERROR_UPLOAD_INI_SIZE;
2063
        case UPLOAD_ERR_FORM_SIZE:
2064
            return ERROR_UPLOAD_FORM_SIZE;
2065
        case UPLOAD_ERR_PARTIAL:
2066
            return ERROR_UPLOAD_PARTIAL;
2067
        case UPLOAD_ERR_NO_FILE:
2068
            return ERROR_UPLOAD_NO_FILE;
2069
        case UPLOAD_ERR_NO_TMP_DIR:
2070
            return ERROR_UPLOAD_NO_TMP_DIR;
2071
        case UPLOAD_ERR_CANT_WRITE:
2072
            return ERROR_UPLOAD_CANT_WRITE;
2073
        case UPLOAD_ERR_EXTENSION:
2074
            return ERROR_UPLOAD_EXTENSION;
2075
        default:
2076
            return ERROR_UNKNOWN;
2077
    }
2078
2079
    if ($attachfile['size'] > ATTACHMENTS_MAXSIZE * 1024)
2080
    {
2081
        debug_write_log(DEBUG_WARNING, '[attachment_add] Attachment is too large.');
2082
        return ERROR_UPLOAD_FORM_SIZE;
2083
    }
2084
2085 View Code Duplication
    if (!is_uploaded_file($attachfile['tmp_name']))
2086
    {
2087
        debug_write_log(DEBUG_WARNING, '[attachment_add] Function "is_uploaded_file" warns that file named by "' . $attachfile['tmp_name'] . '" was not uploaded via HTTP POST.');
2088
        return NO_ERROR;
2089
    }
2090
2091
    if (ATTACHMENTS_TOTALSIZE != 0)
2092
    {
2093
        $rs = dal_query('attachs/total.sql');
2094
        $total_size = $rs->fetch(0);
2095
2096
        if ($total_size + $attachfile['size'] > ATTACHMENTS_TOTALSIZE * 1048576)
2097
        {
2098
            return ERROR_UPLOAD_CANT_WRITE;
2099
        }
2100
    }
2101
2102
    $event = event_create($id, EVENT_FILE_ATTACHED, time());
2103
2104
    if (!$event)
2105
    {
2106
        debug_write_log(DEBUG_ERROR, '[attachment_add] Event cannot be found.');
2107
        return ERROR_NOT_FOUND;
2108
    }
2109
2110
    dal_query('attachs/create.sql',
2111
              $name,
2112
              $attachfile['type'],
2113
              $attachfile['size'],
2114
              $event['event_id']);
2115
2116
    $rs = dal_query('attachs/fndku.sql', $id, $name);
2117
2118
    if ($rs->rows == 0)
0 ignored issues
show
The property $rows is declared protected in CRecordset. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2119
    {
2120
        debug_write_log(DEBUG_ERROR, '[attachment_add] Attachment cannot be found.');
2121
        return ERROR_NOT_FOUND;
2122
    }
2123
2124
    $attachment_id = $rs->fetch('attachment_id');
2125
2126
    $attachment_localname = ATTACHMENTS_PATH . $attachment_id;
2127
2128
    @move_uploaded_file($attachfile['tmp_name'], $attachment_localname);
2129
2130
    if (ATTACHMENTS_COMPRESSED)
2131
    {
2132
        compressfile($attachment_localname);
2133
    }
2134
2135
    event_mail($event, $attachment_id, $name, $attachfile['type'], $attachfile['size']);
2136
2137
    return NO_ERROR;
2138
}
2139
2140
/**
2141
 * Removes specified attachment from its record.
2142
 *
2143
 * @param int $id Record ID.
2144
 * @param int $permissions User permissions (see also {@link record_get_permissions}).
2145
 * @param int $attachment_id ID of attachment to be removed.
2146
 * @return int Error code:
2147
 * <ul>
2148
 * <li>{@link NO_ERROR} - attachment is successfully removed</li>
2149
 * <li>{@link ERROR_NOT_FOUND} - attachment cannot be found</li>
2150
 * </ul>
2151
 */
2152
function attachment_remove ($id, $permissions, $attachment_id)
2153
{
2154
    debug_write_log(DEBUG_TRACE, '[attachment_remove]');
2155
    debug_write_log(DEBUG_DUMP,  '[attachment_remove] $id            = ' . $id);
2156
    debug_write_log(DEBUG_DUMP,  '[attachment_remove] $permissions   = ' . $permissions);
2157
    debug_write_log(DEBUG_DUMP,  '[attachment_remove] $attachment_id = ' . $attachment_id);
2158
2159
    $attachment = attachment_find($attachment_id);
2160
2161
    if (!$attachment)
2162
    {
2163
        debug_write_log(DEBUG_NOTICE, '[attachment_remove] Attachment cannot be found.');
2164
        return ERROR_NOT_FOUND;
2165
    }
2166
2167
    if (($permissions & PERMIT_REMOVE_FILES) == 0)
2168
    {
2169
        debug_write_log(DEBUG_NOTICE, '[attachment_remove] No permissions to remove this attachment.');
2170
        return ERROR_NOT_FOUND;
2171
    }
2172
2173
    $event = event_create($id, EVENT_FILE_REMOVED, time(), $attachment_id);
2174
2175
    dal_query('attachs/remove.sql', $attachment_id);
2176
2177
    @unlink(ATTACHMENTS_PATH . $attachment_id);
2178
2179
    event_mail($event);
2180
2181
    return NO_ERROR;
2182
}
2183
2184
/**
2185
 * Returns {@link CRecordset DAL recordset} which contains all parent records of specified record, sorted by ID.
2186
 *
2187
 * @param int $id Record ID.
2188
 * @return CRecordset Recordset with list of parent records.
2189
 */
2190 View Code Duplication
function parents_list ($id)
2191
{
2192
    debug_write_log(DEBUG_TRACE, '[parents_list]');
2193
    debug_write_log(DEBUG_DUMP,  '[parents_list] $id = ' . $id);
2194
2195
    return dal_query('depends/parents.sql', $id);
2196
}
2197
2198
/**
2199
 * Returns {@link CRecordset DAL recordset} which contains all subrecords of specified record, sorted by ID.
2200
 *
2201
 * @param int $id Record ID.
2202
 * @return CRecordset Recordset with list of subrecords.
2203
 */
2204 View Code Duplication
function subrecords_list ($id)
2205
{
2206
    debug_write_log(DEBUG_TRACE, '[subrecords_list]');
2207
    debug_write_log(DEBUG_DUMP,  '[subrecords_list] $id = ' . $id);
2208
2209
    return dal_query('depends/list.sql', $id);
2210
}
2211
2212
/**
2213
 * Validates subrecord information before creation.
2214
 *
2215
 * @param int $parent_id Parent record ID.
2216
 * @param int $subrecord_id Subrecord ID.
2217
 * @return int Error code:
2218
 * <ul>
2219
 * <li>{@link NO_ERROR} - data are valid</li>
2220
 * <li>{@link ERROR_INCOMPLETE_FORM} - at least one of required field is empty</li>
2221
 * <li>{@link ERROR_INVALID_INTEGER_VALUE} - subrecord ID is not a valid integer value</li>
2222
 * <li>{@link ERROR_RECORD_NOT_FOUND} - subrecord cannot be found</li>
2223
 * </ul>
2224
 */
2225
function subrecord_validate ($parent_id, $subrecord_id)
2226
{
2227
    debug_write_log(DEBUG_TRACE, '[subrecord_validate]');
2228
    debug_write_log(DEBUG_DUMP,  '[subrecord_validate] $parent_id     = ' . $parent_id);
2229
    debug_write_log(DEBUG_DUMP,  '[subrecord_validate] $subrecord_id = ' . $subrecord_id);
2230
2231
    if (ustrlen($subrecord_id) == 0)
2232
    {
2233
        debug_write_log(DEBUG_NOTICE, '[subrecord_validate] At least one required field is empty.');
2234
        return ERROR_INCOMPLETE_FORM;
2235
    }
2236
2237
    if (!is_intvalue($subrecord_id))
2238
    {
2239
        debug_write_log(DEBUG_NOTICE, '[subrecord_validate] Invalid record ID.');
2240
        return ERROR_INVALID_INTEGER_VALUE;
2241
    }
2242
2243
    if ($parent_id == intval($subrecord_id))
2244
    {
2245
        debug_write_log(DEBUG_NOTICE, '[subrecord_validate] Record cannot be parent of itself.');
2246
        return ERROR_RECORD_NOT_FOUND;
2247
    }
2248
2249
    $record = record_find(intval($subrecord_id));
2250
2251
    if (!$record)
2252
    {
2253
        debug_write_log(DEBUG_NOTICE, '[subrecord_validate] Record not found.');
2254
        return ERROR_RECORD_NOT_FOUND;
2255
    }
2256
2257
    return NO_ERROR;
2258
}
2259
2260
/**
2261
 * Adds new subrecord to specified record.
2262
 *
2263
 * @param int $parent_id Parent record ID.
2264
 * @param int $subrecord_id Subrecord ID.
2265
 * @param bool $is_dependency .
2266
 * @return int Error code:
2267
 * <ul>
2268
 * <li>{@link NO_ERROR} - subrecord is successfully added</li>
2269
 * <li>{@link ERROR_NOT_FOUND} - subrecord cannot be found</li>
2270
 * <li>{@link ERROR_ALREADY_EXISTS} - specified subrecord already exists</li>
2271
 * </ul>
2272
 */
2273
function subrecord_add ($parent_id, $subrecord_id, $is_dependency)
2274
{
2275
    debug_write_log(DEBUG_TRACE, '[subrecord_add]');
2276
    debug_write_log(DEBUG_DUMP,  '[subrecord_add] $parent_id     = ' . $parent_id);
2277
    debug_write_log(DEBUG_DUMP,  '[subrecord_add] $subrecord_id  = ' . $subrecord_id);
2278
    debug_write_log(DEBUG_DUMP,  '[subrecord_add] $is_dependency = ' . $is_dependency);
2279
2280
    $rs = dal_query('depends/fnd2.sql', $parent_id, $subrecord_id);
2281
2282
    if ($rs->rows != 0)
0 ignored issues
show
The property $rows is declared protected in CRecordset. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2283
    {
2284
        debug_write_log(DEBUG_WARNING, '[subrecord_add] Subrecord already exists.');
2285
        return ERROR_ALREADY_EXISTS;
2286
    }
2287
2288
    $event = event_create($parent_id, EVENT_SUBRECORD_ADDED, time(), $subrecord_id);
2289
2290
    if (!$event)
2291
    {
2292
        debug_write_log(DEBUG_ERROR, '[subrecord_add] Event cannot be found.');
2293
        return ERROR_NOT_FOUND;
2294
    }
2295
2296
    dal_query('depends/create.sql',
2297
              $parent_id,
2298
              $subrecord_id,
2299
              bool2sql($is_dependency));
2300
2301
    event_mail($event);
2302
2303
    return NO_ERROR;
2304
}
2305
2306
/**
2307
 * Removes specified subrecord.
2308
 *
2309
 * @param int $parent_id Parent record ID.
2310
 * @param int $subrecord_id Subrecord ID.
2311
 * @return int Error code:
2312
 * <ul>
2313
 * <li>{@link NO_ERROR} - subrecord is successfully removed</li>
2314
 * <li>{@link ERROR_NOT_FOUND} - subrecord cannot be found</li>
2315
 * </ul>
2316
 */
2317
function subrecord_remove ($parent_id, $subrecord_id)
2318
{
2319
    debug_write_log(DEBUG_TRACE, '[subrecord_remove]');
2320
    debug_write_log(DEBUG_DUMP,  '[subrecord_remove] $parent_id    = ' . $parent_id);
2321
    debug_write_log(DEBUG_DUMP,  '[subrecord_remove] $subrecord_id = ' . $subrecord_id);
2322
2323
    $rs = dal_query('depends/fnd2.sql', $parent_id, $subrecord_id);
2324
2325
    if ($rs->rows == 0)
0 ignored issues
show
The property $rows is declared protected in CRecordset. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2326
    {
2327
        debug_write_log(DEBUG_WARNING, '[subrecord_remove] Subrecord cannot be found.');
2328
        return ERROR_NOT_FOUND;
2329
    }
2330
2331
    $event = event_create($parent_id, EVENT_SUBRECORD_REMOVED, time(), $subrecord_id);
2332
2333
    if (!$event)
2334
    {
2335
        debug_write_log(DEBUG_ERROR, '[subrecord_remove] Event cannot be found.');
2336
        return ERROR_NOT_FOUND;
2337
    }
2338
2339
    dal_query('depends/delete.sql',
2340
              $parent_id,
2341
              $subrecord_id);
2342
2343
    event_mail($event);
2344
2345
    return NO_ERROR;
2346
}
2347
2348
/**
2349
 * Determines and returns set of permissions of current user for some record.
2350
 *
2351
 * @param int $template_id ID of record's template.
2352
 * @param int $creator_id Author of record.
2353
 * @param int $responsible_id Responsible of record.
2354
 * @return int Set of binary flags:
2355
 * <ul>
2356
 * <li>{@link PERMIT_CREATE_RECORD} - permission to create new records</li>
2357
 * <li>{@link PERMIT_MODIFY_RECORD} - permission to modify records</li>
2358
 * <li>{@link PERMIT_POSTPONE_RECORD} - permission to postpone records</li>
2359
 * <li>{@link PERMIT_RESUME_RECORD} - permission to resume records</li>
2360
 * <li>{@link PERMIT_REASSIGN_RECORD} - permission to reassign records, which are already assigned on another person</li>
2361
 * <li>{@link PERMIT_REOPEN_RECORD} - permission to reopen records</li>
2362
 * <li>{@link PERMIT_ADD_COMMENTS} - permission to add comments</li>
2363
 * <li>{@link PERMIT_ATTACH_FILES} - permission to add attachments</li>
2364
 * <li>{@link PERMIT_REMOVE_FILES} - permission to remove attachments</li>
2365
 * <li>{@link PERMIT_CONFIDENTIAL_COMMENTS} - permission to add and read confidential comments</li>
2366
 * <li>{@link PERMIT_SEND_REMINDERS} - permission to send reminders</li>
2367
 * <li>{@link PERMIT_DELETE_RECORD} - permission to delete records from database</li>
2368
 * <li>{@link PERMIT_ADD_SUBRECORDS} - permission to add subrecords</li>
2369
 * <li>{@link PERMIT_REMOVE_SUBRECORDS} - permission to remove subrecords</li>
2370
 * <li>{@link PERMIT_VIEW_RECORD} - permission to read records</li>
2371
 * </ul>
2372
 */
2373
function record_get_permissions ($template_id, $creator_id, $responsible_id)
2374
{
2375
    debug_write_log(DEBUG_TRACE, '[record_get_permissions]');
2376
    debug_write_log(DEBUG_DUMP,  '[record_get_permissions] $template_id    = ' . $template_id);
2377
    debug_write_log(DEBUG_DUMP,  '[record_get_permissions] $creator_id     = ' . $creator_id);
2378
    debug_write_log(DEBUG_DUMP,  '[record_get_permissions] $responsible_id = ' . $responsible_id);
2379
2380
    $permissions = 0;
2381
2382
    if (get_user_level() == USER_LEVEL_GUEST)
2383
    {
2384
        $rs = dal_query('templates/fndid.sql', $template_id);
2385
2386
        if ($rs->rows != 0)
0 ignored issues
show
The property $rows is declared protected in CRecordset. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2387
        {
2388
            if ($rs->fetch('guest_access'))
2389
            {
2390
                $permissions = PERMIT_VIEW_RECORD;
2391
            }
2392
        }
2393
    }
2394
    else
2395
    {
2396
        if ($_SESSION[VAR_USERID] == $creator_id ||
2397
            $_SESSION[VAR_USERID] == $responsible_id)
2398
        {
2399
            $permissions = PERMIT_VIEW_RECORD;
2400
        }
2401
2402
        $rs = dal_query('groups/gplist.sql',
2403
                        $template_id,
2404
                        $creator_id,
2405
                        is_null($responsible_id) ? 0 : $responsible_id,
2406
                        $_SESSION[VAR_USERID]);
2407
2408
        while (($row = $rs->fetch()))
2409
        {
2410
            $permissions |= $row['perms'];
2411
        }
2412
    }
2413
2414
    return $permissions;
2415
}
2416
2417
/**
2418
 * Subscribes specified account to all events of specified record.
2419
 *
2420
 * @param int $record_id Record ID.
2421
 * @param int $account_id ID of account which is being subscribed.
2422
 * @param int $subscribed_by ID of account which is subscribing another one.
2423
 * @return int Always {@link NO_ERROR}.
2424
 */
2425
function record_subscribe ($record_id, $account_id, $subscribed_by)
2426
{
2427
    debug_write_log(DEBUG_TRACE, '[record_subscribe]');
2428
    debug_write_log(DEBUG_DUMP,  '[record_subscribe] $record_id     = ' . $record_id);
2429
    debug_write_log(DEBUG_DUMP,  '[record_subscribe] $account_id    = ' . $account_id);
2430
    debug_write_log(DEBUG_DUMP,  '[record_subscribe] $subscribed_by = ' . $subscribed_by);
2431
2432
    dal_query('records/unsubscribe.sql', $record_id, $account_id, $subscribed_by);
2433
    dal_query('records/subscribe.sql',   $record_id, $account_id, $subscribed_by);
2434
2435
    if ($account_id != $subscribed_by)
2436
    {
2437
        debug_write_log(DEBUG_NOTICE, '[record_subscribe] Inform about subscription.');
2438
2439
        $record     = record_find($record_id);
2440
        $account    = account_find($account_id);
2441
        $subscriber = account_find($subscribed_by);
2442
2443
        $to = $account['email'];
2444
2445
        $rec_id  = record_id($record_id, $record['template_prefix']);
2446
        $subject = "[{$record['project_name']}] {$rec_id}: "
2447
                 . htmlspecialchars_decode(update_references($record['subject'], BBCODE_OFF), ENT_COMPAT);
2448
2449
        $event = array('event_id'    => NULL,
2450
                       'event_type'  => EVENT_RECORD_SUBSCRIBED,
2451
                       'event_param' => $subscriber['fullname']);
2452
2453
        $message = generate_message($record, $event, $account['locale']);
2454
2455
        if (EMAIL_NOTIFICATIONS_ENABLED)
2456
        {
2457
            debug_write_log(DEBUG_NOTICE, '[record_subscribe] Sending email.');
2458
            sendmail($subscriber['fullname'], $subscriber['email'], $to, $subject, $message);
2459
        }
2460
        else
2461
        {
2462
            debug_write_log(DEBUG_NOTICE, '[record_subscribe] Email notifications are disabled.');
2463
        }
2464
    }
2465
2466
    return NO_ERROR;
2467
}
2468
2469
/**
2470
 * Unsubscribes specified account off specified record.
2471
 *
2472
 * @param int $record_id Record ID.
2473
 * @param int $account_id ID of account which is being unsubscribed.
2474
 * @param int $subscribed_by ID of account which is unsubscribing another one.
2475
 * @return int Always {@link NO_ERROR}.
2476
 */
2477
function record_unsubscribe ($record_id, $account_id, $subscribed_by)
2478
{
2479
    debug_write_log(DEBUG_TRACE, '[record_unsubscribe]');
2480
    debug_write_log(DEBUG_DUMP,  '[record_unsubscribe] $record_id     = ' . $record_id);
2481
    debug_write_log(DEBUG_DUMP,  '[record_unsubscribe] $account_id    = ' . $account_id);
2482
    debug_write_log(DEBUG_DUMP,  '[record_unsubscribe] $subscribed_by = ' . $subscribed_by);
2483
2484
    global $locale_info;
2485
2486
    if ($account_id == $subscribed_by)
2487
    {
2488
        debug_write_log(DEBUG_NOTICE, '[record_unsubscribe] Inform about unsubscription.');
2489
2490
        $record  = record_find($record_id);
2491
        $account = account_find($account_id);
2492
2493
        $supported_locales = array_keys($locale_info);
2494
2495
        foreach ($supported_locales as $locale)
2496
        {
2497
            $to = array();
2498
            $rs = dal_query('records/subscribers.sql', $record_id, $account_id, $locale);
2499
2500
            while (($row = $rs->fetch()))
2501
            {
2502
                array_push($to, $row['email']);
2503
            }
2504
2505
            if (count($to) != 0)
2506
            {
2507
                $recipients = implode(', ', array_unique($to));
2508
2509
                $rec_id  = record_id($record_id, $record['template_prefix']);
2510
                $subject = "[{$record['project_name']}] {$rec_id}: "
2511
                         . htmlspecialchars_decode(update_references($record['subject'], BBCODE_OFF), ENT_COMPAT);
2512
2513
                $event = array('event_id'    => NULL,
2514
                               'event_type'  => EVENT_RECORD_UNSUBSCRIBED,
2515
                               'event_param' => $account['fullname']);
2516
2517
                $message = generate_message($record, $event, $locale);
2518
2519
                if (EMAIL_NOTIFICATIONS_ENABLED)
2520
                {
2521
                    debug_write_log(DEBUG_NOTICE, '[record_unsubscribe] Sending email.');
2522
                    sendmail($account['fullname'], $account['email'], $recipients, $subject, $message);
2523
                }
2524
                else
2525
                {
2526
                    debug_write_log(DEBUG_NOTICE, '[record_unsubscribe] Email notifications are disabled.');
2527
                }
2528
            }
2529
        }
2530
2531
        dal_query('records/unsubscribe2.sql', $record_id, $account_id);
2532
    }
2533
    else
2534
    {
2535
        dal_query('records/unsubscribe.sql', $record_id, $account_id, $subscribed_by);
2536
    }
2537
2538
    return NO_ERROR;
2539
}
2540
2541
/**
2542
 * Checks whether specified account is subscribed to specified record.
2543
 *
2544
 * @param int $record_id Record ID.
2545
 * @param int $account_id Account ID.
2546
 * @return bool TRUE if account is subscribed, FALSE otherwise.
2547
 */
2548 View Code Duplication
function is_record_subscribed ($record_id, $account_id)
2549
{
2550
    debug_write_log(DEBUG_TRACE, '[is_record_subscribed]');
2551
    debug_write_log(DEBUG_DUMP,  '[is_record_subscribed] $record_id  = ' . $record_id);
2552
    debug_write_log(DEBUG_DUMP,  '[is_record_subscribed] $account_id = ' . $account_id);
2553
2554
    $rs = dal_query('records/fndsubsc.sql', $record_id, $account_id);
2555
2556
    return ($rs->rows != 0);
0 ignored issues
show
The property $rows is declared protected in CRecordset. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2557
}
2558
2559
/**
2560
 * Checks whether a specified record has reached critical age.
2561
 *
2562
 * @param array $record Record information, as it returned by {@link record_find}.
2563
 * @return bool TRUE if record's age is already critical, FALSE otherwise.
2564
 */
2565 View Code Duplication
function is_record_critical ($record)
2566
{
2567
    debug_write_log(DEBUG_TRACE, '[is_recorde_critical]');
2568
2569
    return (is_null($record['closure_time']) &&
2570
            !is_null($record['critical_age']) &&
2571
            $record['creation_time'] + $record['critical_age'] * SECS_IN_DAY < time());
2572
}
2573
2574
/**
2575
 * Checks whether a specified record is frozen.
2576
 *
2577
 * @param array $record Record information, as it returned by {@link record_find}.
2578
 * @return bool TRUE if record is already frozen, FALSE otherwise.
2579
 */
2580 View Code Duplication
function is_record_frozen ($record)
2581
{
2582
    debug_write_log(DEBUG_TRACE, '[is_record_frozen]');
2583
2584
    return (!is_null($record['closure_time']) &&
2585
            !is_null($record['frozen_time']) &&
2586
            $record['closure_time'] + $record['frozen_time'] * SECS_IN_DAY < time());
2587
}
2588
2589
/**
2590
 * Checks whether a specified record is postponed.
2591
 *
2592
 * @param array $record Record information, as it returned by {@link record_find}.
2593
 * @return bool TRUE if record is currently postponed, FALSE otherwise.
2594
 */
2595
function is_record_postponed ($record)
2596
{
2597
    debug_write_log(DEBUG_TRACE, '[is_record_postponed]');
2598
2599
    return (is_null($record['closure_time']) &&
2600
            $record['postpone_time'] > time());
2601
}
2602
2603
/**
2604
 * Checks whether a specified record is closed.
2605
 *
2606
 * @param array $record Record information, as it returned by {@link record_find}.
2607
 * @return bool TRUE if record is already closed, FALSE otherwise.
2608
 */
2609
function is_record_closed ($record)
2610
{
2611
    debug_write_log(DEBUG_TRACE, '[is_record_closed]');
2612
2613
    return !is_null($record['closure_time']);
2614
}
2615
2616
/**
2617
 * Checks whether a specified record was cloned from another one.
2618
 *
2619
 * @param int $id Record ID.
2620
 * @return int ID of original record if specified one was cloned from it, 0 otherwise.
2621
 */
2622 View Code Duplication
function is_record_cloned ($id)
2623
{
2624
    debug_write_log(DEBUG_TRACE, '[is_record_cloned]');
2625
    debug_write_log(DEBUG_DUMP,  '[is_record_cloned] $id = ' . $id);
2626
2627
    $rs = dal_query('events/fnd.sql',
2628
                    $id,
2629
                    EVENT_RECORD_CLONED);
2630
2631
    return ($rs->rows == 0 ? 0 : $rs->fetch('event_param'));
0 ignored issues
show
The property $rows is declared protected in CRecordset. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2632
}
2633
2634
/**
2635
 * Calculates number of days since the last event of specified record.
2636
 *
2637
 * @param array $record Record information, as it returned by {@link record_find}.
2638
 * @return int Number of days.
2639
 */
2640
function get_record_last_event ($record)
2641
{
2642
    debug_write_log(DEBUG_TRACE, '[get_record_last_event]');
2643
2644
    return ceil((time() - $record['change_time'] + 1) / SECS_IN_DAY);
2645
}
2646
2647
/**
2648
 * Calculates number of days since the last change of state of specified record.
2649
 *
2650
 * @param array $record Record information, as it returned by {@link records_list}.
2651
 * @return int Number of days.
2652
 */
2653
function get_record_last_state ($record)
2654
{
2655
    debug_write_log(DEBUG_TRACE, '[get_record_last_state]');
2656
2657
    return ceil((time() - $record['state_time'] + 1) / SECS_IN_DAY);
2658
}
2659
2660
/**
2661
 * Calculates age of specified record.
2662
 *
2663
 * @param array $record Record information, as it returned by {@link record_find}.
2664
 * @return int Age as number of days.
2665
 */
2666
function get_record_age ($record)
2667
{
2668
    debug_write_log(DEBUG_TRACE, '[get_record_age]');
2669
2670
    return (is_record_closed($record)
2671
        ? ceil($record['closed_age'] / SECS_IN_DAY)
2672
        : ceil(($record['opened_age'] + 1) / SECS_IN_DAY));
2673
}
2674
2675
/**
2676
 * Checks whether specified permissions allow to see a record.
2677
 *
2678
 * @param int $permissions User permissions (see also {@link record_get_permissions}).
2679
 * @return bool TRUE if record can be displayed, FALSE otherwise.
2680
 */
2681
function can_record_be_displayed ($permissions)
2682
{
2683
    debug_write_log(DEBUG_TRACE, '[can_record_be_displayed]');
2684
2685
    return ($permissions & PERMIT_VIEW_RECORD);
2686
}
2687
2688
/**
2689
 * Checks whether current user is allowed to create new record.
2690
 *
2691
 * @return bool TRUE if user is allowed to create new record, FALSE otherwise.
2692
 */
2693
function can_record_be_created ()
2694
{
2695
    debug_write_log(DEBUG_TRACE, '[can_record_be_created]');
2696
2697
    if (get_user_level() == USER_LEVEL_GUEST)
2698
    {
2699
        return FALSE;
2700
    }
2701
2702
    if (DATABASE_DRIVER == DRIVER_ORACLE9)
2703
    {
2704
        $rs = dal_query('records/oracle/plist.sql', $_SESSION[VAR_USERID]);
2705
    }
2706
    else
2707
    {
2708
        $rs = dal_query('records/plist.sql', $_SESSION[VAR_USERID]);
2709
    }
2710
2711
    return ($rs->rows != 0);
0 ignored issues
show
The property $rows is declared protected in CRecordset. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2712
}
2713
2714
/**
2715
 * Checks whether specified permissions allow to modify specified record.
2716
 *
2717
 * @param array $record Record information, as it returned by {@link record_find}.
2718
 * @param int $permissions User permissions (see also {@link record_get_permissions}).
2719
 * @return bool TRUE if record can be modified, FALSE otherwise.
2720
 */
2721
function can_record_be_modified ($record, $permissions)
2722
{
2723
    debug_write_log(DEBUG_TRACE, '[can_record_be_modified]');
2724
2725
    return (get_user_level() != USER_LEVEL_GUEST &&
2726
            !$record['is_suspended']             &&
2727
            !$record['is_locked']                &&
2728
            !is_record_postponed($record)        &&
2729
            !is_record_frozen($record)           &&
2730
            ($permissions & PERMIT_MODIFY_RECORD));
2731
}
2732
2733
/**
2734
 * Checks whether specified permissions allow to delete specified record.
2735
 *
2736
 * @param array $record Record information, as it returned by {@link record_find}.
2737
 * @param int $permissions User permissions (see also {@link record_get_permissions}).
2738
 * @return bool TRUE if record can be deleted, FALSE otherwise.
2739
 */
2740
function can_record_be_deleted ($record, $permissions)
2741
{
2742
    debug_write_log(DEBUG_TRACE, '[can_record_be_deleted]');
2743
2744
    return (get_user_level() != USER_LEVEL_GUEST &&
2745
            !$record['is_suspended']             &&
2746
            !$record['is_locked']                &&
2747
            !is_record_postponed($record)        &&
2748
            !is_record_frozen($record)           &&
2749
            ($permissions & PERMIT_DELETE_RECORD));
2750
}
2751
2752
/**
2753
 * Checks whether specified permissions allow to postpone specified record.
2754
 *
2755
 * @param array $record Record information, as it returned by {@link record_find}.
2756
 * @param int $permissions User permissions (see also {@link record_get_permissions}).
2757
 * @return bool TRUE if record can be postponed, FALSE otherwise.
2758
 */
2759 View Code Duplication
function can_record_be_postponed ($record, $permissions)
2760
{
2761
    debug_write_log(DEBUG_TRACE, '[can_record_be_postponed]');
2762
2763
    return (get_user_level() != USER_LEVEL_GUEST &&
2764
            !$record['is_suspended']             &&
2765
            !$record['is_locked']                &&
2766
            !is_record_postponed($record)        &&
2767
            is_null($record['closure_time'])     &&
2768
            ($permissions & PERMIT_POSTPONE_RECORD));
2769
}
2770
2771
/**
2772
 * Checks whether specified permissions allow to resume specified record.
2773
 *
2774
 * @param array $record Record information, as it returned by {@link record_find}.
2775
 * @param int $permissions User permissions (see also {@link record_get_permissions}).
2776
 * @return bool TRUE if record can be resumed, FALSE otherwise.
2777
 */
2778
function can_record_be_resumed ($record, $permissions)
2779
{
2780
    debug_write_log(DEBUG_TRACE, '[can_record_be_resumed]');
2781
2782
    return (get_user_level() != USER_LEVEL_GUEST &&
2783
            !$record['is_suspended']             &&
2784
            !$record['is_locked']                &&
2785
            is_record_postponed($record)         &&
2786
            is_null($record['closure_time'])     &&
2787
            ($permissions & PERMIT_RESUME_RECORD));
2788
}
2789
2790
/**
2791
 * Checks whether specified permissions allow to reassign specified record.
2792
 *
2793
 * @param array $record Record information, as it returned by {@link record_find}.
2794
 * @param int $permissions User permissions (see also {@link record_get_permissions}).
2795
 * @return bool TRUE if record can be reassigned, FALSE otherwise.
2796
 */
2797 View Code Duplication
function can_record_be_reassigned ($record, $permissions)
2798
{
2799
    debug_write_log(DEBUG_TRACE, '[can_record_be_reassigned]');
2800
2801
    return (get_user_level() != USER_LEVEL_GUEST &&
2802
            !$record['is_suspended']             &&
2803
            !$record['is_locked']                &&
2804
            !is_record_postponed($record)        &&
2805
            is_null($record['closure_time'])     &&
2806
            !is_null($record['responsible_id'])  &&
2807
            ($permissions & PERMIT_REASSIGN_RECORD));
2808
}
2809
2810
/**
2811
 * Checks whether specified permissions allow to reopen specified record.
2812
 *
2813
 * @param array $record Record information, as it returned by {@link record_find}.
2814
 * @param int $permissions User permissions (see also {@link record_get_permissions}).
2815
 * @return bool TRUE if record can be reopened, FALSE otherwise.
2816
 */
2817
function can_record_be_reopened ($record, $permissions)
2818
{
2819
    debug_write_log(DEBUG_TRACE, '[can_record_be_reopened]');
2820
2821
    return (get_user_level() != USER_LEVEL_GUEST &&
2822
            !$record['is_suspended']             &&
2823
            !$record['is_locked']                &&
2824
            !is_null($record['closure_time'])    &&
2825
            ($permissions & PERMIT_REOPEN_RECORD));
2826
}
2827
2828
/**
2829
 * Checks whether it's allowed to change state of specified record.
2830
 *
2831
 * @param array $record Record information, as it returned by {@link record_find}.
2832
 * @return bool TRUE if state of the record can be changed, FALSE otherwise.
2833
 */
2834
function can_state_be_changed ($record)
2835
{
2836
    debug_write_log(DEBUG_TRACE, '[can_state_be_changed]');
2837
2838
    return (get_user_level() != USER_LEVEL_GUEST &&
2839
            !$record['is_suspended']             &&
2840
            !$record['is_locked']                &&
2841
            !is_record_postponed($record)        &&
2842
            is_null($record['closure_time']));
2843
}
2844
2845
/**
2846
 * Checks whether specified permissions allow to post a comment in specified record.
2847
 *
2848
 * @param array $record Record information, as it returned by {@link record_find}.
2849
 * @param int $permissions User permissions (see also {@link record_get_permissions}).
2850
 * @return bool TRUE if comment can be posted, FALSE otherwise.
2851
 */
2852 View Code Duplication
function can_comment_be_added ($record, $permissions)
2853
{
2854
    debug_write_log(DEBUG_TRACE, '[can_comment_be_added]');
2855
2856
    return (get_user_level() != USER_LEVEL_GUEST &&
2857
            !$record['is_suspended']             &&
2858
            !$record['is_locked']                &&
2859
            !is_record_frozen($record)           &&
2860
            ($permissions & (PERMIT_ADD_COMMENTS | PERMIT_CONFIDENTIAL_COMMENTS)));
2861
}
2862
2863
/**
2864
 * Checks whether specified permissions allow to attach file to specified record.
2865
 *
2866
 * @param array $record Record information, as it returned by {@link record_find}.
2867
 * @param int $permissions User permissions (see also {@link record_get_permissions}).
2868
 * @return bool TRUE if file can be attached, FALSE otherwise.
2869
 */
2870 View Code Duplication
function can_file_be_attached ($record, $permissions)
2871
{
2872
    debug_write_log(DEBUG_TRACE, '[can_file_be_attached]');
2873
2874
    if (ATTACHMENTS_ENABLED == 0)
2875
    {
2876
        return FALSE;
2877
    }
2878
2879
    return (get_user_level() != USER_LEVEL_GUEST &&
2880
            !$record['is_suspended']             &&
2881
            !$record['is_locked']                &&
2882
            !is_record_frozen($record)           &&
2883
            ($permissions & PERMIT_ATTACH_FILES));
2884
}
2885
2886
/**
2887
 * Checks whether specified permissions allow to remove attached file from specified record.
2888
 *
2889
 * @param array $record Record information, as it returned by {@link record_find}.
2890
 * @param int $permissions User permissions (see also {@link record_get_permissions}).
2891
 * @return bool TRUE if attached file can be removed, FALSE otherwise.
2892
 */
2893 View Code Duplication
function can_file_be_removed ($record)
2894
{
2895
    debug_write_log(DEBUG_TRACE, '[can_file_be_removed]');
2896
2897
    if (ATTACHMENTS_ENABLED == 0)
2898
    {
2899
        return FALSE;
2900
    }
2901
2902
    return (get_user_level() != USER_LEVEL_GUEST &&
2903
            !$record['is_suspended']             &&
2904
            !$record['is_locked']                &&
2905
            !is_record_frozen($record));
2906
}
2907
2908
/**
2909
 * Checks whether specified permissions allow to add subrecord to specified record.
2910
 *
2911
 * @param array $record Record information, as it returned by {@link record_find}.
2912
 * @param int $permissions User permissions (see also {@link record_get_permissions}).
2913
 * @return bool TRUE if subrecord can be added, FALSE otherwise.
2914
 */
2915
function can_subrecord_be_added ($record, $permissions)
2916
{
2917
    debug_write_log(DEBUG_TRACE, '[can_subrecord_be_added]');
2918
2919
    return (get_user_level() != USER_LEVEL_GUEST &&
2920
            !$record['is_suspended']             &&
2921
            !$record['is_locked']                &&
2922
            !is_record_postponed($record)        &&
2923
            !is_record_frozen($record)           &&
2924
            ($permissions & PERMIT_ADD_SUBRECORDS));
2925
}
2926
2927
/**
2928
 * Checks whether specified permissions allow to remove subrecord from specified record.
2929
 *
2930
 * @param array $record Record information, as it returned by {@link record_find}.
2931
 * @param int $permissions User permissions (see also {@link record_get_permissions}).
2932
 * @return bool TRUE if subrecord can be removed, FALSE otherwise.
2933
 */
2934 View Code Duplication
function can_subrecord_be_removed ($record, $permissions)
2935
{
2936
    debug_write_log(DEBUG_TRACE, '[can_subrecord_be_removed]');
2937
2938
    $rs = dal_query('depends/list.sql', $record['record_id']);
2939
2940
    return (get_user_level() != USER_LEVEL_GUEST      &&
2941
            !$record['is_suspended']                  &&
2942
            !$record['is_locked']                     &&
2943
            !is_record_postponed($record)             &&
2944
            !is_record_frozen($record)                &&
2945
            ($permissions & PERMIT_REMOVE_SUBRECORDS) &&
2946
            ($rs->rows != 0));
0 ignored issues
show
The property $rows is declared protected in CRecordset. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2947
}
2948
2949
/**
2950
 * Update specified {@link FIELD_TYPE_STRING string} or {@link FIELD_TYPE_MULTILINED multilined} value.
2951
 *
2952
 * @param string $value {@link FIELD_TYPE_STRING String} or {@link FIELD_TYPE_MULTILINED multilined} value to be processed.
2953
 * @param int $bbcode_mode BBCode processing mode:
2954
 * <ul>
2955
 * <li>{@link BBCODE_OFF} - no BBCode processing, all tags are hidden<li>
2956
 * <li>{@link BBCODE_SEARCH_ONLY} - only search tags are processed<li>
2957
 * <li>{@link BBCODE_MINIMUM} - only basic formatting is processed<li>
2958
 * <li>{@link BBCODE_ALL} - all available tags are processed<li>
2959
 * </ul>
2960
 * @param string $regex_search Search PCRE to transform field values.
2961
 * @param string $regex_replace Replace PCRE to transform field values.
2962
 * @return string Processed input value.
2963
 */
2964
function update_references ($value, $bbcode_mode = BBCODE_ALL, $regex_search = NULL, $regex_replace = NULL)
2965
{
2966
    debug_write_log(DEBUG_TRACE, '[update_references]');
2967
    debug_write_log(DEBUG_DUMP,  '[update_references] $value = ' . $value);
2968
    debug_write_log(DEBUG_DUMP,  '[update_references] $bbcode_mode   = ' . $bbcode_mode);
2969
    debug_write_log(DEBUG_DUMP,  '[update_references] $regex_search  = ' . $regex_search);
2970
    debug_write_log(DEBUG_DUMP,  '[update_references] $regex_replace = ' . $regex_replace);
2971
2972
    // Transform values with regex specified for current field.
2973
    if (ustrlen($regex_search) != 0 && ustrlen($regex_replace) != 0)
2974
    {
2975
        debug_write_log(DEBUG_NOTICE, '[update_references] Regex is specified for this field.');
2976
        $value = preg_replace("/{$regex_search}/isu", $regex_replace, $value);
2977
    }
2978
2979
    // Strip special HTML characters.
2980
    $value = ustr2html($value);
2981
2982
    // Transform "rec#<number>" strings into BBCode [url] tags
2983
    $matches = array();
2984
2985
    if (preg_match_all('/(rec#(\d+))/iu', $value, $matches, PREG_SET_ORDER))
2986
    {
2987
        debug_write_log(DEBUG_NOTICE, '[update_references] "rec#" is found.');
2988
2989
        foreach ($matches as $match)
2990
        {
2991
            $id = (int)$match[2];
2992
2993
            if ($id != 0)
2994
            {
2995
                $record = record_find($id);
2996
2997
                if ($record)
2998
                {
2999
                    $replace = '[url=view.php?id=' . $id . ']' . record_id($id, $record['template_prefix']) . '[/url]';
3000
                    $value   = ustr_replace($match[1], $replace, $value);
3001
                }
3002
            }
3003
        }
3004
    }
3005
3006
    // Remove non-Unix EOLs.
3007
    $value = ustr_replace("\r", NULL, $value);
3008
3009
    // Trim extra EOLs inside "[code]" blocks.
3010
    $value = ustr_replace("[code]\n",  "[code]",  $value);
3011
    $value = ustr_replace("\n[/code]", "[/code]", $value);
3012
3013
    // Replace newline characters with "%br;".
3014
    $value = ustr_replace("\n", '%br;', $value);
3015
3016
    // Process BBCode tags.
3017
    $search_text = ($_SESSION[VAR_SEARCH_MODE] ? $_SESSION[VAR_SEARCH_TEXT] : NULL);
3018
    $value = bbcode2xml($value, $bbcode_mode, $search_text);
3019
3020
    debug_write_log(DEBUG_DUMP, '[update_references] return = ' . $value);
3021
3022
    return $value;
3023
}
3024
3025
?>
3026