GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (4873)

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.

plugins/docman/include/docmanPlugin.class.php (6 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
 * Copyright (c) STMicroelectronics, 2006. All Rights Reserved.
4
 * Copyright (c) Enalean, 2015.
5
 *
6
 * Originally written by Manuel Vacelet, 2006
7
 *
8
 * This file is a part of Codendi.
9
 *
10
 * Codendi is free software; you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation; either version 2 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * Codendi is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with Codendi; if not, write to the Free Software
22
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
 *
24
 * 
25
 */
26
27
require_once 'autoload.php';
28
require_once 'constants.php';
29
30
class DocmanPlugin extends Plugin {
31
32
    const TRUNCATED_SERVICE_NAME = 'Documents';
33
    const SYSTEM_NATURE_NAME     = 'document';
34
35
    /**
36
     * Store docman root items indexed by groupId
37
     * 
38
     * @var Array;
39
     */
40
    private $rootItems = array();
41
42
    /**
43
     * Store controller instances
44
     * 
45
     * @var Array
46
     */
47
    private $controller = array();
48
49
    function __construct($id) {
50
        parent::__construct($id);
51
52
        $this->_addHook('cssfile',                           'cssFile',                           false);
53
        $this->_addHook('javascript_file',                   'jsFile',                            false);
54
        $this->_addHook('logs_daily',                        'logsDaily',                         false);
55
        $this->_addHook('permission_get_name',               'permission_get_name',               false);
56
        $this->_addHook('permission_get_object_type',        'permission_get_object_type',        false);
57
        $this->_addHook('permission_get_object_name',        'permission_get_object_name',        false);
58
        $this->_addHook('permission_get_object_fullname',    'permission_get_object_fullname',    false);
59
        $this->_addHook('permission_user_allowed_to_change', 'permission_user_allowed_to_change', false);
60
        $this->_addHook('service_public_areas',              'service_public_areas',              false);
61
        $this->_addHook('service_admin_pages',               'service_admin_pages',               false);
62
        $this->_addHook('permissions_for_ugroup',            'permissions_for_ugroup',            false);
63
        $this->_addHook('register_project_creation',         'installNewDocman',                  false);
64
        $this->_addHook('service_is_used',                   'service_is_used',                   false);
65
        $this->_addHook('soap',                              'soap',                              false);
66
        $this->_addHook('widget_instance',                   'myPageBox',                         false);
67
        $this->_addHook('widgets',                           'widgets',                           false);
68
        $this->_addHook('codendi_daily_start',               'codendiDaily',                      false);
69
        $this->_addHook('default_widgets_for_new_owner',     'default_widgets_for_new_owner',     false);
70
        $this->_addHook('wiki_page_updated',                 'wiki_page_updated',                 false);
71
        $this->_addHook('wiki_before_content',               'wiki_before_content',               false);
72
        $this->_addHook(Event::WIKI_DISPLAY_REMOVE_BUTTON,   'wiki_display_remove_button',        false);
73
        $this->_addHook('isWikiPageReferenced',              'isWikiPageReferenced',              false);
74
        $this->_addHook('isWikiPageEditable',                'isWikiPageEditable',                false);
75
        $this->_addHook('userCanAccessWikiDocument',         'userCanAccessWikiDocument',         false);
76
        $this->_addHook('getPermsLabelForWiki',              'getPermsLabelForWiki',              false);
77
        $this->_addHook('ajax_reference_tooltip',            'ajax_reference_tooltip',            false);
78
        $this->_addHook('project_export_entry',              'project_export_entry',              false);
79
        $this->_addHook('project_export',                    'project_export',                    false);
80
        $this->_addHook('SystemEvent_PROJECT_RENAME',        'renameProject',                     false);
81
        $this->_addHook('file_exists_in_data_dir',           'file_exists_in_data_dir',           false);
82
        $this->_addHook('webdav_root_for_service',           'webdav_root_for_service',           false);
83
        $this->addHook(Event::SERVICE_ICON);
84
        $this->addHook(Event::SERVICES_ALLOWED_FOR_PROJECT);
85
        // Stats plugin
86
        $this->_addHook('plugin_statistics_disk_usage_collect_project', 'plugin_statistics_disk_usage_collect_project', false);
87
        $this->_addHook('plugin_statistics_disk_usage_service_label',   'plugin_statistics_disk_usage_service_label',   false);
88
        $this->_addHook('plugin_statistics_color',                      'plugin_statistics_color',                      false);
89
90
        $this->_addHook('show_pending_documents',             'show_pending_documents',             false);
91
92
        $this->_addHook('backend_system_purge_files',  'purgeFiles',  false);
93
        $this->_addHook('project_admin_remove_user', 'projectRemoveUser', false);
94
        $this->_addHook('project_is_private', 'projectIsPrivate', false);
95
96
        $this->_addHook('permission_request_information', 'permissionRequestInformation', false);
97
98
        $this->_addHook('fill_project_history_sub_events', 'fillProjectHistorySubEvents', false);
99
        $this->_addHook('project_is_deleted',              'project_is_deleted',          false);
100
        $this->_addHook(Event::COMBINED_SCRIPTS,           'combinedScripts',             false);
101
        $this->addHook(Event::PROCCESS_SYSTEM_CHECK);
102
        $this->addHook(Event::SERVICES_TRUNCATED_EMAILS);
103
104
        $this->addHook(Event::GET_REFERENCE);
105
    }
106
107
    public function getHooksAndCallbacks() {
108
        if (defined('STATISTICS_BASE_DIR')) {
109
            $this->addHook(Statistics_Event::FREQUENCE_STAT_ENTRIES);
110
            $this->addHook(Statistics_Event::FREQUENCE_STAT_SAMPLE);
111
        }
112
        if (defined('FULLTEXTSEARCH_BASE_URL')) {
113
            $this->_addHook(FULLTEXTSEARCH_EVENT_FETCH_ALL_DOCUMENT_SEARCH_TYPES);
114
            $this->_addHook(FULLTEXTSEARCH_EVENT_DOES_DOCMAN_SERVICE_USE_UGROUP);
115
        }
116
117
        return parent::getHooksAndCallbacks();
118
    }
119
120
    public function getServiceShortname() {
121
        return 'docman';
122
    }
123
124
    public function service_icon($params) {
125
        $params['list_of_icon_unicodes'][$this->getServiceShortname()] = '\e80c';
126
    }
127
128
    /**
129
     * @see Statistics_Event::FREQUENCE_STAT_ENTRIES
130
     */
131
    public function plugin_statistics_frequence_stat_entries($params) {
132
        $params['entries'][$this->getServiceShortname()] = 'Documents viewed';
133
    }
134
135
    /**
136
     * @see Statistics_Event::FREQUENCE_STAT_SAMPLE
137
     */
138
    public function plugin_statistics_frequence_stat_sample($params) {
139
        if ($params['character'] === $this->getServiceShortname()) {
140
            $params['sample'] = new Docman_Sample();
141
        }
142
    }
143
144
    function permission_get_name($params) {
145
        if (!$params['name']) {
146
            switch($params['permission_type']) {
147
                case 'PLUGIN_DOCMAN_READ':
148
                    $params['name'] = $GLOBALS['Language']->getText('plugin_docman', 'permission_read');
149
                    break;
150
                case 'PLUGIN_DOCMAN_WRITE':
151
                    $params['name'] = $GLOBALS['Language']->getText('plugin_docman', 'permission_write');
152
                    break;
153
                case 'PLUGIN_DOCMAN_MANAGE':
154
                    $params['name'] = $GLOBALS['Language']->getText('plugin_docman', 'permission_manage');
155
                    break;
156
                case 'PLUGIN_DOCMAN_ADMIN':
157
                    $params['name'] = $GLOBALS['Language']->getText('plugin_docman', 'permission_admin');
158
                    break;
159
                default:
160
                    break;
161
            }
162
        }
163
    }
164
    function permission_get_object_type($params) {
165
        if (!$params['object_type']) {
166
            if (in_array($params['permission_type'], array('PLUGIN_DOCMAN_READ', 'PLUGIN_DOCMAN_WRITE', 'PLUGIN_DOCMAN_MANAGE', 'PLUGIN_DOCMAN_ADMIN'))) {
167
                require_once('Docman_ItemFactory.class.php');
168
                $if =& new Docman_ItemFactory();
169
                $item =& $if->getItemFromDb($params['object_id']);
170
                if ($item) {
171
                    $params['object_type'] = is_a($item, 'Docman_Folder') ? 'folder' : 'document';
172
                }
173
            }
174
        }
175
    }
176
    function permission_get_object_name($params) {
177
        if (!$params['object_name']) {
178
            if (in_array($params['permission_type'], array('PLUGIN_DOCMAN_READ', 'PLUGIN_DOCMAN_WRITE', 'PLUGIN_DOCMAN_MANAGE', 'PLUGIN_DOCMAN_ADMIN'))) {
179
                require_once('Docman_ItemFactory.class.php');
180
                $if =& new Docman_ItemFactory();
181
                $item =& $if->getItemFromDb($params['object_id']);
182
                if ($item) {
183
                    $params['object_name'] = $item->getTitle();
184
                }
185
            }
186
        }
187
    }
188
    function permission_get_object_fullname($params) {
189
        if (!$params['object_fullname']) {
190
            if (in_array($params['permission_type'], array('PLUGIN_DOCMAN_READ', 'PLUGIN_DOCMAN_WRITE', 'PLUGIN_DOCMAN_MANAGE', 'PLUGIN_DOCMAN_ADMIN'))) {
191
                require_once('Docman_ItemFactory.class.php');
192
                $if =& new Docman_ItemFactory();
193
                $item =& $if->getItemFromDb($params['object_id']);
194
                if ($item) {
195
                    $type = is_a($item, 'Docman_Folder') ? 'folder' : 'document';
196
                    $name = $item->getTitle();
197
                    $params['object_fullname'] = $type .' '. $name; //TODO i18n
198
                }
199
            }
200
        }
201
    }
202
    function permissions_for_ugroup($params) {
203
        if (!$params['results']) {
204
            if (in_array($params['permission_type'], array('PLUGIN_DOCMAN_READ', 'PLUGIN_DOCMAN_WRITE', 'PLUGIN_DOCMAN_MANAGE', 'PLUGIN_DOCMAN_ADMIN'))) {
205
                require_once('Docman_ItemFactory.class.php');
206
                $if =& new Docman_ItemFactory();
207
                $item =& $if->getItemFromDb($params['object_id']);
208
                if ($item) {
209
                    $type = is_a($item, 'Docman_Folder') ? 'folder' : 'document';
210
                    $params['results']  = $GLOBALS['Language']->getText('plugin_docman', 'resource_name_'.$type, array(
211
                            '/plugins/docman/?group_id='. $params['group_id'] .
212
                              '&amp;action=details&amp;section=permissions' .
213
                              '&amp;id='. $item->getId()
214
                            , $item->getTitle()
215
                        )
216
                    );
217
                }
218
            }
219
        }
220
    }
221
    var $_cached_permission_user_allowed_to_change;
222
    function permission_user_allowed_to_change($params) {
223
        if (!$params['allowed']) {
224
            if (!$this->_cached_permission_user_allowed_to_change) {
225
                if (in_array($params['permission_type'], array('PLUGIN_DOCMAN_READ', 'PLUGIN_DOCMAN_WRITE', 'PLUGIN_DOCMAN_MANAGE', 'PLUGIN_DOCMAN_ADMIN'))) {
226
                    $docman = $this->getHTTPController();
227
                    switch($params['permission_type']) {
228
                        case 'PLUGIN_DOCMAN_READ':
229
                        case 'PLUGIN_DOCMAN_WRITE':
230
                        case 'PLUGIN_DOCMAN_MANAGE':
231
                            $this->_cached_permission_user_allowed_to_change = $docman->userCanManage($params['object_id']);
232
                            break;
233
                        default:
234
                            $this->_cached_permission_user_allowed_to_change = $docman->userCanAdmin();
235
                            break;
236
                    }
237
                }
238
            }
239
            $params['allowed'] = $this->_cached_permission_user_allowed_to_change;
240
        }
241
    }
242
    function &getPluginInfo() {
243
        if (!is_a($this->pluginInfo, 'DocmanPluginInfo')) {
244
            require_once('DocmanPluginInfo.class.php');
245
            $this->pluginInfo =& new DocmanPluginInfo($this);
246
        }
247
        return $this->pluginInfo;
248
    }
249
    
250
    function cssFile($params) {
251
        // Only show the stylesheet if we're actually in the Docman pages.
252
        // This stops styles inadvertently clashing with the main site.
253
        if (strpos($_SERVER['REQUEST_URI'], $this->getPluginPath()) === 0 ||
254
            strpos($_SERVER['REQUEST_URI'], '/widgets/') === 0 
255
        ) {
256
            echo '<link rel="stylesheet" type="text/css" href="'.$this->getThemePath().'/css/style.css" />'."\n";
257
        }
258
    }
259
    
260
    function jsFile($params) {
261
        // Only show the stylesheet if we're actually in the Docman pages.
262
        // This stops styles inadvertently clashing with the main site.
263
        if (strpos($_SERVER['REQUEST_URI'], $this->getPluginPath()) === 0) {
264
            echo '<script type="text/javascript" src="/scripts/behaviour/behaviour.js"></script>'."\n";
265
            echo '<script type="text/javascript" src="/scripts/scriptaculous/scriptaculous.js"></script>'."\n";
266
            echo '<script type="text/javascript" src="'.$this->getPluginPath().'/scripts/docman.js"></script>'."\n";
267
            echo '<script type="text/javascript" src="'.$this->getPluginPath().'/scripts/embedded_file.js"></script>'."\n";
268
        }
269
    }
270
271
    function logsDaily($params) {
272
        $controler = $this->getHTTPController();
273
        $controler->logsDaily($params);
274
    }
275
    
276
    function service_public_areas($params) {
277
        if ($params['project']->usesService($this->getServiceShortname())) {
278
            $params['areas'][] = '<a href="/plugins/docman/?group_id='. $params['project']->getId() .'">' .
279
                '<img src="'. $this->getThemePath() .'/images/ic/text.png" />&nbsp;' .
280
                $GLOBALS['Language']->getText('plugin_docman', 'descriptor_name') .': '.
281
                $GLOBALS['Language']->getText('plugin_docman', 'title') .
282
                '</a>';
283
        }
284
    }
285
    function service_admin_pages($params) {
286
        if ($params['project']->usesService($this->getServiceShortname())) {
287
            $params['admin_pages'][] = '<a href="/plugins/docman/?action=admin&amp;group_id='. $params['project']->getId() .'">' .
288
                $GLOBALS['Language']->getText('plugin_docman', 'service_lbl_key') .' - '. 
289
                $GLOBALS['Language']->getText('plugin_docman', 'admin_title') .
290
                '</a>';
291
        }
292
    }
293
    function installNewDocman($params) {
294
        $controler = $this->getHTTPController();
295
        $controler->installDocman($params['ugroupsMapping'], $params['group_id']);
296
    }
297
    function service_is_used($params) {
298
        if (isset($params['shortname']) && $params['shortname'] == $this->getServiceShortname()) {
299
            if (isset($params['is_used']) && $params['is_used']) {
300
                $this->installNewDocman(array('ugroupsMapping' => false));
301
            }
302
        }
303
    }
304
    function soap($arams) {
305
        require_once('soap.php');
306
    }
307
308
    function myPageBox($params) {
309
        switch ($params['widget']) {
310
            case 'plugin_docman_mydocman':
311
                require_once('Docman_Widget_MyDocman.class.php');
312
                $params['instance'] = new Docman_Widget_MyDocman($this->getPluginPath());
313
                break;
314
            case 'plugin_docman_my_embedded':
315
                require_once('Docman_Widget_MyEmbedded.class.php');
316
                $params['instance'] = new Docman_Widget_MyEmbedded($this->getPluginPath());
317
                break;
318
            case 'plugin_docman_project_embedded':
319
                require_once('Docman_Widget_ProjectEmbedded.class.php');
320
                $params['instance'] = new Docman_Widget_ProjectEmbedded($this->getPluginPath());
321
                break;
322
            case 'plugin_docman_mydocman_search':
323
                require_once('Docman_Widget_MyDocmanSearch.class.php');
324
                $params['instance'] = new Docman_Widget_MyDocmanSearch($this->getPluginPath());
325
                break;
326
            default:
327
                break;
328
        }
329
    }
330
    function widgets($params) {
331
        require_once('common/widget/WidgetLayoutManager.class.php');
332
        if ($params['owner_type'] == WidgetLayoutManager::OWNER_TYPE_USER) {
333
            $params['codendi_widgets'][] = 'plugin_docman_mydocman';
334
            $params['codendi_widgets'][] = 'plugin_docman_mydocman_search';
335
            $params['codendi_widgets'][] = 'plugin_docman_my_embedded';
336
        }
337
        if ($params['owner_type'] == WidgetLayoutManager::OWNER_TYPE_GROUP) {
338
            $params['codendi_widgets'][] = 'plugin_docman_project_embedded';
339
        }
340
    }
341
    function default_widgets_for_new_owner($params) {
342
        require_once('common/widget/WidgetLayoutManager.class.php');
343
        if ($params['owner_type'] == WidgetLayoutManager::OWNER_TYPE_USER) {
344
            $params['widgets'][] = array(
345
                'name' => 'plugin_docman_mydocman',
346
                'column' => 1,
347
                'rank' => 2,
348
            );
349
        }
350
    }
351
    /**
352
     * Hook: called by daily codendi script.
353
     */
354
    function codendiDaily() {
355
        $controler = $this->getHTTPController();
356
        $controler->notifyFuturObsoleteDocuments();
357
        $reminder = new Docman_ApprovalTableReminder();
358
        $reminder->remindApprovers();
359
    }
360
361
    function process() {
362
        $controler = $this->getHTTPController();
363
        $controler->process();
364
    }
365
    
366
    public function processSOAP($request) {
367
        return $this->getSOAPController($request)->process();
368
    }
369
     
370
    function wiki_page_updated($params) {
371
        require_once('Docman_WikiRequest.class.php');
372
        $request = new Docman_WikiRequest(array('action' => 'wiki_page_updated',
373
                                                'wiki_page' => $params['wiki_page'],
374
                                                'diff_link' => $params['diff_link'],
375
                                                'group_id'  => $params['group_id'],
376
                                                'user'      => $params['user'],
377
                                                'version'   => $params['version']));
378
        $this->getWikiController($request)->process(); 
379
    }
380
381
    function wiki_before_content($params) {
382
        require_once('Docman_WikiRequest.class.php');
383
        $params['action'] = 'wiki_before_content';
384
        $request = new Docman_WikiRequest($params);
385
        $this->getWikiController($request)->process(); 
386
    }
387
    
388
    function wiki_display_remove_button($params) {
389
        require_once('Docman_WikiRequest.class.php');
390
        $params['action'] = 'wiki_display_remove_button';
391
        $request = new Docman_WikiRequest($params);
392
        $this->getWikiController($request)->process(); 
393
    }
394
395
    function isWikiPageReferenced($params) {
396
        require_once('Docman_WikiRequest.class.php');
397
        $params['action'] = 'check_whether_wiki_page_is_referenced';
398
        $request = new Docman_WikiRequest($params);
399
        $this->getWikiController($request)->process(); 
400
    }
401
402
    function isWikiPageEditable($params) {
403
        require_once('Docman_WikiRequest.class.php');
404
        $request = new Docman_WikiRequest($params);
405
        $this->getWikiController($request)->process();
406
    }
407
408
    function userCanAccessWikiDocument($params) {
409
        require_once('Docman_WikiRequest.class.php');
410
        $params['action'] = 'check_whether_user_can_access';
411
        $request = new Docman_WikiRequest($params);
412
        $this->getWikiController($request)->process(); 
413
    }
414
415
    function getPermsLabelForWiki($params) {
416
        require_once('Docman_WikiRequest.class.php');
417
        $params['action'] = 'getPermsLabelForWiki';
418
        $request = new Docman_WikiRequest($params);
419
        $this->getWikiController($request)->process(); 
420
    }
421
422
    function ajax_reference_tooltip($params) {
423
        if ($params['reference']->getServiceShortName() == 'docman') {
424
            $request = new Codendi_Request(array(
425
                'id'       => $params['val'],
426
                'group_id' => $params['group_id'],
427
                'action'   => 'ajax_reference_tooltip'
428
            ));
429
            $controler = $this->getCoreController($request);
430
            $controler->process();
431
        }
432
    }
433
434
    /**
435
     *  hook to display the link to export project data
436
     *  @param void
437
     *  @return void
438
     */
439
    function project_export_entry($params) {
440
        // Docman perms
441
        $url  = '?group_id='.$params['group_id'].'&export=plugin_docman_perms';
442
        $params['labels']['plugin_eac_docman']                           = $GLOBALS['Language']->getText('plugin_docman','Project_access_permission');
443
        $params['data_export_links']['plugin_eac_docman']                = $url.'&show=csv';
444
        $params['data_export_format_links']['plugin_eac_docman']         = $url.'&show=format';
445
        $params['history_export_links']['plugin_eac_docman']             = null;
446
        $params['history_export_format_links']['plugin_eac_docman']      = null;
447
        $params['dependencies_export_links']['plugin_eac_docman']        = null;
448
        $params['dependencies_export_format_links']['plugin_eac_docman'] = null;
449
    }
450
451
    /**
452
     *  hook to display the link to export project data
453
     *  @param void
454
     *  @return void
455
     */
456
    function project_export($params) {
457
        if($params['export'] == 'plugin_docman_perms') {
458
            include_once('Docman_PermissionsExport.class.php');
459
            $request = HTTPRequest::instance();
460
            $permExport = new Docman_PermissionsExport($params['project']);
461
            if ($request->get('show') == 'csv') {
462
                $permExport->toCSV();
463
            } else { // show = format
464
                $permExport->renderDefinitionFormat();
465
            }
466
            exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method project_export() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
467
        }
468
    }
469
470
    /**
471
     * Hook called when a project is being renamed
472
     * @param Array $params
473
     * @return Boolean
474
     */
475
    function renameProject($params) {
476
        $docmanPath = $this->getPluginInfo()->getPropertyValueForName('docman_root').'/';
0 ignored issues
show
The method getPropertyValueForName does only exist in ArchiveDeletedItemsPlugi...fo and DocmanPluginInfo, but not in AdminDelegationPluginInf...gileDashboardPluginInfo.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
477
        //Is this project using docman
478
        if (is_dir($docmanPath.$params['project']->getUnixName())){
479
            $version      = new Docman_VersionFactory();
480
481
            return $version->renameProject($docmanPath, $params['project'], $params['new_name']);
482
        }
483
484
        return true;
485
    }
486
    
487
    /**
488
     * Hook called before renaming project to check the name validity
489
     * @param Array $params
490
     */
491
    function file_exists_in_data_dir($params) {
492
        $docmanPath = $this->getPluginInfo()->getPropertyValueForName('docman_root').'/';
0 ignored issues
show
The method getPropertyValueForName does only exist in ArchiveDeletedItemsPlugi...fo and DocmanPluginInfo, but not in AdminDelegationPluginInf...gileDashboardPluginInfo.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
493
        $path = $docmanPath.$params['new_name'];
494
        
495
        if (Backend::fileExists($path)) {
496
            $params['result']= false;
497
            $params['error'] = $GLOBALS['Language']->getText('plugin_docman','name_validity');
498
        }
499
    }
500
501
    /**
502
     * Hook to know if docman is activated for the given project
503
     * it returns the root item of that project
504
     *
505
     * @param Array $params
506
     */
507
    function webdav_root_for_service($params) {
508
        $groupId = $params['project']->getId();
509
        if ($params['project']->usesService('docman')) {
510
            if (!isset($this->rootItems[$groupId])) {
511
                include_once 'Docman_ItemFactory.class.php';
512
                $docmanItemFactory = new Docman_ItemFactory();
513
                $this->rootItems[$groupId] = $docmanItemFactory->getRoot($groupId);
514
            }
515
            $params['roots']['docman'] = $this->rootItems[$groupId];
516
        }
517
    }
518
519
    /**
520
     * Hook to collect docman disk size usage per project
521
     *
522
     * @param array $params
523
     */
524
    function plugin_statistics_disk_usage_collect_project($params) {
525
        $row  = $params['project_row'];
526
        $root = $this->getPluginInfo()->getPropertyValueForName('docman_root');
0 ignored issues
show
The method getPropertyValueForName does only exist in ArchiveDeletedItemsPlugi...fo and DocmanPluginInfo, but not in AdminDelegationPluginInf...gileDashboardPluginInfo.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
527
        $path = $root.'/'.strtolower($row['unix_group_name']);
528
        $params['DiskUsageManager']->storeForGroup($row['group_id'], 'plugin_docman', $path);
529
    }
530
531
    /**
532
     * Hook to list docman in the list of serices managed by disk stats
533
     * 
534
     * @param array $params
535
     */
536
    function plugin_statistics_disk_usage_service_label($params) {
537
        $params['services']['plugin_docman'] = 'Docman';
538
    }
539
540
    /**
541
     * Hook to choose the color of the plugin in the graph
542
     * 
543
     * @param array $params
544
     */
545
    function plugin_statistics_color($params) {
546
        if ($params['service'] == 'plugin_docman') {
547
            $params['color'] = 'royalblue';
548
        }
549
    }
550
551
    /**
552
     * Hook to list pending documents and/or versions of documents in site admin page
553
     *
554
     * @param array $params
555
     */
556
    function show_pending_documents($params) {
557
        $request = HTTPRequest::instance();
558
        $limit = 25;
559
560
        //return all pending versions for given group id
561
        $offsetVers = $request->getValidated('offsetVers', 'uint', 0);
562
        if ( !$offsetVers || $offsetVers < 0 ) {
563
            $offsetVers = 0;
564
        }
565
        
566
        $linkToLogMsg = '<p>When an element is deleted, the action appears in <a href="/project/stats/source_code_access.php/?who=allusers&span=14&view=daily&group_id='.$params['group_id'].'">the access log</a>.</p>';
567
        
568
        require_once('Docman_VersionFactory.class.php');
569
        $version = new Docman_VersionFactory();
570
        $res = $version->listPendingVersions($params['group_id'], $offsetVers, $limit);
571
        $params['id'][] = 'version';
572
        $params['nom'][] = $GLOBALS['Language']->getText('plugin_docman','deleted_version');
573
        $html = '';
574
        $html .= '<div class="contenu_onglet" id="contenu_onglet_version">';
575
        $html .= $linkToLogMsg;
576
        if (isset($res) && $res) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $res 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...
577
            $html .= $this->showPendingVersions($res['versions'], $params['group_id'], $res['nbVersions'], $offsetVers, $limit);
578
        } else {
579
            $html .= 'No restorable versions found';
580
        }
581
        $html .='</div>';
582
        $params['html'][]= $html;
583
584
        //return all pending items for given group id
585
        $offsetItem = $request->getValidated('offsetItem', 'uint', 0);
586
        if ( !$offsetItem || $offsetItem < 0 ) {
587
            $offsetItem = 0;
588
        }
589
        require_once('Docman_ItemFactory.class.php');
590
        $item = new Docman_ItemFactory($params['group_id']);
591
        $res = $item->listPendingItems($params['group_id'], $offsetItem , $limit);
592
        $params['id'][] = 'item';
593
        $params['nom'][]= $GLOBALS['Language']->getText('plugin_docman','deleted_item');
594
        $html = '';
595
        $html .= '<div class="contenu_onglet" id="contenu_onglet_item">';
596
        $html .= $linkToLogMsg;
597
        if (isset($res) && $res) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $res 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...
598
            $html .= $this->showPendingItems($res['items'], $params['group_id'], $res['nbItems'], $offsetItem, $limit);
599
        } else {
600
            $html .= 'No restorable items found';
601
        }
602
        $html .='</div>';
603
        $params['html'][]= $html;
604
    }
605
606
    function showPendingVersions($versions, $groupId, $nbVersions, $offset, $limit) {
607
        $hp = Codendi_HTMLPurifier::instance();
608
609
        $html ='';
610
        $title =array();
611
        $title[] = $GLOBALS['Language']->getText('plugin_docman','item_id');
612
        $title[] = $GLOBALS['Language']->getText('plugin_docman','doc_title');
613
        $title[] = $GLOBALS['Language']->getText('plugin_docman','label');
614
        $title[] = $GLOBALS['Language']->getText('plugin_docman','number');
615
        $title[] = $GLOBALS['Language']->getText('plugin_docman','delete_date');
616
        $title[] = $GLOBALS['Language']->getText('plugin_docman','purge_date');
617
        $title[] = $GLOBALS['Language']->getText('plugin_docman','restore_version');
618
619
        if ($nbVersions > 0) {
620
621
            $html .= '<H3>'.$GLOBALS['Language']->getText('plugin_docman', 'deleted_version').'</H3><P>';
622
            $html .= html_build_list_table_top ($title);
623
            $i=1;
624
625
            foreach ($versions as $row) {
626
                $historyUrl = $this->getPluginPath().'/index.php?group_id='.$groupId.'&id='.$row['item_id'].'&action=details&section=history';
627
                $purgeDate = strtotime('+'.$GLOBALS['sys_file_deletion_delay'].' day', $row['date']);
628
                $html .= '<tr class="'. html_get_alt_row_color($i++) .'">'.
629
                '<td><a href="'.$historyUrl.'">'.$row['item_id'].'</a></td>'.
630
                '<td>'.$hp->purify($row['title'], CODENDI_PURIFIER_BASIC, $groupId).'</td>'.
631
                '<td>'.$hp->purify($row['label']).'</td>'.
632
                '<td>'.$row['number'].'</td>'.
633
                '<td>'.html_time_ago($row['date']).'</td>'.
634
                '<td>'.format_date($GLOBALS['Language']->getText('system', 'datefmt'), $purgeDate).'</td>'.
635
                '<td align="center"><a href="/plugins/docman/restore_documents.php?group_id='.$groupId.'&func=confirm_restore_version&id='.$row['id'].'&item_id='.$row['item_id'].'" ><IMG SRC="'.util_get_image_theme("ic/convert.png").'" onClick="return confirm(\'Confirm restore of this version\')" BORDER=0 HEIGHT=16 WIDTH=16></a></td></tr>';
636
            }
637
            $html .= '</TABLE>'; 
638
639
640
            $html .= '<div style="text-align:center" class="'. util_get_alt_row_color($i++) .'">';
641
642
            if ($offset > 0) {
643
                $html .=  '<a href="?group_id='.$groupId.'&focus=version&offsetVers='.($offset -$limit).'">[ '.$GLOBALS['Language']->getText('plugin_docman', 'previous').'  ]</a>';
644
                $html .= '&nbsp;';
645
            }
646
            if (($offset + $limit) < $nbVersions) {
647
                $html .= '&nbsp;';
648
                $html .='<a href="?group_id='.$groupId.'&focus=version&offsetVers='.($offset+$limit).'">[ '.$GLOBALS['Language']->getText('plugin_docman', 'next').' ]</a>';
649
            }
650
            $html .='<br>'.($offset+$i-2).'/'.$nbVersions.'</br>';
651
            $html .= '</div>';
652
          
653
        } else {
654
            $html .= $GLOBALS['Response']->addFeedback('info',$GLOBALS['Language']->getText('plugin_docman', 'no_pending_versions'));
655
        }
656
        return $html;
657
    }
658
659
    function showPendingItems($res, $groupId, $nbItems, $offset, $limit) {
660
        $hp = Codendi_HTMLPurifier::instance();
661
        require_once('Docman_ItemFactory.class.php');
662
        $itemFactory = new Docman_ItemFactory($groupId);
663
        $uh = UserHelper::instance();
664
665
        $html ='';
666
        $title =array();
667
        $title[] = $GLOBALS['Language']->getText('plugin_docman','item_id');
668
        $title[] = $GLOBALS['Language']->getText('plugin_docman','filters_item_type');
669
        $title[] = $GLOBALS['Language']->getText('plugin_docman','doc_title');
670
        $title[] = $GLOBALS['Language']->getText('plugin_docman','location');
671
        $title[] = $GLOBALS['Language']->getText('plugin_docman','owner');
672
        $title[] = $GLOBALS['Language']->getText('plugin_docman','delete_date');
673
        $title[] = $GLOBALS['Language']->getText('plugin_docman','purge_date');
674
        $title[] = $GLOBALS['Language']->getText('plugin_docman','restore_item');
675
676
677
        if ($nbItems > 0) {
678
            $html .= '<H3>'.$GLOBALS['Language']->getText('plugin_docman', 'deleted_item').'</H3><P>';
679
            $html .= html_build_list_table_top ($title);
680
            $i=1;
681
            foreach ($res as $row ){
682
                $purgeDate = strtotime('+'.$GLOBALS['sys_file_deletion_delay'].' day', $row['date']);
683
                $html .='<tr class="'. html_get_alt_row_color($i++) .'">'.
684
                '<td>'.$row['id'].'</td>'.
685
                '<td>'.$itemFactory->getItemTypeAsText($row['item_type']).'</td>'.
686
                '<td>'.$hp->purify($row['title'], CODENDI_PURIFIER_BASIC, $groupId).'</td>'.
687
                '<td>'.$hp->purify($row['location']).'</td>'.
688
                '<td>'.$hp->purify($uh->getDisplayNameFromUserId($row['user'])).'</td>'.
689
                '<td>'.html_time_ago($row['date']).'</td>'.
690
                '<td>'.format_date($GLOBALS['Language']->getText('system', 'datefmt'), $purgeDate).'</td>'.
691
                '<td align="center"><a href="/plugins/docman/restore_documents.php?group_id='.$groupId.'&func=confirm_restore_item&id='.$row['id'].'" ><IMG SRC="'.util_get_image_theme("ic/convert.png").'" onClick="return confirm(\'Confirm restore of this item\')" BORDER=0 HEIGHT=16 WIDTH=16></a></td></tr>';
692
            }
693
            $html .= '</TABLE>'; 
694
695
            $html .= '<div style="text-align:center" class="'. util_get_alt_row_color($i++) .'">';
696
697
            if ($offset > 0) {
698
                $html .=  '<a href="?group_id='.$groupId.'&focus=item&offsetItem='.($offset -$limit).'">[ '.$GLOBALS['Language']->getText('plugin_docman', 'previous').'  ]</a>';
699
                $html .= '&nbsp;';
700
            }
701
            if (($offset + $limit) < $nbItems) {
702
                $html .= '&nbsp;';
703
                $html .= '<a href="?group_id='.$groupId.'&focus=item&offsetItem='.($offset+$limit).'">[ '.$GLOBALS['Language']->getText('plugin_docman', 'next').' ]</a>';
704
            }
705
            $html .='<br>'.($offset +$i-2).'/'.$nbItems.'</br>';
706
            $html .= '</div>';
707
708
        } else {
709
            $html .= $GLOBALS['Response']->addFeedback('info',$GLOBALS['Language']->getText('plugin_docman', 'no_pending_items'));
710
        }
711
        return $html;
712
    }
713
714
    /**
715
     * Hook to purge deleted items if their agony ends today
716
     *
717
     * @param Array $params
718
     *
719
     * @return void
720
     */
721
    function purgeFiles(array $params) {
722
        require_once('Docman_ItemFactory.class.php');
723
        $itemFactory = new Docman_ItemFactory();
724
        $itemFactory->purgeDeletedItems($params['time']);
725
726
        require_once('Docman_VersionFactory.class.php');
727
        $versionFactory = new Docman_VersionFactory();
728
        $versionFactory->purgeDeletedVersions($params['time']);
729
    }
730
731
    /**
732
     * Function called when a project is deleted.
733
     * It Marks all project documents as deleted
734
     *
735
     * @param mixed $params ($param['group_id'] the ID of the deleted project)
736
     *
737
     * @return void
738
     */
739
        function project_is_deleted($params) {
740
            $groupId = $params['group_id'];
741
            if ($groupId) {
742
                require_once('Docman_ItemFactory.class.php');
743
                $docmanItemFactory = new Docman_ItemFactory();
744
                $docmanItemFactory->deleteProjectTree($groupId);
745
            }
746
        }
747
748
    /**
749
     * Function called when a user is removed from a project 
750
     * If a user is removed from a private project, the
751
     * documents monitored by that user should be monitored no more.
752
     *
753
     * @param array $params
754
     *
755
     * @return void
756
     */
757
    function projectRemoveUser($params) {
758
        $groupId = $params['group_id'];
759
        $userId = $params['user_id'];
760
761
        $project = $this->getProject($groupId);
762
        if (!$project->isPublic()) {
763
            require_once('Docman_ItemFactory.class.php');
764
            $docmanItemFactory = new Docman_ItemFactory();
765
            $root = $docmanItemFactory->getRoot($groupId);
766
            if ($root) {
767
                require_once('Docman_NotificationsManager.class.php');
768
                $notificationsManager = new Docman_NotificationsManager($project, null, null, $this->getMailBuilder());
769
                $dar = $notificationsManager->listAllMonitoredItems($groupId, $userId);
770
                if($dar && !$dar->isError()) {
771
                    foreach ($dar as $row) {
772
                        $notificationsManager->remove($row['user_id'], $row['object_id'], $row['type']);
773
                    }
774
                }
775
            }
776
        }
777
    }
778
779
    /**
780
     * Function called when a project goes from public to private so
781
     * documents monitored by non member users should be monitored no more.
782
     *
783
     * @param array $params
784
     *
785
     * @return void
786
     */
787
    function projectIsPrivate($params) {
788
        $groupId = $params['group_id'];
789
        $private = $params['project_is_private'];
790
791
        if ($private) {
792
            require_once('Docman_ItemFactory.class.php');
793
            $docmanItemFactory = new Docman_ItemFactory();
794
            $root = $docmanItemFactory->getRoot($groupId);
795
            if ($root) {
796
                require_once('Docman_NotificationsManager.class.php');
797
                $notificationsManager = new Docman_NotificationsManager($this->getProject($groupId), null, null, $this->getMailBuilder());
798
                $dar = $notificationsManager->listAllMonitoredItems($groupId);
799
                if($dar && !$dar->isError()) {
800
                    $userManager = UserManager::instance();
801
                    $user = null;
802
                    foreach ($dar as $row) {
803
                        $user = $userManager->getUserById($row['user_id']);
804
                        if (!$user->isMember($groupId)) {
805
                            $notificationsManager->remove($row['user_id'], $row['object_id'], $row['type']);
806
                        }
807
                    }
808
                }
809
            }
810
        }
811
    }
812
813
    /**
814
     * Display information about admin delegation
815
     *
816
     * @return void
817
     */
818
    function permissionRequestInformation($params) {
819
        echo "<p><h2>".$GLOBALS['Language']->getText('plugin_docman', 'permission_requests')."</h2>".$GLOBALS['Language']->getText('plugin_docman', 'permission_requests_information')."</p>";
820
    }
821
822
    /**
823
     * Fill the list of subEvents related to docman in the project history interface
824
     *
825
     */
826
    function fillProjectHistorySubEvents($params) {
827
        array_push($params['subEvents']['event_permission'], 'perm_reset_for_document',
828
                                                             'perm_granted_for_document',
829
                                                             'perm_reset_for_folder',
830
                                                             'perm_granted_for_folder'
831
        );
832
    }
833
834
    protected function getWikiController($request) {
835
        return $this->getController('Docman_WikiController', $request);
836
    }
837
838
    protected function getHTTPController($request=null) {
839
        if ($request == null) {
840
            $request = HTTPRequest::instance();
841
        }
842
        return $this->getController('Docman_HTTPController', $request);
843
    }
844
    
845
    protected function getCoreController($request) {
846
        return $this->getController('Docman_CoreController', $request);
847
    }
848
    
849
    protected function getSOAPController($request) {
850
        return $this->getController('Docman_SOAPController', $request);
851
    }
852
853
    protected function getController($controller, $request) {
854
        if (!isset($this->controller[$controller])) {
855
            include_once $controller.'.class.php';
856
            $this->controller[$controller] = new $controller($this, $this->getPluginPath(), $this->getThemePath(), $request);
857
        } else {
858
            $this->controller[$controller]->setRequest($request);
859
        }
860
        return $this->controller[$controller];
861
    }
862
863
    /**
864
     * Append scripts to the combined JS file
865
     *
866
     * @param Array $params parameters of the hook
867
     *
868
     * @return Void
869
     */
870
    public function combinedScripts($params) {
871
        $params['scripts'] = array_merge($params['scripts'], array($this->getPluginPath().'/scripts/ApprovalTableReminder.js'));
872
    }
873
874
    public function fulltextsearch_event_fetch_all_document_search_types($params) {
875
        $params['all_document_search_types'][] = array(
876
            'key'     => 'docman',
877
            'name'    => $GLOBALS['Language']->getText('plugin_docman', 'search_type'),
878
            'info'    => false,
879
            'can_use' => true,
880
            'special' => false,
881
        );
882
    }
883
884
    public function fulltextsearch_event_does_docman_service_use_ugroup($params) {
885
        $manager   = Docman_PermissionsManager::instance($params['project_id']);
886
        $ugroup_id = $params['ugroup_id'];
887
888
        $params['is_used'] = $manager->isUgroupUsed($ugroup_id);
889
    }
890
891
    public function proccess_system_check($params) {
892
        $docman_system_check = new Docman_SystemCheck(
893
            $this,
894
            new Docman_SystemCheckProjectRetriever(new Docman_SystemCheckDao()),
895
            BackendSystem::instance(),
896
            $params['logger']
897
        );
898
899
        $docman_system_check->process();
900
    }
901
902
    public function services_truncated_emails($params) {
903
        $project = $params['project'];
904
        if ($project->usesService('docman')) {
905
            $params['services'][] = $GLOBALS['Language']->getText('plugin_docman', 'service_lbl_key');
906
        }
907
    }
908
909
    /**
910
     * @return Project
911
     */
912
    private function getProject($group_id) {
913
        return ProjectManager::instance()->getProject($group_id);
914
    }
915
916
    /**
917
     * @return MailBuilder
918
     */
919
    private function getMailBuilder() {
920
        return new MailBuilder(TemplateRendererFactory::build());
921
    }
922
923
    public function get_reference($params) {
924
        $keyword       = $params['keyword'];
925
        $reference_row = $this->getSystemDocmanReferenceByKeyword($keyword);
926
927
        if ($reference_row) {
928
            $docman_element_id   = $params['value'];
929
            $docman_item_factory = new Docman_ItemFactory();
930
            $reference_factory   = new Docman_ReferenceFactory();
931
932
            $docman_item = $docman_item_factory->getItemFromDb($docman_element_id);
933
934
            if ($docman_item) {
935
                $reference = $reference_factory->getInstanceFromRowAndProjectId(
936
                    $reference_row,
937
                    $docman_item->getGroupId()
938
                );
939
940
                $params['reference'] = $reference;
941
            }
942
        }
943
    }
944
945
    private function getSystemDocmanReferenceByKeyword($keyword) {
946
        $dao    = new ReferenceDao();
947
        $result = $dao->searchSystemReferenceByNatureAndKeyword($keyword, self::SYSTEM_NATURE_NAME);
948
949
        if (! $result || $result->rowCount() < 1) {
950
            return null;
951
        }
952
953
        return $result->getRow();
954
    }
955
}
956