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/events.php (17 issues)

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

1
<?php
2
3
//------------------------------------------------------------------------------
4
//
5
//  eTraxis - Records tracking web-based system
6
//  Copyright (C) 2005-2011  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
 * Events
25
 *
26
 * This module provides API to work with events of records.
27
 * See also {@link https://github.com/etraxis/etraxis-obsolete/wiki/tbl_events tbl_events} database table.
28
 *
29
 * @package DBO
30
 * @subpackage Events
31
 */
32
33
/**#@+
34
 * Dependency.
35
 */
36
require_once('../engine/engine.php');
37
require_once('../dbo/accounts.php');
38
require_once('../dbo/states.php');
39
/**#@-*/
40
41
//------------------------------------------------------------------------------
42
//  Definitions.
43
//------------------------------------------------------------------------------
44
45
/**#@+
46
 * Event type.
47
 */
48
define('EVENT_UNUSED',               0);
49
define('EVENT_RECORD_CREATED',       1);
50
define('EVENT_RECORD_ASSIGNED',      2);
51
define('EVENT_RECORD_MODIFIED',      3);
52
define('EVENT_RECORD_STATE_CHANGED', 4);
53
define('EVENT_RECORD_POSTPONED',     5);
54
define('EVENT_RECORD_RESUMED',       6);
55
define('EVENT_COMMENT_ADDED',        7);
56
define('EVENT_FILE_ATTACHED',        8);
57
define('EVENT_FILE_REMOVED',         9);
58
define('EVENT_RECORD_CLONED',        10);
59
define('EVENT_SUBRECORD_ADDED',      11);
60
define('EVENT_SUBRECORD_REMOVED',    12);
61
define('EVENT_CONFIDENTIAL_COMMENT', 13);
62
define('EVENT_RECORD_REOPENED',      14);
63
define('EVENT_RECORD_SUBSCRIBED',    100);
64
define('EVENT_RECORD_UNSUBSCRIBED',  101);
65
/**#@-*/
66
67
/**#@+
68
 * Permission.
69
 */
70
define('PERMIT_CREATE_RECORD',         0x0001);
71
define('PERMIT_MODIFY_RECORD',         0x0002);
72
define('PERMIT_POSTPONE_RECORD',       0x0004);
73
define('PERMIT_RESUME_RECORD',         0x0008);
74
define('PERMIT_REASSIGN_RECORD',       0x0010);
75
define('PERMIT_REOPEN_RECORD',         0x0020);
76
define('PERMIT_ADD_COMMENTS',          0x0040);
77
define('PERMIT_ATTACH_FILES',          0x0080);
78
define('PERMIT_REMOVE_FILES',          0x0100);
79
define('PERMIT_CONFIDENTIAL_COMMENTS', 0x0200);
80
define('PERMIT_SEND_REMINDERS',        0x0400);
81
define('PERMIT_DELETE_RECORD',         0x0800);
82
define('PERMIT_ADD_SUBRECORDS',        0x1000);
83
define('PERMIT_REMOVE_SUBRECORDS',     0x2000);
84
define('PERMIT_VIEW_RECORD',       0x40000000);
85
/**#@-*/
86
87
/**#@+
88
 * Notifications filter.
89
 */
90
define('NOTIFY_RECORD_CREATED',       0x0001);
91
define('NOTIFY_RECORD_ASSIGNED',      0x0002);
92
define('NOTIFY_RECORD_MODIFIED',      0x0004);
93
define('NOTIFY_RECORD_STATE_CHANGED', 0x0008);
94
define('NOTIFY_RECORD_POSTPONED',     0x0010);
95
define('NOTIFY_RECORD_RESUMED',       0x0020);
96
define('NOTIFY_COMMENT_ADDED',        0x0040);
97
define('NOTIFY_FILE_ATTACHED',        0x0080);
98
define('NOTIFY_FILE_REMOVED',         0x0100);
99
define('NOTIFY_RECORD_CLONED',        0x0200);
100
define('NOTIFY_SUBRECORD_ADDED',      0x0400);
101
define('NOTIFY_SUBRECORD_REMOVED',    0x0800);
102
define('NOTIFY_RECORD_REOPENED',      0x1000);
103
/**#@-*/
104
105
// Correspondence between event types
106
// and notifications filter.
107
$notifications_filter = array
108
(
109
    EVENT_RECORD_CREATED       => NOTIFY_RECORD_CREATED,
110
    EVENT_RECORD_ASSIGNED      => NOTIFY_RECORD_ASSIGNED,
111
    EVENT_RECORD_MODIFIED      => NOTIFY_RECORD_MODIFIED,
112
    EVENT_RECORD_STATE_CHANGED => NOTIFY_RECORD_STATE_CHANGED,
113
    EVENT_RECORD_POSTPONED     => NOTIFY_RECORD_POSTPONED,
114
    EVENT_RECORD_RESUMED       => NOTIFY_RECORD_RESUMED,
115
    EVENT_RECORD_REOPENED      => NOTIFY_RECORD_REOPENED,
116
    EVENT_COMMENT_ADDED        => NOTIFY_COMMENT_ADDED,
117
    EVENT_FILE_ATTACHED        => NOTIFY_FILE_ATTACHED,
118
    EVENT_FILE_REMOVED         => NOTIFY_FILE_REMOVED,
119
    EVENT_RECORD_CLONED        => NOTIFY_RECORD_CLONED,
120
    EVENT_SUBRECORD_ADDED      => NOTIFY_SUBRECORD_ADDED,
121
    EVENT_SUBRECORD_REMOVED    => NOTIFY_SUBRECORD_REMOVED,
122
    EVENT_CONFIDENTIAL_COMMENT => NOTIFY_COMMENT_ADDED,
123
);
124
125
//------------------------------------------------------------------------------
126
//  Functions.
127
//------------------------------------------------------------------------------
128
129
/**
130
 * Finds in database and returns the information about specified event of particular record.
131
 *
132
 * @param int $record_id Record ID.
133
 * @param int $type Type of event.
134
 * @param int $time {@link http://en.wikipedia.org/wiki/Unix_time Unix timestamp} of event.
135
 * @param int $param Parameter of event, depends on the event type.
136
 * @return array Array with data if event is found in database, FALSE otherwise.
137
 */
138
function event_find ($record_id, $type, $time, $param)
139
{
140
    debug_write_log(DEBUG_TRACE, '[event_find]');
141
    debug_write_log(DEBUG_DUMP,  '[event_find] $record_id = ' . $record_id);
142
    debug_write_log(DEBUG_DUMP,  '[event_find] $type      = ' . $type);
143
    debug_write_log(DEBUG_DUMP,  '[event_find] $time      = ' . $time);
144
    debug_write_log(DEBUG_DUMP,  '[event_find] $param     = ' . $param);
145
146
    $rs = dal_query(is_null($param) ? 'events/fndk2.sql' : 'events/fndk.sql',
147
                    $record_id,
148
                    $_SESSION[VAR_USERID],
149
                    $type,
150
                    $time,
151
                    $param);
152
153
    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...
154
}
155
156
/**
157
 * Creates new event for specified record.
158
 *
159
 * @param int $record_id Record ID.
160
 * @param int $type Type of event.
161
 * @param int $time {@link http://en.wikipedia.org/wiki/Unix_time Unix timestamp} of event.
162
 * @param int $param Parameter of event, depends on the event type.
163
 * @return array Array with data of event if it was successfully created, FALSE otherwise.
164
 */
165
function event_create ($record_id, $type, $time, $param = NULL)
166
{
167
    debug_write_log(DEBUG_TRACE, '[event_create]');
168
    debug_write_log(DEBUG_DUMP,  '[event_create] $record_id = ' . $record_id);
169
    debug_write_log(DEBUG_DUMP,  '[event_create] $type      = ' . $type);
170
    debug_write_log(DEBUG_DUMP,  '[event_create] $time      = ' . $time);
171
    debug_write_log(DEBUG_DUMP,  '[event_create] $param     = ' . $param);
172
173
    dal_query('events/create.sql',
174
              $record_id,
175
              $_SESSION[VAR_USERID],
176
              $type,
177
              $time,
178
              is_null($param) ? NULL : $param);
179
180
    dal_query('records/change.sql',
181
              $record_id,
182
              $time);
183
184
    return event_find($record_id, $type, $time, is_null($param) ? NULL : $param);
185
}
186
187
/**
188
 * Deletes specified event.
189
 *
190
 * @param int $event_id ID of event to be deleted.
191
 * @return int Always {@link NO_ERROR}.
192
 */
193
function event_destroy ($event_id)
194
{
195
    debug_write_log(DEBUG_TRACE, '[event_destroy]');
196
    debug_write_log(DEBUG_DUMP,  '[event_destroy] $event_id = ' . $event_id);
197
198
    dal_query('events/delete.sql', $event_id);
199
200
    return NO_ERROR;
201
}
202
203
/**
204
 * Generates and returns string of text, describing specified event.
205
 *
206
 * @param int $event_id ID of event to be described.
207
 * @param int $event_type Type of event.
208
 * @param int $event_param Parameter of event, depends on the event type.
209
 * @param int $locale ID of language. If omitted, then language of current user, or (when user is not logged in) default language will be used (see {@link LANG_DEFAULT}).
210
 * @return string Event description, or NULL on failure.
211
 */
212
function get_event_string ($event_id, $event_type, $event_param, $locale = NULL)
213
{
214
    debug_write_log(DEBUG_TRACE, '[get_event_string]');
215
    debug_write_log(DEBUG_DUMP,  '[get_event_string] $event_id    = ' . $event_id);
216
    debug_write_log(DEBUG_DUMP,  '[get_event_string] $event_type  = ' . $event_type);
217
    debug_write_log(DEBUG_DUMP,  '[get_event_string] $event_param = ' . $event_param);
218
    debug_write_log(DEBUG_DUMP,  '[get_event_string] $locale      = ' . $locale);
219
220
    $res = NULL;
221
222
    switch ($event_type)
223
    {
224 View Code Duplication
        case EVENT_RECORD_CREATED:
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
225
            $state = state_find($event_param);
226
            $res = ustrprocess(get_html_resource(RES_EVENT_RECORD_CREATED_ID, $locale), ustr2html($state['state_name']));
227
            break;
228
229
        case EVENT_RECORD_ASSIGNED:
230
            $account = account_find($event_param);
231
            $res = ustrprocess(get_html_resource(RES_EVENT_RECORD_ASSIGNED_ID, $locale), ustr2html(is_null($locale) ? sprintf('%s (%s)', $account['fullname'], account_get_username($account['username'])) : $account['fullname']));
232
            break;
233
234
        case EVENT_RECORD_MODIFIED:
235
            $res = get_html_resource(RES_EVENT_RECORD_MODIFIED_ID, $locale);
236
            break;
237
238 View Code Duplication
        case EVENT_RECORD_STATE_CHANGED:
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
239
            $state = state_find($event_param);
240
            $res = ustrprocess(get_html_resource(RES_EVENT_RECORD_STATE_CHANGED_ID, $locale), ustr2html($state['state_name']));
241
            break;
242
243
        case EVENT_RECORD_POSTPONED:
244
            $res = ustrprocess(get_html_resource(RES_EVENT_RECORD_POSTPONED_ID, $locale), get_date($event_param, $locale));
245
            break;
246
247
        case EVENT_RECORD_RESUMED:
248
            $res = get_html_resource(RES_EVENT_RECORD_RESUMED_ID, $locale);
249
            break;
250
251 View Code Duplication
        case EVENT_RECORD_REOPENED:
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
252
            $state = state_find($event_param);
253
            $res = ustrprocess(get_html_resource(RES_EVENT_RECORD_REOPENED_ID, $locale), ustr2html($state['state_name']));
254
            break;
255
256
        case EVENT_COMMENT_ADDED:
257
            $res = get_html_resource(RES_EVENT_COMMENT_ADDED_ID, $locale);
258
            break;
259
260 View Code Duplication
        case EVENT_FILE_ATTACHED:
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
261
            $rs2 = dal_query('attachs/fndk.sql', $event_id);
262
            $name = ($rs2->rows == 0 ? '?' : $rs2->fetch('attachment_name'));
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...
263
            $res = ustrprocess(get_html_resource(RES_EVENT_FILE_ATTACHED_ID, $locale), ustr2html($name));
264
            break;
265
266 View Code Duplication
        case EVENT_FILE_REMOVED:
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
267
            $rs2 = dal_query('attachs/fndid.sql', $event_param);
268
            $name = ($rs2->rows == 0 ? '?' : $rs2->fetch('attachment_name'));
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...
269
            $res = ustrprocess(get_html_resource(RES_EVENT_FILE_REMOVED_ID, $locale), ustr2html($name));
270
            break;
271
272
        case EVENT_RECORD_CLONED:
273
            $res = ustrprocess(get_html_resource(RES_EVENT_RECORD_CLONED_ID, $locale), str_pad($event_param, 3, '0', STR_PAD_LEFT));
274
            break;
275
276
        case EVENT_SUBRECORD_ADDED:
277
            $res = ustrprocess(get_html_resource(RES_EVENT_SUBRECORD_ADDED_ID, $locale), str_pad($event_param, 3, '0', STR_PAD_LEFT));
278
            break;
279
280
        case EVENT_SUBRECORD_REMOVED:
281
            $res = ustrprocess(get_html_resource(RES_EVENT_SUBRECORD_REMOVED_ID, $locale), str_pad($event_param, 3, '0', STR_PAD_LEFT));
282
            break;
283
284
        case EVENT_CONFIDENTIAL_COMMENT:
285
            $res = get_html_resource(RES_EVENT_CONFIDENTIAL_COMMENT_ADDED_ID, $locale);
286
            break;
287
288
        case EVENT_RECORD_SUBSCRIBED:
289
            $res = ustrprocess(get_html_resource(RES_SUBJECT_SUBSCRIBED_ID, $locale), ustr2html($event_param));
290
            break;
291
292
        case EVENT_RECORD_UNSUBSCRIBED:
293
            $res = ustrprocess(get_html_resource(RES_SUBJECT_UNSUBSCRIBED_ID, $locale), ustr2html($event_param));
294
            break;
295
296
        default:
297
            debug_write_log(DEBUG_WARNING, 'Unknown event type = ' . $event_type);
298
    }
299
300
    return $res;
301
}
302
303
/**
304
 * Generates and returns message body for mail notification about specified event of particular record.
305
 *
306
 * @param array $record Array with data of record (e.g. how it's returned by {@link record_find}).
307
 * @param array $event Array with data of event (e.g. how it's returned by {@link event_find}).
308
 * @param int $locale ID of language. If omitted, then language of current user, or (when user is not logged in) default language will be used (see {@link LANG_DEFAULT}).
309
 * @return string Generated message body.
310
 */
311
function generate_message ($record, $event, $locale = NULL)
312
{
313
    debug_write_log(DEBUG_TRACE, '[generate_message]');
314
    debug_write_log(DEBUG_DUMP,  '[generate_message] $locale = ' . $locale);
315
316
    // general information
317
318
    $fields = array
319
    (
320
        RES_ID_ID          => record_id($record['record_id'], $record['template_prefix']),
321
        RES_SUBJECT_ID     => update_references($record['subject'], BBCODE_MINIMUM),
322
        RES_STATE_ID       => ustr2html($record['state_name']),
323
        RES_RESPONSIBLE_ID => is_null($record['username']) ? get_html_resource(RES_NONE_ID, $locale)
324
                                                           : ustr2html(sprintf('%s (%s)', $record['fullname'], account_get_username($record['username']))),
325
        RES_AUTHOR_ID      => ustr2html(sprintf('%s (%s)', $record['author_fullname'], account_get_username($record['author_username']))),
326
        RES_AGE_ID         => get_record_last_event($record) . '/' . get_record_age($record),
327
        RES_PROJECT_ID     => ustr2html($record['project_name']),
328
        RES_TEMPLATE_ID    => ustr2html($record['template_name']),
329
        RES_EVENT_ID       => get_event_string($event['event_id'], $event['event_type'], $event['event_param'], $locale),
330
    );
331
332
    $message = '<html>'
333
             . '<body>'
334
             . '<b><font color="red">' . get_html_resource(RES_ALERT_DO_NOT_REPLY_ID, $locale) . '</font></b><br/>'
335
             . '<br/><hr/>'
336
             . '<b>' . get_html_resource(RES_GENERAL_INFO_ID, $locale) . '</b>'
337
             . '<hr/>'
338
             . '<table border="0" cellspacing="0" cellpadding="5">';
339
340
    foreach ($fields as $key => $value)
341
    {
342
        $message .= '<tr valign="top">'
343
                  . '<td><b>' . get_html_resource($key, $locale) . ':</b></td>'
344
                  . '<td>' . $value . '</td>'
345
                  . '</tr>';
346
    }
347
348
    $message .= '</table>'
349
              . '<br/><hr/>';
350
351
    // go through the list of all states and their fields
352
353
    $states = dal_query('records/elist.sql', $record['record_id']);
354
355
    while (($state = $states->fetch()))
356
    {
357
        $fields = dal_query('records/flist3.sql', $record['record_id'], $state['state_id']);
358
359
        if ($fields->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...
360
        {
361
            $message .= '<b>' . ustr2html($state['state_name']) . '</b>'
362
                      . '<hr/>'
363
                      . '<table border="0" cellspacing="0" cellpadding="5">';
364
365
            while (($field = $fields->fetch()))
366
            {
367
                $value = value_find($field['field_type'], $field['value_id']);
368
369
                if ($field['field_type'] == FIELD_TYPE_CHECKBOX)
370
                {
371
                    $value = get_html_resource($value ? RES_YES_ID : RES_NO_ID);
372
                }
373
                elseif ($field['field_type'] == FIELD_TYPE_LIST)
374
                {
375
                    $value = (is_null($value) ? NULL : value_find_listvalue($field['field_id'], $value));
376
                }
377
                elseif ($field['field_type'] == FIELD_TYPE_RECORD)
378
                {
379
                    $value = (is_null($value) ? NULL : 'rec#' . $value);
380
                }
381
382
                $value = is_null($value)
383
                       ? get_html_resource(RES_NONE_ID)
384
                       : str_replace('%br;', '<br/>', update_references($value, BBCODE_ALL, $field['regex_search'], $field['regex_replace']));
385
386
                $message .= '<tr valign="top">'
387
                          . '<td><b>' . ustr2html($field['field_name']) . ':</b></td>'
388
                          . '<td>' . $value . '</td>'
389
                          . '</tr>';
390
            }
391
392
            $message .= '</table>'
393
                      . '<br/><hr/>';
394
        }
395
    }
396
397
    // if it was a comment, add its text
398
399
    if (!is_null($event) &&
400
        ($event['event_type'] == EVENT_COMMENT_ADDED || $event['event_type'] == EVENT_CONFIDENTIAL_COMMENT))
401
    {
402
        $rs = dal_query('comments/fndk.sql', $event['event_id']);
403
404
        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...
405
        {
406
            $message .= '<b>' . get_html_resource(RES_COMMENT_ID, $locale) . '</b>'
407
                      . '<hr/>'
408
                      . '<table border="0" cellspacing="0" cellpadding="5">'
409
                      . '<tr><td>'
410
                      . str_replace('%br;', '<br/>', update_references($rs->fetch('comment_body')))
411
                      . '</td></tr>'
412
                      . '</table>'
413
                      . '<br/><hr/>';
414
        }
415
    }
416
417
    // link to the record
418
419
    $message .= '<a href="' . WEBROOT . 'records/view.php?id=' . $record['record_id'] . '">' . get_html_resource(RES_VIEW_RECORD_ID, $locale) . '</a>'
420
              . '</body>'
421
              . '</html>';
422
423
    return $message;
424
}
425
426
/**
427
 * Sends mail notification about specified event to all interested parties.
428
 *
429
 * @param array $event Array with data of event (e.g. how it's returned by {@link event_find}).
430
 * @param int $attachment_id ID of new attachment if event is about attached file, NULL otherwise.
431
 * @param string $attachment_name Name of new attachment if event is about attached file, NULL otherwise.
432
 * @param string $attachment_type MIME type of new attachment if event is about attached file, NULL otherwise.
433
 * @param int $attachment_size Size of new attachment if event is about attached file, NULL otherwise.
434
 * @return int Always {@link NO_ERROR}.
435
 */
436
function event_mail ($event, $attachment_id = NULL, $attachment_name = NULL, $attachment_type = NULL, $attachment_size = NULL)
437
{
438
    debug_write_log(DEBUG_TRACE, '[event_mail]');
439
440
    global $notifications_filter;
441
    global $locale_info;
442
443
    // If debug mode is turned on full level, we go through the function even email notifications
444
    // functionality is disabled, but don't send the email.
445
    if (!EMAIL_NOTIFICATIONS_ENABLED && DEBUG_MODE != DEBUG_MODE_FULL)
446
    {
447
        debug_write_log(DEBUG_NOTICE, '[event_mail] Email notifications are disabled.');
448
        return NO_ERROR;
449
    }
450
451
    // Find info about currently logged in user, who is always an author of the event.
452
    $account = account_find($_SESSION[VAR_USERID]);
453
454
    if (!$account)
0 ignored issues
show
Bug Best Practice introduced by Artem Rodygin
The expression $account of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
455
    {
456
        debug_write_log(DEBUG_WARNING, '[event_mail] Account cannot be found.');
457
        return ERROR_NOT_FOUND;
458
    }
459
460
    // Find info about record, which the event is of.
461
    $rs = dal_query('records/fndid.sql', $event['record_id'], time());
462
463
    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...
464
    {
465
        debug_write_log(DEBUG_WARNING, '[event_mail] Record not found = ' . $event['record_id']);
466
        return ERROR_NOT_FOUND;
467
    }
468
469
    // Since sending email can takes a time, disable PHP execution timeout.
470
    if (!ini_get('safe_mode'))
471
    {
472
        set_time_limit(0);
473
    }
474
475
    $record = $rs->fetch();
476
477
    $subscribes = array();  // IDs of everyone, who is subscribed to the record
478
    $allowed    = array();  // email addresses of everyone, who is permitted to see this event
479
480
    // Enumerate IDs of everyone, who is subscribed to the record.
481
    $rs = dal_query('records/subscribes.sql', $record['record_id']);
482
483
    while (($row = $rs->fetch()))
484
    {
485
        array_push($subscribes, $row[0]);
486
    }
487
488
    // Enumerate email addresses of everyone, who is permitted to see this event.
489
    $rs = dal_query((DATABASE_DRIVER == DRIVER_ORACLE9 ? 'groups/oracle/gplist2.sql' : 'groups/gplist2.sql'),
490
                    $record['record_id'],
491
                    ($event['event_type'] == EVENT_CONFIDENTIAL_COMMENT ? PERMIT_CONFIDENTIAL_COMMENTS : PERMIT_VIEW_RECORD));
492
493
    while (($row = $rs->fetch()))
494
    {
495
        array_push($allowed, $row['email']);
496
    }
497
498
    // Author of record and current responsible are permitted to see any event besides confidential comment.
499
    if ($event['event_type'] != EVENT_CONFIDENTIAL_COMMENT)
500
    {
501
        $author      = account_find($record['creator_id']);
502
        $responsible = (is_null($record['responsible_id'])
503
                        ? FALSE
504
                        : account_find($record['responsible_id']));
505
506
        if ($author)
0 ignored issues
show
Bug Best Practice introduced by Artem Rodygin
The expression $author of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
507
        {
508
            array_push($allowed, $author['email']);
509
        }
510
511
        if ($responsible)
0 ignored issues
show
Bug Best Practice introduced by Artem Rodygin
The expression $responsible of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
512
        {
513
            array_push($allowed, $responsible['email']);
514
        }
515
    }
516
517
    // Get IDs of all supported locales.
518
    $supported_locales = array_keys($locale_info);
519
520
    // Send separated notification for each locale in corresponding language.
521
    foreach ($supported_locales as $locale)
522
    {
523
        $to = array();  // email addresses of recipients for TO field
524
        $cc = array();  // email addresses of recipients for CC field
525
526
        // Enumerate all project members with specified locale in settings.
527
        $rs = dal_query('records/members.sql', $event['project_id'], $locale);
528
529
        while (($row = $rs->fetch()))
530
        {
531
            // Skip author of event.
532
            if ($row['account_id'] == $_SESSION[VAR_USERID])
533
            {
534
                debug_write_log(DEBUG_TRACE, '[event_mail] User is author of event.');
535
                continue;
536
            }
537
538
            // Submitter of the record should receive notification of each record's event.
539
            if ($row['account_id'] == $record['creator_id'])
540
            {
541
                debug_write_log(DEBUG_TRACE, '[event_mail] User is creator of record.');
542
                array_push($to, $row['email']);
543
            }
544
            // Current assignee of the record should receive notification of each record's event.
545 View Code Duplication
            elseif ($row['account_id'] == $record['responsible_id'])
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
546
            {
547
                debug_write_log(DEBUG_TRACE, '[event_mail] User is responsible of record.');
548
                array_push($to, $row['email']);
549
            }
550
            // Subscribed to the record should receive notification of each record's event.
551 View Code Duplication
            elseif (in_array($row['account_id'], $subscribes))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
552
            {
553
                debug_write_log(DEBUG_TRACE, '[event_mail] User is subscribed to record.');
554
                array_push($to, $row['email']);
555
            }
556
            else
557
            {
558
                debug_write_log(DEBUG_TRACE, '[event_mail] User should not recieve the notification.');
559
            }
560
        }
561
562
        // Enumerate all subscriptions for this template.
563
        $rs = dal_query('subscriptions/enum.sql', $record['project_id'], $record['template_id'], $locale);
564
565
        while (($row = $rs->fetch()))
566
        {
567
            // Skip author of event.
568
            if ($row['account_id'] == $_SESSION[VAR_USERID])
569
            {
570
                debug_write_log(DEBUG_TRACE, '[event_mail] User is author of event.');
571
                continue;
572
            }
573
574
            // If the event corresponds to the subscription, add owner of this subscription to recipients.
575
            if (($row['subscribe_flags'] & $notifications_filter[$event['event_type']]) != 0)
576
            {
577
                debug_write_log(DEBUG_TRACE, '[event_mail] Event satisfies the filter.');
578
                array_push($to, $row['email']);
579
580
                // If subscription contains specified carbon copy, add the address to CC.
581
                if (ustrlen($row['carbon_copy']) != 0)
582
                {
583
                    debug_write_log(DEBUG_TRACE, '[event_mail] Carbon copy is set.');
584
                    $cc[$row['carbon_copy']] = $row['email'];
585
                }
586
            }
587
        }
588
589
        $to = array_intersect($to, $allowed);       // remove from TO everyone who is not permitted to see this event
590
        $cc = array_intersect($cc, $to);            // remove from CC all carbon copies which are not in TO anymore
591
        $to = array_merge($to, array_keys($cc));    // merge TO and CC
592
        $to = array_unique($to);                    // remove all duplicates inside TO
593
594
        // If we still have at least one recipient - send the mail.
595
        if (count($to) != 0)
596
        {
597
            $recipients = implode(', ', $to);
598
            $rec_id     = record_id($record['record_id'], $record['template_prefix']);
599
            $subject    = "[{$event['project_name']}] {$rec_id}: "
600
                        . htmlspecialchars_decode(update_references($record['subject'], BBCODE_OFF), ENT_COMPAT);
601
            $message    = generate_message($record, $event, $locale);
602
603
            if (EMAIL_NOTIFICATIONS_ENABLED)
604
            {
605
                debug_write_log(DEBUG_NOTICE, '[event_mail] Sending email.');
606
607
                sendmail($account['fullname'],
608
                         $account['email'],
609
                         $recipients,
610
                         $subject,
611
                         $message,
612
                         $attachment_id,
613
                         $attachment_name,
614
                         $attachment_type,
615
                         $attachment_size);
616
            }
617
            else
618
            {
619
                // If debug mode is turned on full level, we goes through the function even email
620
                // notifications functionality is disabled, but do not send the email.
621
                debug_write_log(DEBUG_NOTICE, '[event_mail] Email notifications are disabled.');
622
            }
623
        }
624
    }
625
626
    // Restore PHP execution timeout, disabled above.
627
    if (!ini_get('safe_mode'))
628
    {
629
        ini_restore('max_execution_time');
630
    }
631
632
    return NO_ERROR;
633
}
634
635
?>
0 ignored issues
show
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...
636