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.

include/fulltextsearchPlugin.class.php (1 issue)

Labels
Severity

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) Enalean, 2012 - 2014. All Rights Reserved.
4
 *
5
 * This file is a part of Tuleap.
6
 *
7
 * Tuleap is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * Tuleap is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with Tuleap. If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
require_once 'common/plugin/Plugin.class.php';
22
require_once 'autoload.php';
23
require_once 'constants.php';
24
25
class fulltextsearchPlugin extends Plugin {
26
27
    const SEARCH_TYPE         = 'fulltext';
28
    const SEARCH_DOCMAN_TYPE  = 'docman';
29
    const SEARCH_WIKI_TYPE    = 'wiki';
30
    const SEARCH_TRACKER_TYPE = 'tracker';
31
    const SEARCH_DEFAULT      = '';
32
33
    private $allowed_system_events = array(
34
        SystemEvent_FULLTEXTSEARCH_DOCMAN_INDEX::NAME,
35
        SystemEvent_FULLTEXTSEARCH_DOCMAN_EMPTY_INDEX::NAME,
36
        SystemEvent_FULLTEXTSEARCH_DOCMAN_LINK_INDEX::NAME,
37
        SystemEvent_FULLTEXTSEARCH_DOCMAN_FOLDER_INDEX::NAME,
38
        SystemEvent_FULLTEXTSEARCH_DOCMAN_REINDEX_PROJECT::NAME,
39
        SystemEvent_FULLTEXTSEARCH_DOCMAN_COPY::NAME,
40
        SystemEvent_FULLTEXTSEARCH_DOCMAN_UPDATE::NAME,
41
        SystemEvent_FULLTEXTSEARCH_DOCMAN_UPDATE_PERMISSIONS::NAME,
42
        SystemEvent_FULLTEXTSEARCH_DOCMAN_UPDATE_METADATA::NAME,
43
        SystemEvent_FULLTEXTSEARCH_DOCMAN_DELETE::NAME,
44
        SystemEvent_FULLTEXTSEARCH_DOCMAN_APPROVAL_TABLE_COMMENT::NAME,
45
        SystemEvent_FULLTEXTSEARCH_DOCMAN_WIKI_INDEX::NAME,
46
        SystemEvent_FULLTEXTSEARCH_DOCMAN_WIKI_UPDATE::NAME,
47
        SystemEvent_FULLTEXTSEARCH_DOCMAN_UPDATELINK::NAME,
48
        SystemEvent_FULLTEXTSEARCH_TRACKER_ARTIFACT_UPDATE::NAME,
49
        SystemEvent_FULLTEXTSEARCH_TRACKER_REINDEX_PROJECT::NAME,
50
        SystemEvent_FULLTEXTSEARCH_TRACKER_ARTIFACT_DELETE::NAME,
51
        SystemEvent_FULLTEXTSEARCH_TRACKER_TRACKER_DELETE::NAME,
52
        SystemEvent_FULLTEXTSEARCH_TRACKER_REINDEX::NAME,
53
        SystemEvent_FULLTEXTSEARCH_WIKI_INDEX::NAME,
54
        SystemEvent_FULLTEXTSEARCH_WIKI_UPDATE::NAME,
55
        SystemEvent_FULLTEXTSEARCH_WIKI_UPDATE_PERMISSIONS::NAME,
56
        SystemEvent_FULLTEXTSEARCH_WIKI_UPDATE_SERVICE_PERMISSIONS::NAME,
57
        SystemEvent_FULLTEXTSEARCH_WIKI_DELETE::NAME,
58
        SystemEvent_FULLTEXTSEARCH_WIKI_REINDEX_PROJECT::NAME,
59
    );
60
61
    public function __construct($id) {
62
        parent::__construct($id);
63
        $this->setScope(Plugin::SCOPE_PROJECT);
64
        $this->allowed_for_project = array();
65
    }
66
67
    public function getHooksAndCallbacks() {
68
        // docman
69
        if ($this->isDocmanPluginActivated()) {
70
            $this->addHook('plugin_docman_after_new_document');
71
            $this->addHook('plugin_docman_event_del');
72
            $this->addHook('plugin_docman_event_update');
73
            $this->addHook('plugin_docman_event_perms_change');
74
            $this->addHook('plugin_docman_event_new_version');
75
            $this->addHook('plugin_docman_event_new_wikipage');
76
            $this->addHook('plugin_docman_event_wikipage_update');
77
            $this->addHook('plugin_docman_event_move');
78
            $this->addHook(PLUGIN_DOCMAN_EVENT_COPY);
79
            $this->addHook(PLUGIN_DOCMAN_EVENT_APPROVAL_TABLE_COMMENT);
80
            $this->addHook(PLUGIN_DOCMAN_EVENT_NEW_EMPTY);
81
            $this->addHook(PLUGIN_DOCMAN_EVENT_NEW_LINK);
82
            $this->addHook(PLUGIN_DOCMAN_EVENT_NEW_FOLDER);
83
            $this->addHook(PLUGIN_DOCMAN_EVENT_NEW_LINKVERSION);
84
        }
85
        // tracker
86
        if (defined('TRACKER_BASE_URL')) {
87
            $this->_addHook(TRACKER_EVENT_REPORT_DISPLAY_ADDITIONAL_CRITERIA);
88
            $this->_addHook(TRACKER_EVENT_REPORT_PROCESS_ADDITIONAL_QUERY);
89
            $this->_addHook(TRACKER_EVENT_ARTIFACT_POST_UPDATE);
90
            $this->_addHook(TRACKER_EVENT_ARTIFACT_DELETE);
91
            $this->_addHook(TRACKER_EVENT_TRACKER_DELETE);
92
            $this->_addHook(TRACKER_EVENT_TRACKER_PERMISSIONS_CHANGE);
93
            $this->_addHook(TRACKER_EVENT_SEMANTIC_CONTRIBUTOR_CHANGE);
94
            $this->_addHook('tracker_followup_event_update', 'tracker_event_artifact_post_update', false);
95
            $this->_addHook('tracker_report_followup_warning', 'tracker_report_followup_warning', false);
96
        }
97
98
        // site admin
99
        $this->_addHook('site_admin_option_hook', 'site_admin_option_hook', false);
100
        $this->_addHook('project_is_private', 'project_is_private', false);
101
102
        // wiki
103
        $this->_addHook('wiki_page_updated', 'wiki_page_updated', false);
104
        $this->_addHook('wiki_page_created', 'wiki_page_created', false);
105
        $this->_addHook('wiki_page_deleted', 'wiki_page_deleted', false);
106
        $this->_addHook('wiki_page_permissions_updated', 'wiki_page_permissions_updated', false);
107
        $this->_addHook('wiki_service_permissions_updated', 'wiki_service_permissions_updated', false);
108
109
        // assets
110
        $this->_addHook('cssfile', 'cssfile', false);
111
        $this->_addHook(Event::COMBINED_SCRIPTS, 'combined_scripts', false);
112
        $this->_addHook('javascript_file');
113
114
        // system events
115
        $this->_addHook(Event::GET_SYSTEM_EVENT_CLASS, 'get_system_event_class', false);
116
        $this->_addHook(Event::SYSTEM_EVENT_GET_CUSTOM_QUEUES);
117
        $this->_addHook(Event::SYSTEM_EVENT_GET_TYPES_FOR_CUSTOM_QUEUE);
118
119
        // Search
120
        $this->addHook(Event::LAYOUT_SEARCH_ENTRY);
121
        $this->addHook(Event::PLUGINS_POWERED_SEARCH);
122
        $this->addHook(Event::SEARCH_TYPE, 'search_type');
123
        $this->addHook(Event::FETCH_ADDITIONAL_SEARCH_TABS);
124
        $this->addHook(Event::REDEFINE_SEARCH_TYPE);
125
126
        //Ugroups
127
        $this->_addHook('project_admin_ugroup_remove_user', 'project_admin_ugroup_delete_or_remove_user');
128
        $this->_addHook('project_admin_ugroup_deletion', 'project_admin_ugroup_delete_or_remove_user');
129
        $this->_addHook(Event::UGROUP_MANAGER_UPDATE_UGROUP_BINDING_ADD, 'ugroup_manager_update_ugroup_binding_change');
130
        $this->_addHook(Event::UGROUP_MANAGER_UPDATE_UGROUP_BINDING_REMOVE, 'ugroup_manager_update_ugroup_binding_change');
131
        $this->_addHook('project_admin_remove_user_from_project_ugroups', 'eventChangeProjectUgroupsMembers');
132
        $this->_addHook('project_admin_ugroup_add_user', 'eventChangeProjectUgroupsMembers');
133
134
        $this->_addHook('project_admin_remove_user', 'project_admin_remove_user');
135
        $this->_addHook('project_admin_change_user_permissions', 'project_admin_change_user_permissions');
136
137
        return parent::getHooksAndCallbacks();
138
    }
139
140
    private function isDocmanPluginActivated() {
141
        return defined('PLUGIN_DOCMAN_EVENT_APPROVAL_TABLE_COMMENT');
142
    }
143
144
    private function getCurrentUser() {
145
        return UserManager::instance()->getCurrentUser();
146
    }
147
148
    public function layout_search_entry($params) {
149
        if ($this->getCurrentUser()->useLabFeatures()) {
150
            $params['search_entries'][] = array(
151
                'value'    => self::SEARCH_TYPE,
152
                'label'    => 'Fulltext',
153
                'selected' => $params['type_of_search'] == self::SEARCH_TYPE,
154
            );
155
        }
156
    }
157
158
    public function plugins_powered_search($params) {
159
        if ($this->getCurrentUser()->useLabFeatures() && $params['type_of_search'] === self::SEARCH_TYPE) {
160
            $params['plugins_powered_search'] = true;
161
        }
162
    }
163
164
    public function fetch_additional_search_tabs($params) {
165
        if ($this->getCurrentUser()->useLabFeatures()) {
166
            $params['additional_search_tabs'][] = new Search_AdditionalSearchTabsPresenter(
167
                '<i class="icon-beaker"></i> ' . $GLOBALS['Language']->getText('plugin_fulltextsearch', 'additional_search_tab'),
168
                '/search?type_of_search=' . self::SEARCH_TYPE,
169
                self::SEARCH_TYPE
170
            );
171
        }
172
    }
173
174
    public function search_type($params) {
175
        $type_of_search = $params['query']->getTypeOfSearch();
176
177
        if ($type_of_search !== self::SEARCH_TYPE) {
178
            return;
179
        }
180
181
        try {
182
            $search_controller = $this->getSearchController();
183
        } catch (ElasticSearch_ClientNotFoundException $exception) {
184
            $search_error_controller = $this->getSearchErrorController();
185
            $search_error_controller->clientNotFound($params);
186
            return;
187
        }
188
189
        $search_controller->siteSearch($params);
190
    }
191
192
    private function getDocmanSystemEventManager() {
193
        return new FullTextSearch_DocmanSystemEventManager(
194
            SystemEventManager::instance(),
195
            $this->getIndexClient(self::SEARCH_DOCMAN_TYPE),
196
            $this,
197
            $this->getFullTextSearchSystemEventQueue()->getLogger()
198
        );
199
    }
200
201
    private function getWikiSystemEventManager() {
202
        return new FullTextSearch_WikiSystemEventManager(
203
            SystemEventManager::instance(),
204
            $this->getIndexClient(self::SEARCH_WIKI_TYPE),
205
            $this,
206
            $this->getFullTextSearchSystemEventQueue()->getLogger()
207
        );
208
    }
209
210
    private function getTrackerSystemEventManager() {
211
        $form_element_factory   = Tracker_FormElementFactory::instance();
212
        $permissions_serializer = new Tracker_Permission_PermissionsSerializer(
213
            new Tracker_Permission_PermissionRetrieveAssignee(UserManager::instance())
214
        );
215
216
        $artifact_properties_extractor = new ElasticSearch_1_2_ArtifactPropertiesExtractor(
217
            $form_element_factory,
218
            $permissions_serializer
219
        );
220
221
        return new FullTextSearch_TrackerSystemEventManager(
222
            SystemEventManager::instance(),
223
            new FullTextSearchTrackerActions(
224
                $this->getIndexClient(self::SEARCH_TRACKER_TYPE),
225
                new ElasticSearch_1_2_RequestTrackerDataFactory(
226
                    $artifact_properties_extractor
227
                ),
228
                $this->getFullTextSearchSystemEventQueue()->getLogger()
229
            ),
230
            Tracker_ArtifactFactory::instance(),
231
            TrackerFactory::instance(),
232
            $this,
233
            $this->getFullTextSearchSystemEventQueue()->getLogger()
234
        );
235
    }
236
237
    /** @see Event::SYSTEM_EVENT_GET_CUSTOM_QUEUES */
238
    public function system_event_get_custom_queues(array $params) {
239
        $params['queues'][FullTextSearchSystemEventQueue::NAME] = $this->getFullTextSearchSystemEventQueue();
240
    }
241
242
    private function getFullTextSearchSystemEventQueue() {
243
        return new FullTextSearchSystemEventQueue();
244
    }
245
246
    /** @see Event::SYSTEM_EVENT_GET_TYPES_FOR_CUSTOM_QUEUE */
247
    public function system_event_get_types_for_custom_queue(array &$params) {
248
        if ($params['queue'] !== FullTextSearchSystemEventQueue::NAME) {
249
            return;
250
        }
251
252
        $params['types'] = array_merge(
253
            $params['types'],
254
            $this->allowed_system_events
255
        );
256
    }
257
258
    /**
259
     * This callback make SystemEvent manager knows about fulltext plugin System Events
260
     */
261
    public function get_system_event_class($params) {
262
        $providers = array(
263
            $this->getDocmanSystemEventManager(),
264
            $this->getWikiSystemEventManager(),
265
            $this->getTrackerSystemEventManager(),
266
        );
267
        $i = 0;
268
269
        if (! in_array($params['type'], $this->allowed_system_events)) {
270
            return;
271
        }
272
273
        do {
274
            $providers[$i]->getSystemEventClass(
275
                $params['type'],
276
                $params['class'],
277
                $params['dependencies']
278
            );
279
            $i++;
280
        } while ($i < count($providers) || !$params['class']);
281
    }
282
283
    /**
284
     * Return true if given project has the right to use this plugin.
285
     *
286
     * @param string $group_id
287
     *
288
     * @return bool
289
     */
290
    public function isAllowed($group_id) {
291
        if(! isset($this->allowedForProject[$group_id])) {
292
            $this->allowed_for_project[$group_id] = PluginManager::instance()->isPluginAllowedForProject($this, $group_id);
293
        }
294
        return $this->allowed_for_project[$group_id];
295
    }
296
297
    public function wiki_page_updated($params) {
298
        if (! $this->isDocmanPluginActivated() || ! $params['referenced']) {
299
            $this->getWikiSystemEventManager()->queueUpdateWikiPage($params);
300
        }
301
    }
302
303
    public function wiki_page_created($params) {
304
        $this->getWikiSystemEventManager()->queueIndexWikiPage($params);
305
    }
306
307
    public function wiki_page_deleted($params) {
308
        $this->getWikiSystemEventManager()->queueDeleteWikiPage($params);
309
    }
310
311
    public function wiki_page_permissions_updated($params) {
312
        $this->getWikiSystemEventManager()->queueUpdateWikiPagePermissions($params);
313
    }
314
315
    public function wiki_service_permissions_updated($params) {
316
        $this->getWikiSystemEventManager()->queueUpdateWikiServicePermissions($params);
317
    }
318
319
    /**
320
     * Event triggered when a document is created
321
     *
322
     * @param array $params
323
     */
324
    public function plugin_docman_after_new_document($params) {
325
        $this->getDocmanSystemEventManager()->queueNewDocument($params['item'], $params['version']);
326
    }
327
328
    /**
329
     * Event triggered when a new empty document is created
330
     *
331
     * @param array $params
332
     */
333
    public function plugin_docman_event_new_empty($params) {
334
        $this->getDocmanSystemEventManager()->queueNewEmptyDocument($params['item']);
335
    }
336
337
    /**
338
     * Event triggered when a new link document is created
339
     *
340
     * @param array $params
341
     */
342
    public function plugin_docman_event_new_link($params) {
343
        $this->getDocmanSystemEventManager()->queueNewLinkDocument($params['item']);
344
    }
345
346
    /**
347
     * Event triggered when a new folder is created
348
     *
349
     * @param array $params
350
     */
351
    public function plugin_docman_event_new_folder($params) {
352
        $this->getDocmanSystemEventManager()->queueNewDocmanFolder($params['item']);
353
    }
354
355
    /**
356
     * Event triggered when a document is updated
357
     *
358
     * @param array $params
359
     */
360
    public function plugin_docman_event_update($params) {
361
        $this->getDocmanSystemEventManager()->queueUpdateMetadata($params['item']);
362
    }
363
364
    /**
365
     * Event triggered when a document is moved
366
     *
367
     * @param array $params
368
     */
369
    public function plugin_docman_event_move($params) {
370
        $this->getDocmanSystemEventManager()->queueUpdateDocumentPermissions($params['item']);
371
    }
372
373
    /**
374
     * Event triggered when a document is copied
375
     *
376
     * @param array $params
377
     */
378
    public function plugin_docman_event_copy($params) {
379
        $this->getDocmanSystemEventManager()->queueCopyDocument($params['item']);
380
    }
381
382
    /**
383
     * Event triggered when a document is deleted
384
     *
385
     * @param array $params
386
     */
387
    public function plugin_docman_event_del($params) {
388
        $this->getDocmanSystemEventManager()->queueDeleteDocument($params['item']);
389
    }
390
391
    /**
392
     * Event triggered when the permissions on a document change
393
     */
394
    public function plugin_docman_event_perms_change($params) {
395
        $this->getDocmanSystemEventManager()->queueUpdateDocumentPermissions($params['item']);
396
    }
397
398
    /**
399
     * Event triggered when the permissions on a document version update
400
     */
401
    public function plugin_docman_event_new_version($params) {
402
        $this->getDocmanSystemEventManager()->queueNewDocumentVersion($params['item'], $params['version']);
403
    }
404
405
    /**
406
     * Event triggered when the permissions on a document version update
407
     */
408
    public function plugin_docman_event_new_linkVersion($params) {
409
        $this->getDocmanSystemEventManager()->queueNewDocumentLinkVersion($params['item'], $params['version']);
410
    }
411
412
    /**
413
     * Event triggered when a wiki document is updated
414
     */
415
    public function plugin_docman_event_new_wikipage($params) {
416
        $this->getWikiSystemEventManager()->queueDeleteWikiPage(array(
417
            'group_id'  => $params['group_id'],
418
            'wiki_page' => $params['wiki_page']
419
        ));
420
421
        $this->getDocmanSystemEventManager()->queueNewWikiDocument($params['item']);
422
    }
423
424
    /**
425
     * Event triggered when a wiki document is updated
426
     */
427
    public function plugin_docman_event_wikipage_update($params) {
428
        $this->getDocmanSystemEventManager()->queueNewWikiDocumentVersion($params['item']);
429
    }
430
431
    /**
432
     * @see PLUGIN_DOCMAN_EVENT_APPROVAL_TABLE_COMMENT
433
     * @param array $params
434
     */
435
    public function plugin_docman_approval_table_comment(array $params) {
436
        $this->getDocmanSystemEventManager()->queueNewApprovalTableComment($params['item']);
437
    }
438
439
    /**
440
     * Index added followup comment
441
     *
442
     * @param Array $params Hook params
443
     * @see TRACKER_EVENT_ARTIFACT_POST_UPDATE
444
     * @return Void
445
     */
446
    public function tracker_event_artifact_post_update($params) {
447
        $this->getTrackerSystemEventManager()->queueArtifactUpdate($params['artifact']);
448
    }
449
450
    public function tracker_event_artifact_delete($params) {
451
        $this->getTrackerSystemEventManager()->queueArtifactDelete($params['artifact']);
452
    }
453
454
    public function tracker_event_tracker_delete($params) {
455
        $this->getTrackerSystemEventManager()->queueTrackerDelete($params['tracker']);
456
    }
457
458
    /**
459
     * Display search form in tracker followup
460
     *
461
     * @param Array $params Hook params
462
     *
463
     * @return Void
464
     */
465
    public function tracker_event_report_display_additional_criteria($params) {
466
        if (! $params['tracker']) {
467
            return;
468
        }
469
470
        if ($this->tracker_followup_check_preconditions($params['tracker']->getGroupId())) {
471
            $request = HTTPRequest::instance();
472
            $hp      = Codendi_HTMLPurifier::instance();
473
            $filter  = $hp->purify($request->getValidated('search_fulltext', 'string', ''));
474
475
            $additional_criteria  = '';
476
            $additional_criteria .= '<label title="'.$GLOBALS['Language']->getText('plugin_fulltextsearch', 'global_search_tooltip').'" for="tracker_report_crit_followup_search">';
477
            $additional_criteria .= '<i class="icon-beaker"></i> '. $GLOBALS['Language']->getText('plugin_fulltextsearch', 'global_search_label');
478
            $additional_criteria .= '</label>';
479
            $additional_criteria .= '<input id="tracker_report_crit_followup_search" type="text" name="search_fulltext" value="'.$filter.'" />';
480
481
            $params['array_of_html_criteria'][] = $additional_criteria;
482
        }
483
    }
484
485
    /**
486
     * Process warning display for tracker followup search
487
     *
488
     * @param Array $params Hook params
489
     *
490
     * @return Void
491
     */
492
    public function tracker_report_followup_warning($params) {
493
        if ($this->tracker_followup_check_preconditions($params['group_id'])) {
494
            if ($params['request']->get('search_fulltext')) {
495
                $params['html'] .= '<div id="tracker_report_selection" class="tracker_report_haschanged_and_isobsolete report_warning">';
496
                $params['html'] .= $GLOBALS['HTML']->getimage('ic/warning.png', array('style' => 'vertical-align:top;'));
497
                $params['html'] .= $GLOBALS['Language']->getText('plugin_fulltextsearch', 'followup_full_text_warning_search');
498
                $params['html'] .= '</div>';
499
            }
500
        }
501
    }
502
503
    /**
504
     * This method check preconditions to use fulltext:
505
     * Elastic Search is available, the plugin is enabled for this project and the user is enabling mode Lab.
506
     *
507
     * @param Integer $group_id Project Id
508
     *
509
     * @return Boolean
510
     */
511
    private function tracker_followup_check_preconditions($group_id) {
512
        try {
513
            $index_status = $this->getAdminController()->getIndexStatus();
514
        } catch (ElasticSearchTransportHTTPException $e) {
0 ignored issues
show
The class ElasticSearchTransportHTTPException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
515
            return false;
516
        } catch (ElasticSearch_ClientNotFoundException $exception) {
517
            return false;
518
        }
519
520
        return $this->isAllowed($group_id) && $this->getCurrentUser()->useLabFeatures();
521
    }
522
523
    /**
524
     * Process search in tracker followup
525
     *
526
     * @param Array $params Hook params
527
     *
528
     * @return Void
529
     */
530
    public function tracker_event_report_process_additional_query($params) {
531
        if (! $params['request'] || ! $params['tracker'] || ! $params['user'] || ! $params['form_element_factory']) {
532
            return;
533
        }
534
535
        $group_id = $params['tracker']->getGroupId();
536
        if (! $this->tracker_followup_check_preconditions($group_id)) {
537
            return;
538
        }
539
540
        $filter = $params['request']->get('search_fulltext');
541
        if (empty($filter)) {
542
            return;
543
        }
544
545
        $field_names = array();
546
        $formElementFactory = $params['form_element_factory'];
547
        $fields = $formElementFactory->getUsedTextFieldsUserCanRead($params['tracker'], $params['user']);
548
        foreach ($fields as $field) {
549
            $field_names[] = $field->getName();
550
        }
551
552
        $field_names[] = ElasticSearch_1_2_RequestTrackerDataFactory::COMMENT_FIELD_NAME;
553
554
        try {
555
            $controller         = $this->getSearchController('tracker');
556
            $params['result'][] = $controller->searchSpecial($field_names, $params['request']);
557
            $params['search_performed'] = true;
558
        } catch (ElasticSearch_ClientNotFoundException $e) {
559
            // do nothing
560
        }
561
    }
562
563
    /**
564
     * Event to display something in siteadmin interface
565
     *
566
     * @param array $params
567
     */
568
    public function site_admin_option_hook($params) {
569
        echo '<li><a href="'.$this->getPluginPath().'/">Full Text Search</a></li>';
570
    }
571
572
    /**
573
     * Event to load css stylesheet
574
     *
575
     * @param array $params
576
     */
577
    public function cssfile($params) {
578
        if ($this->canIncludeAssets()) {
579
            echo '<link rel="stylesheet" type="text/css" href="'.$this->getThemePath().'/css/style.css" />';
580
        }
581
    }
582
583
    function combined_scripts($params) {
584
        $params['scripts'] = array_merge(
585
            $params['scripts'],
586
            array(
587
                $this->getPluginPath().'/scripts/full-text-search.js',
588
            )
589
        );
590
    }
591
592
    public function javascript_file() {
593
        if ($this->isAdminPage()) {
594
            echo '<script type="text/javascript" src="' . $this->getPluginPath() . '//scripts/admin-load-project-autocompleter.js"></script>';
595
        }
596
    }
597
598
    private function isAdminPage() {
599
        return strpos($_SERVER['REQUEST_URI'], $this->getPluginPath()) === 0;
600
    }
601
602
    private function canIncludeAssets() {
603
        return strpos($_SERVER['REQUEST_URI'], $this->getPluginPath()) === 0 ||
604
            strpos($_SERVER['REQUEST_URI'], '/search/') === 0;
605
    }
606
607
    /**
608
     * Obtain index client
609
     *
610
     * @param String $index Type of the index
611
     *
612
     * @return ElasticSearch_IndexClientFacade
613
     */
614
    private function getIndexClient($index) {
615
        $factory = $this->getClientFactory();
616
        $type    = '';
617
618
        return $factory->buildIndexClient($index, $type);
619
    }
620
621
    /**
622
     * Obtain search client
623
     *
624
     * @param String $index Type of items to search (e.g. docman, wiki, '', ...)
625
     *
626
     * @return ElasticSearch_SearchClientFacade
627
     */
628
    private function getSearchClient($index, $type = '') {
629
        $factory = $this->getClientFactory();
630
631
        return $factory->buildSearchClient($index, $type);
632
    }
633
634
    private function getSearchAdminClient() {
635
        $factory = $this->getClientFactory();
636
637
        return $factory->buildSearchAdminClient();
638
    }
639
640
    private function getClientFactory() {
641
        return new ElasticSearch_ClientFactory(
642
            $this->getElasticSearchConfig(),
643
            $this->getProjectManager()
644
        );
645
    }
646
647
    private function getElasticSearchConfig() {
648
        return new ElasticSearch_ClientConfig(
649
            $this->getPluginInfo()->getPropertyValueForName('fulltextsearch_path'),
650
            $this->getPluginInfo()->getPropertyValueForName('fulltextsearch_host'),
651
            $this->getPluginInfo()->getPropertyValueForName('fulltextsearch_port'),
652
            $this->getPluginInfo()->getPropertyValueForName('fulltextsearch_user'),
653
            $this->getPluginInfo()->getPropertyValueForName('fulltextsearch_password'),
654
            $this->getPluginInfo()->getPropertyValueForName('max_seconds_for_request')
655
        );
656
    }
657
658
    /**
659
     * @return FulltextsearchPluginInfo
660
     */
661
    public function getPluginInfo() {
662
        if (!is_a($this->pluginInfo, 'FulltextsearchPluginInfo')) {
663
            $this->pluginInfo = new FulltextsearchPluginInfo($this);
664
        }
665
        return $this->pluginInfo;
666
    }
667
668
    /**
669
     * @throws ElasticSearch_ClientNotFoundException
670
     */
671
    private function getSearchController($index = self::SEARCH_DEFAULT, $type = '') {
672
        return new FullTextSearch_Controller_Search($this->getRequest(), $this->getSearchClient($index, $type));
673
    }
674
675
    private function getSearchErrorController() {
676
        return new FullTextSearch_Controller_SearchError($this->getRequest());
677
    }
678
679
    private function getAdminController() {
680
        return new FullTextSearch_Controller_Admin(
681
            $this->getRequest(),
682
            $this->getSearchAdminClient(),
683
            $this->getDocmanSystemEventManager(),
684
            $this->getWikiSystemEventManager(),
685
            $this->getTrackerSystemEventManager()
686
        );
687
    }
688
689
    private function getProjectManager() {
690
        return ProjectManager::instance();
691
    }
692
693
    private function getRequest() {
694
        return HTTPRequest::instance();
695
    }
696
697
    public function process() {
698
        $request = $this->getRequest();
699
        // Grant access only to site admin
700
        if (!$request->getCurrentUser()->isSuperUser()) {
701
            header('Location: ' . get_server_url());
702
        }
703
704
        $project_name_from_autocomplete = $request->get('fts_project');
705
        $project_manager = $this->getProjectManager();
706
        $project = $project_manager->getProjectFromAutocompleter($project_name_from_autocomplete);
707
708
        if ($project) {
709
            $this->reindexAll($project->getId());
710
711
            $GLOBALS['Response']->addFeedback(
712
                'info',
713
                $GLOBALS['Language']->getText(
714
                    'plugin_fulltextsearch',
715
                    'waiting_for_reindexation',
716
                    array(util_unconvert_htmlspecialchars($project->getPublicName()))
717
                )
718
            );
719
        }
720
721
        $this->redirectToIndex();
722
    }
723
724
    private function redirectToIndex() {
725
        $this->getAdminController()->index();
726
    }
727
728
    public function project_is_private($params) {
729
        $project_id = $params['group_id'];
730
731
        $this->reindexAll($project_id);
732
    }
733
734
    /** @see TRACKER_EVENT_TRACKER_PERMISSIONS_CHANGE */
735
    public function tracker_event_tracker_permisssions_change($params) {
736
        $this->reindexTracker($params);
737
    }
738
739
    /** @see TRACKER_EVENT_SEMANTIC_CONTRIBUTOR_CHANGE */
740
    public function tracker_event_semantic_contributor_change($params) {
741
        $this->reindexTracker($params);
742
    }
743
744
    private function reindexTracker($params) {
745
        $tracker    = $params['tracker'];
746
        $controller = $this->getAdminController();
747
748
        $controller->reindexTracker($tracker);
749
    }
750
751
    private function reindexAll($project_id) {
752
        $controller = $this->getAdminController();
753
754
        $controller->reindexDocman($project_id);
755
        $controller->reindexWiki($project_id);
756
        $controller->reindexTrackers($project_id);
757
    }
758
759
    public function project_admin_ugroup_delete_or_remove_user($params) {
760
        $ugroup_id  = $params['ugroup_id'];
761
        $project_id = $params['group_id'];
762
763
        $this->reindexForServicesUsingUgroup($ugroup_id, $project_id);
764
    }
765
766
    public function ugroup_manager_update_ugroup_binding_change($params) {
767
        /*@var $ugroup ProjectUGroup  */
768
        $ugroup     = $params['ugroup'];
769
        $project_id = $ugroup->getProjectId();
770
771
        $this->reindexForServicesUsingUgroup($ugroup->getId(), $project_id);
772
    }
773
774
    public function project_admin_change_user_permissions($params) {
775
        $project_id = $params['group_id'];
776
777
        if ($this->hasAMemberOfProjectMembersBeenRemoved($params)) {
778
            $ugroup_id = ProjectUGroup::PROJECT_ADMIN;
779
            $this->reindexForServicesUsingUgroup($ugroup_id, $project_id);
780
        }
781
        if ($this->hasAMemberOfWikiAdminsBeenRemoved($params)) {
782
            $ugroup_id = ProjectUGroup::WIKI_ADMIN;
783
            $this->reindexForWikiServiceUsingUgroup($ugroup_id, $project_id);
784
        }
785
    }
786
787
    private function hasAMemberOfProjectMembersBeenRemoved(array $params) {
788
        return $params['user_permissions']['admin_flags'] === '' && $params['previous_permissions']['admin_flags'] === 'A';
789
    }
790
791
    private function hasAMemberOfWikiAdminsBeenRemoved(array $params) {
792
        if (! isset($params['user_permissions']['wiki_flags'])) {
793
            return false;
794
        }
795
796
        return $params['user_permissions']['wiki_flags'] === '0' && $params['previous_permissions']['wiki_flags'] === '2';
797
    }
798
799
    public function project_admin_remove_user($params) {
800
        $ugroup_id  = ProjectUGroup::PROJECT_MEMBERS;
801
        $project_id = $params['group_id'];
802
803
        $this->reindexForServicesUsingUgroup($ugroup_id, $project_id);
804
    }
805
806
    /**
807
     * Reindex everything if user is removed from project (too many edge cases to check)
808
     */
809
    public function eventChangeProjectUgroupsMembers($params) {
810
        $project_id = $params['group_id'];
811
812
        $this->getTrackerSystemEventManager()->queueTrackersProjectReindexation($project_id);
813
        $this->getDocmanSystemEventManager()->queueDocmanProjectReindexation($project_id);
814
        $this->getWikiSystemEventManager()->queueWikiProjectReindexation($project_id);
815
    }
816
817
    private function reindexForServicesUsingUgroup($ugroup_id, $project_id) {
818
        $this->reindexForTrackerServiceUsingUgroup($ugroup_id, $project_id);
819
        $this->reindexForDocmanServiceUsingUgroup($ugroup_id, $project_id);
820
        $this->reindexForWikiServiceUsingUgroup($ugroup_id, $project_id);
821
    }
822
823
    private function reindexForTrackerServiceUsingUgroup($ugroup_id, $project_id) {
824
        $tracker_uses = false;
825
826
        EventManager::instance()->processEvent(
827
            FULLTEXTSEARCH_EVENT_DOES_TRACKER_SERVICE_USE_UGROUP,
828
            array('is_used' => &$tracker_uses, 'ugroup_id' => $ugroup_id, 'project_id' => $project_id)
829
        );
830
831
        if ($tracker_uses) {
832
            $this->getTrackerSystemEventManager()->queueTrackersProjectReindexation($project_id);
833
        }
834
    }
835
836
    private function reindexForDocmanServiceUsingUgroup($ugroup_id, $project_id) {
837
        $docman_uses  = false;
838
839
        EventManager::instance()->processEvent(
840
            FULLTEXTSEARCH_EVENT_DOES_DOCMAN_SERVICE_USE_UGROUP,
841
            array('is_used' => &$docman_uses, 'ugroup_id' => $ugroup_id, 'project_id' => $project_id)
842
        );
843
844
        if ($docman_uses) {
845
            $this->getDocmanSystemEventManager()->queueDocmanProjectReindexation($project_id);
846
        }
847
    }
848
849
    private function reindexForWikiServiceUsingUgroup($ugroup_id, $project_id) {
850
        $wiki_perm_manager = new Wiki_PermissionsManager(
851
            PermissionsManager::instance(),
852
            ProjectManager::instance(),
853
            new UGroupLiteralizer()
854
        );
855
856
        if ($wiki_perm_manager->isUgroupUsed($ugroup_id, $project_id)) {
857
            $this->getWikiSystemEventManager()->queueWikiProjectReindexation($project_id);
858
        }
859
    }
860
861
    public function redefine_search_type($params) {
862
        $calculator = new FullTextSearch_TypeOfSearchCalculator(PluginManager::instance(), $this);
863
864
        $params['type'] = $calculator->calculate($params['user'], $params['type'], $params['service_name'], $params['project_id']);
865
    }
866
}
867