Passed
Push — dependabot/npm_and_yarn/braces... ( fffa50...0815ab )
by
unknown
14:30 queued 07:16
created

getVisibleForumsInCategory()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 5
eloc 14
c 1
b 0
f 1
nc 5
nop 3
dl 0
loc 22
rs 9.4888
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\AbstractResource;
6
use Chamilo\CoreBundle\Entity\Course;
7
use Chamilo\CoreBundle\Entity\GradebookLink;
8
use Chamilo\CoreBundle\Entity\ResourceLink;
9
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
10
use Chamilo\CoreBundle\Entity\User;
11
use Chamilo\CoreBundle\Framework\Container;
12
use Chamilo\CourseBundle\Entity\CForum;
13
use Chamilo\CourseBundle\Entity\CForumAttachment;
14
use Chamilo\CourseBundle\Entity\CForumCategory;
15
use Chamilo\CourseBundle\Entity\CForumNotification;
16
use Chamilo\CourseBundle\Entity\CForumPost;
17
use Chamilo\CourseBundle\Entity\CForumThread;
18
use Chamilo\CourseBundle\Entity\CGroup;
19
use Chamilo\CourseBundle\Entity\CLp;
20
use Chamilo\CourseBundle\Entity\CLpItem;
21
use ChamiloSession as Session;
22
use Doctrine\Common\Collections\Criteria;
23
use Symfony\Component\HttpFoundation\File\UploadedFile;
24
use Chamilo\CoreBundle\Component\Utils\ActionIcon;
25
26
27
/**
28
 * @todo convert this library into a class
29
 */
30
function handleForum($url)
31
{
32
    $id = isset($_REQUEST['id']) ? (int) $_REQUEST['id'] : null;
33
34
    if (api_is_allowed_to_edit(false, true)) {
35
        $course = api_get_course_entity();
36
        $session = api_get_session_entity();
37
        $linksRepo = Database::getManager()->getRepository(ResourceLink::class);
38
39
        //if is called from a learning path lp_id
40
        $lp_id = isset($_REQUEST['lp_id']) ? (int) $_REQUEST['lp_id'] : null;
41
        $content = $_REQUEST['content'] ?? '';
42
        $action = $_REQUEST['action'] ?? null;
43
        $repo = null;
44
        switch ($content) {
45
            case 'forumcategory':
46
                $repo = Container::getForumCategoryRepository();
47
                break;
48
            case 'forum':
49
                $repo = Container::getForumRepository();
50
                break;
51
            case 'thread':
52
                $repo = Container::getForumThreadRepository();
53
                break;
54
        }
55
56
        /** @var AbstractResource|null $resource */
57
        $resource = null;
58
        if ($repo && $id) {
59
            $resource = $repo->find($id);
60
        }
61
62
        switch ($action) {
63
            case 'add_forum':
64
                $formContent = forumForm(null, $lp_id);
65
66
                return $formContent;
67
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
68
            case 'edit_forum':
69
                $repo = Container::getForumRepository();
70
                $resource = $repo->find($id);
71
                $formContent = forumForm($resource, $lp_id);
72
73
                return $formContent;
74
                break;
75
            case 'add_category':
76
                $formContent = getForumCategoryAddForm($lp_id);
77
78
                return $formContent;
79
                break;
80
            case 'edit_category':
81
                $repo = Container::getForumCategoryRepository();
82
                $category = $repo->find($id);
83
                $formContent = editForumCategoryForm($category);
84
85
                return $formContent;
86
                break;
87
            case 'notify':
88
                if (0 != api_get_session_id() &&
89
                    false == api_is_allowed_to_session_edit(false, true)
90
                ) {
91
                    api_not_allowed();
92
                }
93
                $message = set_notification($content, $id);
94
                Display::addFlash(Display::return_message($message, 'confirm', false));
95
96
                header('Location: '.$url);
97
                exit;
98
                break;
99
            case 'lock':
100
            case 'unlock':
101
                if (null !== $resource) {
102
                    if ('lock' === $action) {
103
                        $locked = 1;
104
                        $message = get_lang('Locked: students can no longer post new messages in this forum category, forum or thread but they can still read the messages that were already posted');
105
                    } else {
106
                        $locked = 0;
107
                        $message = get_lang('Unlocked: learners can post new messages in this forum category, forum or thread');
108
                    }
109
110
                    $resource->setLocked($locked);
111
                    $repo->update($resource);
112
113
                    Display::addFlash(
114
                        Display::return_message($message, 'confirmation', false)
115
                    );
116
                }
117
118
                header('Location: '.$url);
119
                exit;
120
                break;
121
            case 'move':
122
                moveUpDown($content, $_REQUEST['direction'] ?? '', $id);
123
                header('Location: '.$url);
124
                exit;
125
                break;
126
            case 'move_thread':
127
                $message = move_thread_form();
128
129
                return $message;
130
                break;
131
            case 'visible':
132
            case 'invisible':
133
                if (null !== $resource) {
134
                    if ('visible' === $action) {
135
                        $repo->setVisibilityPublished($resource);
136
                    } else {
137
                        $repo->setVisibilityPending($resource);
138
                    }
139
140
                    if ('visible' === $action) {
141
                        handle_mail_cue($content, $id);
142
                    }
143
144
                    Display::addFlash(
145
                        Display::return_message(get_lang('Updated'), 'confirmation', false)
146
                    );
147
                }
148
                header('Location: '.$url);
149
                exit;
150
                break;
151
            case 'delete_category':
152
                if ($resource) {
153
                    $linksRepo->removeByResourceInContext($resource, $course, $session);
154
155
                    Display::addFlash(
156
                        Display::return_message(get_lang('Forum category deleted'), 'confirmation', false)
157
                    );
158
                }
159
                header('Location: '.$url);
160
                exit;
161
                break;
162
            case 'delete_forum':
163
                if ($resource) {
164
                    $linksRepo->removeByResourceInContext($resource, $course, $session);
165
166
                    Display::addFlash(Display::return_message(get_lang('Forum deleted'), 'confirmation', false));
167
                }
168
169
                header('Location: '.$url);
170
                exit;
171
                break;
172
            case 'delete_thread':
173
                $locked = api_resource_is_locked_by_gradebook($id, LINK_FORUM_THREAD);
174
                if ($resource && false === $locked) {
175
                    $linksRepo->removeByResourceInContext($resource, $course, $session);
176
177
                    SkillModel::deleteSkillsFromItem($id, ITEM_TYPE_FORUM_THREAD);
178
                    $link_info = GradebookUtils::isResourceInCourseGradebook(
179
                        api_get_course_int_id(),
180
                        5,
181
                        $id,
182
                        api_get_session_id()
183
                    );
184
185
                    if (false !== $link_info) {
186
                        $link_id = $link_info['id'];
187
                        GradebookUtils::remove_resource_from_course_gradebook($link_id);
188
                    }
189
                    Display::addFlash(Display::return_message(get_lang('Thread deleted'), 'confirmation', false));
190
                }
191
192
                header('Location: '.$url);
193
                exit;
194
                break;
195
        }
196
    }
197
}
198
199
/**
200
 * This function displays the form that is used to add a forum category
201
 * or validates the form input, depending on the context.
202
 *
203
 * @author Patrick Cool <[email protected]>, Ghent University
204
 * @author Juan Carlos Raña Trabado (return to lp_id)
205
 *
206
 * @version may 2011, Chamilo 1.8.8
207
 * @throws Exception
208
 */
209
function getForumCategoryAddForm(int $lp_id = null): string
210
{
211
    $form = new FormValidator(
212
        'forumcategory',
213
        'post',
214
        'index.php?'.api_get_cidreq().'&action=add_category'
215
    );
216
    // hidden field if from learning path
217
    $form->addHidden('lp_id', $lp_id);
218
    $form->addHidden('action', 'add_category');
219
    // Setting the form elements.
220
    $form->addHeader(get_lang('Add forum category'));
221
    $form->addText('forum_category_title', get_lang('Title'), true, ['autofocus']);
222
    $form->addHtmlEditor(
223
        'forum_category_comment',
224
        get_lang('Description'),
225
        false,
226
        false,
227
        ['ToolbarSet' => 'Forum', 'Width' => '98%', 'Height' => '200']
228
    );
229
230
    $extraField = new ExtraField('forum_category');
231
    $extraField->addElements(
232
        $form,
233
        null,
234
        [], //exclude
235
        false, // filter
236
        false, // tag as select
237
        [], //show only fields
238
        [], // order fields
239
        [] // extra data
240
    );
241
242
    $form->addButtonCreate(get_lang('Create category'), 'SubmitForumCategory');
243
244
    // Setting the rules.
245
    $form->addRule('forum_category_title', get_lang('Required field'), 'required');
246
247
    // The validation or display
248
    if ($form->validate()) {
249
        $check = Security::check_token('post');
250
        if ($check) {
251
            $values = $form->exportValues();
252
            saveForumCategory($values);
253
        }
254
        Security::clear_token();
255
        return '';
256
    } else {
257
        $token = Security::get_token();
258
        $form->addElement('hidden', 'sec_token');
259
        $form->setConstants(['sec_token' => $token]);
260
261
        return $form->returnForm();
262
    }
263
}
264
265
/**
266
 * Generates a forum creation or edition form
267
 */
268
function forumForm(CForum $forum = null, int $lp_id = null): string
269
{
270
    $_course = api_get_course_info();
271
    // The header for the form
272
    $form_title = get_lang('Add a forum');
273
    $action = 'add_forum';
274
    $id = 0;
275
    if ($forum) {
276
        $id = $forum->getIid();
277
        $action = 'edit_forum';
278
        $form_title = get_lang('Edit forum');
279
    }
280
281
    $form = new FormValidator(
282
        'forumcategory',
283
        'post',
284
        'index.php?'.api_get_cidreq().'&action='.$action.'&id='.$id
285
    );
286
    $form->addHidden('action', $action);
287
    $form->addHeader($form_title);
288
289
    // We have a hidden field if we are editing.
290
    if ($forum) {
291
        $form->addHidden('forum_id', $id);
292
    }
293
294
    // hidden field if from learning path
295
    $form->addHidden('lp_id', $lp_id);
296
297
    // The title of the forum
298
    $form->addText('forum_title', get_lang('Title'), true, ['autofocus']);
299
300
    // The comment of the forum.
301
    $form->addHtmlEditor(
302
        'forum_comment',
303
        get_lang('Description'),
304
        false,
305
        false,
306
        ['ToolbarSet' => 'Forum', 'Width' => '98%', 'Height' => '200']
307
    );
308
309
    // Dropdown list: Forum categories
310
    $forum_categories = get_forum_categories();
311
    $forum_categories_titles = [];
312
    foreach ($forum_categories as $value) {
313
        $forum_categories_titles[$value->getIid()] = $value->getTitle();
314
    }
315
    $form->addSelect(
316
        'forum_category',
317
        get_lang('Create in category'),
318
        $forum_categories_titles
319
    );
320
    $form->applyFilter('forum_category', 'html_filter');
321
322
    if (COURSE_VISIBILITY_OPEN_WORLD == $_course['visibility']) {
323
        // This is for horizontal
324
        $group = [];
325
        $group[] = $form->createElement('radio', 'allow_anonymous', null, get_lang('Yes'), 1);
326
        $group[] = $form->createElement('radio', 'allow_anonymous', null, get_lang('No'), 0);
327
        $form->addGroup($group, 'allow_anonymous_group', get_lang('Allow anonymous posts?'));
328
    }
329
330
    $form->addButtonAdvancedSettings('advanced_params');
331
    $form->addHtml('<div id="advanced_params_options" style="display:none">');
332
333
    $form->addDateTimePicker(
334
        'start_time',
335
        [
336
            get_lang('Public access (access authorized to any member of the course)ation date'),
337
            get_lang('Public access (access authorized to any member of the course)ation dateComment'),
338
        ],
339
        ['id' => 'start_time']
340
    );
341
342
    $form->addDateTimePicker(
343
        'end_time',
344
        [get_lang('Closing date'), get_lang('Closing dateComment')],
345
        ['id' => 'end_time']
346
    );
347
348
    $form->addRule(
349
        ['start_time', 'end_time'],
350
        get_lang('Start date must be before the end date'),
351
        'compare_datetime_text',
352
        '< allow_empty'
353
    );
354
355
    $group = [];
356
    $group[] = $form->createElement('radio', 'moderated', null, get_lang('Yes'), 1);
357
    $group[] = $form->createElement('radio', 'moderated', null, get_lang('No'), 0);
358
    $form->addGroup($group, 'moderated', get_lang('Moderated forum'));
359
360
    $group = [];
361
    $group[] = $form->createElement('radio', 'students_can_edit', null, get_lang('Yes'), 1);
362
    $group[] = $form->createElement('radio', 'students_can_edit', null, get_lang('No'), 0);
363
    $form->addGroup($group, 'students_can_edit_group', get_lang('Can learners edit their own posts?'));
364
365
    $group = [];
366
    $group[] = $form->createElement('radio', 'approval_direct', null, get_lang('Approval'), 1);
367
    $group[] = $form->createElement('radio', 'approval_direct', null, get_lang('Direct'), 0);
368
369
    $group = [];
370
    $group[] = $form->createElement('radio', 'allow_attachments', null, get_lang('Yes'), 1);
371
    $group[] = $form->createElement('radio', 'allow_attachments', null, get_lang('No'), 0);
372
373
    $group = [];
374
    $group[] = $form->createElement('radio', 'allow_new_threads', null, get_lang('Yes'), 1);
375
    $group[] = $form->createElement('radio', 'allow_new_threads', null, get_lang('No'), 0);
376
    $form->addGroup($group, 'allow_new_threads_group', get_lang('Allow users to start new threads'));
377
378
    $group = [];
379
    $group[] = $form->createElement('radio', 'default_view_type', null, get_lang('Flat'), 'flat');
380
    $group[] = $form->createElement('radio', 'default_view_type', null, get_lang('Threaded'), 'threaded');
381
    $group[] = $form->createElement('radio', 'default_view_type', null, get_lang('Nested'), 'nested');
382
    $form->addGroup($group, 'default_view_type_group', get_lang('Default view type'));
383
384
    // Drop down list: Groups
385
    $groups = GroupManager::get_group_list();
386
    $groups_titles[0] = get_lang('Not a group forum');
387
    foreach ($groups as $value) {
388
        $groups_titles[$value['iid']] = $value['name'];
389
    }
390
    $form->addSelect('group_forum', get_lang('For Group'), $groups_titles);
391
392
    // Public or private group forum
393
    $group = [];
394
    $group[] = $form->createElement(
395
        'radio',
396
        'public_private_group_forum',
397
        null,
398
        get_lang('Public access (access authorized to any member of the course)'),
399
        'public'
400
    );
401
    $group[] = $form->createElement(
402
        'radio',
403
        'public_private_group_forum',
404
        null,
405
        get_lang('Private access (access authorized to group members only)'),
406
        'private'
407
    );
408
    $form->addGroup(
409
        $group,
410
        'public_private_group_forum_group',
411
        get_lang(
412
            'Public access (access authorized to any member of the course)Private access (access authorized to group members only)GroupForum'
413
        )
414
    );
415
416
    // Forum image
417
    $form->addProgress();
418
419
    $form->addElement('html', '</div>');
420
421
    // The OK button
422
    if ($forum) {
423
        $form->addButtonUpdate(get_lang('Edit forum'), 'SubmitForum');
424
    } else {
425
        $form->addButtonCreate(get_lang('Create forum'), 'SubmitForum');
426
    }
427
428
    // setting the rules
429
    $form->addRule('forum_title', get_lang('Required field'), 'required');
430
    $form->addRule('forum_category', get_lang('Required field'), 'required');
431
432
    // Settings the defaults
433
    if (null === $forum) {
434
        $defaults['moderated']['moderated'] = 0;
435
        $defaults['allow_anonymous_group']['allow_anonymous'] = 0;
436
        $defaults['students_can_edit_group']['students_can_edit'] = 0;
437
        $defaults['approval_direct_group']['approval_direct'] = 0;
438
        $defaults['allow_attachments_group']['allow_attachments'] = 1;
439
        $defaults['allow_new_threads_group']['allow_new_threads'] = 1;
440
        $defaults['default_view_type_group']['default_view_type'] = api_get_setting('default_forum_view');
441
        $defaults['public_private_group_forum_group']['public_private_group_forum'] = 'public';
442
        if (isset($_GET['forumcategory'])) {
443
            $defaults['forum_category'] = Security::remove_XSS($_GET['forumcategory']);
444
        }
445
    } else {
446
        // the default values when editing = the data in the table
447
        $defaults['forum_id'] = $forum->getIid();
448
        $defaults['forum_title'] = prepare4display($forum->getTitle());
449
        $defaults['forum_comment'] = prepare4display($forum->getForumComment());
450
        $defaults['start_time'] = api_get_local_time($forum->getStartTime());
451
        $defaults['end_time'] = api_get_local_time($forum->getEndTime());
452
        $defaults['moderated']['moderated'] = $forum->isModerated();
453
        $defaults['forum_category'] = $forum->getForumCategory()->getIid();
454
        $defaults['allow_anonymous_group']['allow_anonymous'] = $forum->getAllowAnonymous();
455
        $defaults['students_can_edit_group']['students_can_edit'] = $forum->getAllowEdit();
456
        $defaults['approval_direct_group']['approval_direct'] = $forum->getApprovalDirectPost();
457
        $defaults['allow_attachments_group']['allow_attachments'] = $forum->getAllowAttachments();
458
        $defaults['allow_new_threads_group']['allow_new_threads'] = $forum->getAllowNewThreads();
459
        $defaults['default_view_type_group']['default_view_type'] = $forum->getDefaultView();
460
        $defaults['public_private_group_forum_group']['public_private_group_forum'] = $forum->getForumGroupPublicPrivate();
461
        $defaults['group_forum'] = $forum->getForumOfGroup();
462
    }
463
464
    $form->setDefaults($defaults);
465
    // Validation or display
466
    if ($form->validate()) {
467
        $check = Security::check_token('post');
468
        if ($check) {
469
            $values = $form->getSubmitValues();
470
            $forumId = store_forum($values, [], true);
471
            if ($forumId) {
472
                // SkillModel::saveSkills($form, ITEM_TYPE_FORUM, $forumId);
473
                if (isset($values['forum_id'])) {
474
                    Display::addFlash(Display::return_message(get_lang('The forum has been modified'), 'confirmation'));
475
                } else {
476
                    Display::addFlash(Display::return_message(get_lang('The forum has been added'), 'confirmation'));
477
                }
478
            }
479
            $url = api_get_path(WEB_CODE_PATH).'forum/index.php?'.api_get_cidreq();
480
            header('Location: '.$url);
481
            exit;
482
        }
483
        Security::clear_token();
484
    } else {
485
        $token = Security::get_token();
486
        $form->addElement('hidden', 'sec_token');
487
        $form->setConstants(['sec_token' => $token]);
488
489
        return $form->returnForm();
490
    }
491
492
    return '';
493
}
494
495
/**
496
 * This function deletes the forum image if exists.
497
 * @author Julio Montoya <[email protected]>
498
 * @version february 2006, dokeos 1.8
499
 * @todo Implement
500
 * @throws Exception
501
 */
502
function deleteForumImage(CForum $forum): bool
503
{
504
    throw new Exception('delete_forum_image');
505
506
    return false;
0 ignored issues
show
Unused Code introduced by
return false is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
507
}
508
509
/**
510
 * Generates an HTML form to edit the forum category
511
 * @throws Exception
512
 */
513
function editForumCategoryForm(CForumCategory $category): string
514
{
515
    $categoryId = $category->getIid();
516
    $form = new FormValidator(
517
        'forumcategory',
518
        'post',
519
        'index.php?action=edit_category&'.api_get_cidreq().'&id='.$categoryId
520
    );
521
    // Setting the form elements.
522
    $form->addElement('header', '', get_lang('Edit forumCategory'));
523
524
    $form->addElement('hidden', 'action', 'edit_category');
525
    $form->addElement('hidden', 'forum_category_id');
526
    $form->addElement('text', 'forum_category_title', get_lang('Title'));
527
528
    $form->addElement(
529
        'html_editor',
530
        'forum_category_comment',
531
        get_lang('Comment'),
532
        null,
533
        ['ToolbarSet' => 'Forum', 'Width' => '98%', 'Height' => '200']
534
    );
535
536
    $extraField = new ExtraField('forum_category');
537
    $extraField->addElements(
538
        $form,
539
        $categoryId,
540
        [], //exclude
541
        false, // filter
542
        false, // tag as select
543
        [], //show only fields
544
        [], // order fields
545
        [] // extra data
546
    );
547
548
    $form->addButtonUpdate(get_lang('Edit category'), 'SubmitEdit forumCategory');
549
550
    // Setting the default values.
551
    $defaultvalues['forum_category_id'] = $categoryId;
552
    $defaultvalues['forum_category_title'] = $category->getTitle();
553
    $defaultvalues['forum_category_comment'] = $category->getCatComment();
554
    $form->setDefaults($defaultvalues);
555
556
    // Setting the rules.
557
    $form->addRule('forum_category_title', get_lang('Required field'), 'required');
558
559
    // Validation or display
560
    if ($form->validate()) {
561
        $check = Security::check_token('post');
562
        if ($check) {
563
            $values = $form->exportValues();
564
            saveForumCategory($values);
565
        }
566
        Security::clear_token();
567
    } else {
568
        $token = Security::get_token();
569
        $form->addElement('hidden', 'sec_token');
570
        $form->setConstants(['sec_token' => $token]);
571
572
        return $form->returnForm();
573
    }
574
575
    return '';
576
}
577
578
/**
579
 * This function stores the forum category in the database.
580
 * The new category is added to the end.
581
 * @throws Exception
582
 */
583
function saveForumCategory(array $values, array $courseInfo = [], bool $showMessage = true): CForumCategory
584
{
585
    $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo;
586
    $course_id = $courseInfo['real_id'];
587
    $session_id = api_get_session_id();
588
    $clean_cat_title = $values['forum_category_title'];
589
    $repo = Container::getForumCategoryRepository();
590
591
    $message = '';
592
    if (isset($values['forum_category_id'])) {
593
        /** @var CForumCategory $category */
594
        $category = $repo->find($values['forum_category_id']);
595
        $category
596
            ->setCatComment($values['forum_category_comment'] ?? '')
597
            ->setTitle($values['forum_category_title'])
598
        ;
599
        $repo->update($category);
600
        $message = get_lang('The forum category has been modified');
601
602
        $logInfo = [
603
            'tool' => TOOL_FORUM,
604
            'action' => 'update-forumcategory',
605
            'action_details' => 'forumcategory',
606
            'info' => $clean_cat_title,
607
        ];
608
        Event::registerLog($logInfo);
609
610
        $values['item_id'] = $values['forum_category_id'];
611
    } else {
612
        $course = api_get_course_entity($course_id);
613
        $session = api_get_session_entity($session_id);
614
615
        $category = new CForumCategory();
616
        $category
617
            ->setTitle($clean_cat_title)
618
            ->setCatComment($values['forum_category_comment'] ?? '')
619
            ->setParent($course)
620
            ->addCourseLink($course, $session)
621
        ;
622
        $repo->create($category);
623
624
        $last_id = $category->getIid();
625
        if ($last_id > 0) {
626
            $message = get_lang('The forum category has been added');
627
        }
628
629
        $logInfo = [
630
            'tool' => TOOL_FORUM,
631
            'action' => 'new-forumcategory',
632
            'action_details' => 'forumcategory',
633
            'info' => $clean_cat_title,
634
        ];
635
        Event::registerLog($logInfo);
636
637
        $values['item_id'] = $last_id;
638
    }
639
640
    $extraFieldValue = new ExtraFieldValue('forum_category');
641
    $extraFieldValue->saveFieldValues($values);
642
643
    if ($showMessage) {
644
        Display::addFlash(Display::return_message($message, 'confirmation'));
645
    }
646
647
    return $category;
648
}
649
650
/**
651
 * Stores a new or edited forum in the database.
652
 * The new forum is added to the end of the others.
653
 * Returns an ID if the option 'returnId' was set to true, otherwise returns a confirmation message
654
 */
655
function store_forum(array $values, array $courseInfo = [], bool $returnId = false): string|int
656
{
657
    $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo;
658
    $courseId = $courseInfo['real_id'];
659
    $session_id = api_get_session_id();
660
661
    // Find the max forum_order for the given category. The new forum is added at the end => max cat_order + &
662
    if (null === $values['forum_category']) {
663
        $new_max = null;
664
    } else {
665
        /*$sql = "SELECT MAX(forum_order) as sort_max
666
                FROM $table_forums
667
                WHERE
668
                    c_id = $courseId AND
669
                    forum_category='".Database::escape_string($values['forum_category'])."'";
670
        $result = Database::query($sql);
671
        $row = Database::fetch_array($result);
672
        $new_max = $row['sort_max'] + 1;*/
673
    }
674
675
    // Forum images
676
    $has_attachment = false;
677
    $image_moved = true;
678
    if (!empty($_FILES['picture']['name'])) {
679
        $upload_ok = process_uploaded_file($_FILES['picture']);
680
        $has_attachment = true;
681
    }
682
683
    // Remove existing picture if it was requested.
684
    if (!empty($_POST['remove_picture'])) {
685
        //deleteForumImage($values['forum_id']);
686
    }
687
688
    $new_file_name = '';
689
    if (isset($upload_ok)) {
690
        if ($has_attachment) {
691
            throw new Exception('$has_attachment');
692
            /*$course_dir = $courseInfo['path'].'/upload/forum/images';
693
            $sys_course_path = api_get_path(SYS_COURSE_PATH);
694
            $updir = $sys_course_path.$course_dir;
695
            // Try to add an extension to the file if it hasn't one.
696
            $new_file_name = add_ext_on_mime(
697
                Database::escape_string($_FILES['picture']['name']),
698
                $_FILES['picture']['type']
699
            );
700
            if (!filter_extension($new_file_name)) {
701
                //Display::addFlash(Display::return_message(get_lang('File upload failed: this file extension or file type is prohibited'), 'error'));
702
                $image_moved = false;
703
            } else {
704
                $file_extension = explode('.', $_FILES['picture']['name']);
705
                $file_extension = strtolower($file_extension[count($file_extension) - 1]);
706
                $new_file_name = uniqid('').'.'.$file_extension;
707
                $new_path = $updir.'/'.$new_file_name;
708
                $result = @move_uploaded_file($_FILES['picture']['tmp_name'], $new_path);
709
                // Storing the attachments if any
710
                if ($result) {
711
                    $image_moved = true;
712
                }
713
            }*/
714
        }
715
    }
716
717
    $repo = Container::getForumRepository();
718
719
    if (!isset($values['forum_id'])) {
720
        $forum = new CForum();
721
    } else {
722
        /** @var CForum $forum */
723
        $forum = $repo->find($values['forum_id']);
724
    }
725
726
    $forumCategory = null;
727
    if (!empty($values['forum_category'])) {
728
        $repoForumCategory = Container::getForumCategoryRepository();
729
        $forumCategory = $repoForumCategory->find($values['forum_category']);
730
    }
731
732
    $lpId = $values['lp_id'] ?? 0;
733
    $lpRepo = Container::getLpRepository();
734
    $lp = null;
735
    if (!empty($lpId)) {
736
        /** @var CLp $lp */
737
        $lp = $lpRepo->find($lpId);
738
    }
739
740
    $forum
741
        ->setTitle($values['forum_title'])
742
        ->setForumComment($values['forum_comment'] ?? '')
743
        ->setForumCategory($forumCategory)
744
        ->setAllowAnonymous($values['allow_anonymous_group']['allow_anonymous'] ?? 0)
745
        ->setAllowEdit($values['students_can_edit_group']['students_can_edit'] ?? 0)
746
        ->setApprovalDirectPost((string) ($values['approval_direct_group']['approval_direct'] ?? '0'))
747
        ->setAllowAttachments($values['allow_attachments_group']['allow_attachments'] ?? 0)
748
        ->setAllowNewThreads((int) ($values['allow_new_threads_group']['allow_new_threads'] ?? 0))
749
        ->setDefaultView($values['default_view_type_group']['default_view_type'] ?? '')
750
        ->setForumOfGroup((string) ($values['group_forum'] ?? ''))
751
        ->setForumGroupPublicPrivate($values['public_private_group_forum_group']['public_private_group_forum'] ?? '')
752
        ->setModerated((bool) ($values['moderated']['moderated'] ?? false))
753
        ->setStartTime(!empty($values['start_time']) ? api_get_utc_datetime($values['start_time'], true, true) : null)
754
        ->setEndTime(!empty($values['end_time']) ? api_get_utc_datetime($values['end_time'], true, true) : null)
755
        ->setLp($lp)
756
    ;
757
758
    $course = api_get_course_entity($courseId);
759
    $session = api_get_session_entity($session_id);
760
761
    if (isset($values['forum_id'])) {
762
        // Update.
763
        $repo->update($forum);
764
765
        // Move groups from one group to another
766
        if (isset($values['group_forum']) && false) {
767
            $forumData = get_forums($values['forum_id']);
768
            $currentGroupId = $forumData['forum_of_group'];
769
        }
770
771
        $return_message = get_lang('The forum has been modified');
772
        $forumId = $forum->getIid();
773
774
        $logInfo = [
775
            'tool' => TOOL_FORUM,
776
            'tool_id' => $values['forum_id'],
777
            'action' => 'update-forum',
778
            'action_details' => 'forum',
779
            'info' => $values['forum_title'],
780
        ];
781
        Event::registerLog($logInfo);
782
    } else {
783
        $forum
784
            ->setParent($forumCategory)
785
            ->addCourseLink($course, $session);
786
        $repo->create($forum);
787
788
        $forumId = $forum->getIid();
789
        if ($forumId > 0) {
790
            $courseCode = $courseInfo['code'];
791
            $subscribe = (int) api_get_course_setting('subscribe_users_to_forum_notifications');
792
793
            $status = STUDENT;
794
            if (!empty($session_id)) {
795
                $status = 0;
796
            }
797
            if (1 === $subscribe) {
798
                $userList = CourseManager::get_user_list_from_course_code(
799
                    $courseCode,
800
                    $session_id,
801
                    null,
802
                    null,
803
                    $status
804
                );
805
                foreach ($userList as $userInfo) {
806
                    set_notification('forum', $forumId, false, $userInfo, $courseInfo);
807
                }
808
            }
809
810
            $logInfo = [
811
                'tool' => TOOL_FORUM,
812
                'tool_id' => $forumId,
813
                'action' => 'new-forum',
814
                'action_details' => 'forum',
815
                'info' => $values['forum_title'],
816
            ];
817
            Event::registerLog($logInfo);
818
        }
819
        $return_message = get_lang('The forum has been added');
820
    }
821
822
    if ($returnId) {
823
        return $forumId;
824
    }
825
826
    return $return_message;
827
}
828
829
function deletePost(CForumPost $post): void
830
{
831
    $table_threads = Database::get_course_table(TABLE_FORUM_THREAD);
832
    $em = Database::getManager();
833
    $em
834
        ->createQuery('
835
            UPDATE ChamiloCourseBundle:CForumPost p
836
            SET p.postParent = :parent_of_deleted_post
837
            WHERE
838
                p.postParent = :post AND
839
                p.thread = :thread_of_deleted_post AND
840
                p.forum = :forum_of_deleted_post
841
        ')
842
        ->execute([
843
            'parent_of_deleted_post' => $post->getPostParent(),
844
            'post' => $post->getIid(),
845
            'thread_of_deleted_post' => $post->getThread() ? $post->getThread()->getIid() : 0,
846
            'forum_of_deleted_post' => $post->getForum(),
847
        ]);
848
849
    $attachments = $post->getAttachments();
850
    if (!empty($attachments)) {
851
        foreach ($attachments as $attachment) {
852
            $em->remove($attachment);
853
        }
854
    }
855
856
    $em->remove($post);
857
    $em->flush();
858
859
    $lastPostOfTheTread = getLastPostOfThread($post->getThread()->getIid());
860
861
    if (!empty($lastPostOfTheTread)) {
862
        // Decreasing the number of replies for this thread and also changing the last post information.
863
        $sql = "UPDATE $table_threads
864
                SET
865
                    thread_replies = thread_replies - 1,
866
                    thread_last_post = ".$lastPostOfTheTread['iid'].",
867
                    thread_date = '".$lastPostOfTheTread['post_date']."'
868
                WHERE iid = ".$post->getThread()->getIid();
869
        Database::query($sql);
870
        Display::addFlash(Display::return_message(get_lang('Post has been deleted')));
871
    } else {
872
        // We deleted the very last post of the thread, so we need to delete the thread as well.
873
        $sql = "DELETE FROM $table_threads
874
                WHERE iid = ".$post->getThread()->getIid();
875
        Database::query($sql);
876
877
        Display::addFlash(Display::return_message(get_lang('Thread deleted')));
878
    }
879
}
880
881
/**
882
 * This function gets the all information of the last (=most recent) post of the thread
883
 * This can be done by sorting the posts that have the field threadId=$threadId and sort them by post_date.
884
 * @author Patrick Cool <[email protected]>, Ghent University
885
 * @version february 2006, dokeos 1.8
886
 */
887
function getLastPostOfThread(int $threadId): array
888
{
889
    $post = Container::getForumPostRepository()->findOneBy(['thread' => $threadId], ['postDate' => 'DESC']);
890
891
    if (null === $post) {
892
        return [];
893
    }
894
895
    return [
896
        'iid' => $post->getIid(),
897
        'post_date' => $post->getPostDate()->format('Y-m-d H:i:s'),
898
    ];
899
}
900
901
/**
902
 * @param string $content                   Type of content forum category, forum, thread, post
903
 * @param int    $id                        the id of the content we want to make invisible
904
 * @param int    $current_visibility_status what is the current status of the visibility (0 = invisible, 1 = visible)
905
 * @param array  $additional_url_parameters
906
 *
907
 * @return string HTML
908
 */
909
function returnVisibleInvisibleIcon(
910
    string $content,
911
    int $id,
912
    int $current_visibility_status,
913
    array $additional_url_parameters = []
914
): string
915
{
916
    $html = '';
917
918
    if (1 == $current_visibility_status) {
919
        $html .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&';
920
        if (is_array($additional_url_parameters)) {
921
            foreach ($additional_url_parameters as $key => $value) {
922
                $html .= $key.'='.$value.'&';
923
            }
924
        }
925
        $html .= 'action=invisible&content='.$content.'&id='.$id.'">'.
926
            Display::getMdiIcon(ActionIcon::VISIBLE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('MakeInvisible')).'</a>';
927
    }
928
    if (0 == $current_visibility_status) {
929
        $html .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&';
930
        if (is_array($additional_url_parameters)) {
931
            foreach ($additional_url_parameters as $key => $value) {
932
                $html .= $key.'='.$value.'&';
933
            }
934
        }
935
        $html .= 'action=visible&content='.$content.'&id='.$id.'">'.
936
            Display::getMdiIcon(ActionIcon::INVISIBLE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Make Visible')).'</a>';
937
    }
938
939
    return $html;
940
}
941
942
/**
943
 * Returns an HTML string with the appropriate icon
944
 * @param string $content                   Type of content forum category, forum, thread, post
945
 * @param int    $id                        the id of the content we want to make invisible
946
 * @param int    $current_lock_status what is the current status of the visibility (0 = unlocked, 1 = locked)
947
 * @param array  $additional_url_parameters
948
 */
949
function returnLockUnlockIcon(
950
    string $content,
951
    int $id,
952
    int $current_lock_status,
953
    array $additional_url_parameters = []
954
): string
955
{
956
    $html = '';
957
    //check if the forum is blocked due
958
    if ('thread' === $content) {
959
        if (api_resource_is_locked_by_gradebook($id, LINK_FORUM_THREAD)) {
960
            return $html.Display::getMdiIcon(
961
                    ActionIcon::LOCK,
962
                    'ch-tool-icon-disabled',
963
                    '',
964
                    ICON_SIZE_SMALL,
965
                    get_lang(
966
                        'This option is not available because this activity is contained by an assessment, which is currently locked. To unlock the assessment, ask your platform administrator.'
967
                    )
968
                );
969
        }
970
    }
971
    if ('1' == $current_lock_status) {
972
        $html .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&';
973
        if (is_array($additional_url_parameters)) {
974
            foreach ($additional_url_parameters as $key => $value) {
975
                $html .= $key.'='.$value.'&';
976
            }
977
        }
978
        $html .= 'action=unlock&content='.$content.'&id='.$id.'">'.
979
            Display::getMdiIcon(ActionIcon::LOCK, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Unlock')).'</a>';
980
    }
981
    if ('0' == $current_lock_status) {
982
        $html .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&';
983
        if (is_array($additional_url_parameters)) {
984
            foreach ($additional_url_parameters as $key => $value) {
985
                $html .= $key.'='.$value.'&';
986
            }
987
        }
988
        $html .= 'action=lock&content='.$content.'&id='.$id.'">'.
989
            Display::getMdiIcon(ActionIcon::UNLOCK, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Lock')).'</a>';
990
    }
991
992
    return $html;
993
}
994
995
/**
996
 * Generates HTML for up and down icons with links to move an item up or down in a list.
997
 */
998
function returnUpDownIcon(string $content, int $id, array $list): string {
999
    $forumCategory = isset($_GET['forumcategory']) ? Security::remove_XSS($_GET['forumcategory']) : null;
1000
    $totalItemsOfType = 0;
1001
    $position = 0;
1002
    foreach ($list as $key => $item) {
1003
        if (($content === 'forumcategory' && $item instanceof CForumCategory) ||
1004
            ($content === 'forum' && $item instanceof CForum)) {
1005
            $totalItemsOfType++;
1006
            if ($id == $item->getIid()) {
1007
                $position = $key + 1;
1008
            }
1009
        }
1010
    }
1011
1012
    $upIcon = $position > 1
1013
        ? '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=move&direction=up&content='.$content.'&id='.$id.'" title="'.get_lang('Move up').'">'.Display::getMdiIcon(ActionIcon::UP, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Move up')).'</a>'
1014
        : '<span class="ch-tool-icon-disabled">'.Display::getMdiIcon(ActionIcon::UP, 'ch-tool-icon-disabled', null, ICON_SIZE_SMALL, '').'</span>';
1015
1016
    $downIcon = $position < $totalItemsOfType
1017
        ? '<a href="'.api_get_self().'?'.api_get_cidreq().'&action=move&direction=down&content='.$content.'&id='.$id.'" title="'.get_lang('Move down').'">'.Display::getMdiIcon(ActionIcon::DOWN, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Move down')).'</a>'
1018
        : '<span class="ch-tool-icon-disabled">'.Display::getMdiIcon(ActionIcon::DOWN, 'ch-tool-icon-disabled', null, ICON_SIZE_SMALL, '').'</span>';
1019
1020
1021
    return $upIcon . $downIcon;
1022
}
1023
1024
/**
1025
 * Moves a forum or forum category up or down in the display order.
1026
 */
1027
function moveUpDown(string $content, string $direction, int $id): string
1028
{
1029
    $em = Database::getManager();
1030
1031
    if ('forumcategory' === $content) {
1032
        $entityRepo = $em->getRepository(CForumCategory::class);
1033
    } elseif ('forum' === $content) {
1034
        $entityRepo = $em->getRepository(CForum::class);
1035
    }
1036
1037
    if (null === $entityRepo) {
1038
        return false;
1039
    }
1040
1041
    $entity = $entityRepo->find($id);
1042
    if (null === $entity) {
1043
        return false;
1044
    }
1045
1046
    $resourceNode = $entity->getResourceNode();
1047
    if (null === $resourceNode) {
1048
        return false;
1049
    }
1050
1051
    $course = api_get_course_entity();
1052
    $session = api_get_session_entity();
1053
1054
    $link = $resourceNode->getResourceLinkByContext($course, $session);
1055
1056
    if (!$link) {
1057
        return false;
1058
    }
1059
1060
    if ('down' === $direction) {
1061
        $link->moveDownPosition();
1062
    } else {
1063
        $link->moveUpPosition();
1064
    }
1065
1066
    $em->flush();
1067
1068
    Display::addFlash(Display::return_message(get_lang('Updated')));
1069
1070
    return true;
1071
}
1072
1073
/**
1074
 * Retrieve all the information off the forum categories (or one specific) for the current course.
1075
 * The categories are sorted according to their sorting order (cat_order.
1076
 *
1077
 * @param int $courseId  Optional. The course ID
1078
 * @param int $sessionId Optional. The session ID
1079
 *
1080
 * @return CForumCategory[]
1081
 */
1082
function get_forum_categories(int $courseId = 0, int $sessionId = 0): Array
1083
{
1084
    $repo = Container::getForumCategoryRepository();
1085
1086
    $course = api_get_course_entity($courseId);
1087
    $session = api_get_session_entity($sessionId);
1088
1089
    $qb = $repo->getResourcesByCourse($course, $session, null, $course->getResourceNode(), true, true);
1090
1091
    return $qb->getQuery()->getResult();
1092
}
1093
1094
/**
1095
 * This function retrieves all the fora in a given forum category.
1096
 *
1097
 * @todo: Fixes error if there forums with no category.
1098
 *
1099
 * @param int $categoryId the id of the forum category
1100
 * @param int $courseId   Optional. The course ID
1101
 *
1102
 * @return CForum[] containing all the information about the forums (regardless of their category)
1103
 *
1104
 * @author Patrick Cool <[email protected]>, Ghent University
1105
 *
1106
 * @version february 2006, dokeos 1.8
1107
 */
1108
function get_forums_in_category(int $categoryId, int $courseId = 0, int $sessionId = 0)
1109
{
1110
    $repo = Container::getForumRepository();
1111
    $course = api_get_course_entity($courseId);
1112
    $session = api_get_session_entity($sessionId);
1113
1114
    $qb = $repo->getResourcesByCourse($course, $session, null, null, true, true);
1115
    $qb
1116
        ->andWhere('resource.forumCategory = :catId')
1117
        ->setParameter('catId', $categoryId)
1118
    ;
1119
1120
    return $qb->getQuery()->getResult();
1121
}
1122
1123
/**
1124
 * Retrieve all the forums (regardless of their category) or of only one.
1125
 * The forums are sorted according to the forum_order.
1126
 * Since it does not take the forum category into account there probably
1127
 * will be two or more forums that have forum_order=1, ...
1128
 *
1129
 * @param bool $includeGroupsForum
1130
 * @param int  $sessionId
1131
 *
1132
 * @return CForum[]
1133
 */
1134
function get_forums(
1135
    int $courseId = null,
1136
    int $sessionId = 0
1137
) {
1138
    $repo = Container::getForumRepository();
1139
    $courseId = empty($courseId) ? api_get_course_int_id() : $courseId;
1140
    $course = api_get_course_entity($courseId);
1141
    $session = api_get_session_entity($sessionId);
1142
1143
    $qb = $repo->getResourcesByCourse($course, $session);
1144
1145
    /*$qb->andWhere('resource.forumCategory = :catId')
1146
        ->setParameter('catId', $cat_id);
1147
    */
1148
    return $qb->getQuery()->getResult();
1149
}
1150
1151
/**
1152
 * Returns the given forum ID's forum instance
1153
 */
1154
function getForum(
1155
    int $forumId = null
1156
): CForum|bool {
1157
    if (!empty($forumId)) {
1158
        $repo = Container::getForumRepository();
1159
        $qb = $repo->find($forumId);
1160
1161
        return $qb->getQuery()->getResult();
1162
    }
1163
1164
    return false;
1165
}
1166
1167
/**
1168
 * Retrieves all the threads for a given forum or counts them.
1169
 */
1170
function get_threads(int $forumId, int $courseId = null, int $sessionId = null, bool $count = false): array|int
1171
{
1172
    $repo = Container::getForumThreadRepository();
1173
    $courseId = empty($courseId) ? api_get_course_int_id() : $courseId;
1174
    $course = api_get_course_entity($courseId);
1175
    $session = api_get_session_entity($sessionId);
1176
1177
    $qb = $repo->getResourcesByCourse($course, $session);
1178
    $qb->andWhere('resource.forum = :forum')->setParameter('forum', $forumId);
1179
1180
    if ($count) {
1181
        $qb->select('COUNT(resource.iid)');
1182
        return (int) $qb->getQuery()->getSingleScalarResult();
1183
    } else {
1184
        return $qb->getQuery()->getResult();
1185
    }
1186
}
1187
1188
/**
1189
 * Get a thread by Id and course id.
1190
 *
1191
 * @param int $threadId the thread Id
1192
 *
1193
 * @return array containing all the information about the thread
1194
 */
1195
function getThreadInfo(int $threadId): Array
1196
{
1197
    $repo = Database::getManager()->getRepository(CForumThread::class);
1198
    /** @var CForumThread $forumThread */
1199
    $forumThread = $repo->findOneBy(['iid' => $threadId]);
1200
1201
    $thread = [];
1202
    if ($forumThread) {
1203
        $thread['iid'] = $forumThread->getIid();
1204
        $thread['threadId'] = $forumThread->getIid();
1205
        $thread['threadTitle'] = $forumThread->getTitle();
1206
        $thread['forumId'] = $forumThread->getForum() ? $forumThread->getForum()->getIid() : 0;
1207
        //$thread['sessionId'] = $forumThread->getSessionId();
1208
        $thread['threadSticky'] = $forumThread->getThreadSticky();
1209
        $thread['locked'] = $forumThread->getLocked();
1210
        $thread['threadTitleQualify'] = $forumThread->getThreadTitleQualify();
1211
        $thread['threadQualifyMax'] = $forumThread->getThreadQualifyMax();
1212
        $thread['threadCloseDate'] = $forumThread->getThreadCloseDate();
1213
        $thread['threadWeight'] = $forumThread->getThreadWeight();
1214
        $thread['threadPeerQualify'] = $forumThread->isThreadPeerQualify();
1215
    }
1216
1217
    return $thread;
1218
}
1219
1220
/**
1221
 * Retrieve all posts of a given thread.
1222
 *
1223
 * @param int    $threadId       The thread ID
1224
 * @param string $orderDirection Optional. The direction for sort the posts
1225
 * @param bool   $recursive      Optional. If the list is recursive
1226
 * @param int    $postId         Optional. The post ID for recursive list
1227
 * @param int    $depth          Optional. The depth to indicate the indent
1228
 *
1229
 * @todo move to a repository
1230
 *
1231
 * @return array containing all the information about the posts of a given thread
1232
 */
1233
function getPosts(
1234
    CForum $forum,
1235
    int $threadId,
1236
    string $orderDirection = 'ASC',
1237
    bool $recursive = false,
1238
    int $postId = null,
1239
    int $depth = -1
1240
): Array
1241
{
1242
    $em = Database::getManager();
1243
1244
    if (api_is_allowed_to_edit(false, true)) {
1245
        $visibleCriteria = Criteria::expr()->neq('visible', 2);
1246
    } else {
1247
        $visibleCriteria = Criteria::expr()->eq('visible', 1);
1248
    }
1249
1250
    $criteria = Criteria::create();
1251
    $criteria
1252
        ->where(Criteria::expr()->eq('thread', $threadId))
1253
        //->andWhere(Criteria::expr()->eq('cId', $forum->getCId()))
1254
        ->andWhere($visibleCriteria)
1255
    ;
1256
1257
    $groupId = api_get_group_id();
1258
    $filterModerated = true;
1259
1260
    if (empty($groupId)) {
1261
        if (api_is_allowed_to_edit()) {
1262
            $filterModerated = false;
1263
        }
1264
    } else {
1265
        $groupEntity = api_get_group_entity($groupId);
1266
        if (GroupManager::isTutorOfGroup(api_get_user_id(), $groupEntity) ||
1267
            api_is_allowed_to_edit(false, true)
1268
        ) {
1269
            $filterModerated = false;
1270
        }
1271
    }
1272
1273
    if ($recursive) {
1274
        $criteria->andWhere(Criteria::expr()->eq('postParent', $postId));
1275
    }
1276
1277
    $qb = $em->getRepository(CForumPost::class)->createQueryBuilder('p');
1278
    $qb->select('p')
1279
        ->addCriteria($criteria)
1280
        ->addOrderBy('p.iid', $orderDirection);
1281
1282
    if ($filterModerated && 1 == $forum->isModerated()) {
1283
        if (!api_is_allowed_to_edit(false, true)) {
1284
            $userId = api_get_user_id();
1285
            $qb->andWhere(
1286
                'p.status = 1 OR
1287
                    (p.status = '.CForumPost::STATUS_WAITING_MODERATION." AND p.posterId = $userId) OR
1288
                    (p.status = ".CForumPost::STATUS_REJECTED." AND p.posterId = $userId) OR
1289
                    (p.status IS NULL AND p.posterId = $userId)
1290
                    "
1291
            );
1292
        }
1293
    }
1294
1295
    $posts = $qb->getQuery()->getResult();
1296
    $depth++;
1297
1298
    $list = [];
1299
    /** @var CForumPost $post */
1300
    foreach ($posts as $post) {
1301
        $postInfo = [
1302
            'iid' => $post->getIid(),
1303
            'post_id' => $post->getIid(),
1304
            'post_title' => $post->getTitle(),
1305
            'post_text' => $post->getPostText(),
1306
            'thread_id' => $post->getThread() ? $post->getThread()->getIid() : 0,
1307
            'forum_id' => $post->getForum()->getIid(),
1308
            //'poster_id' => $post->getPosterId(),
1309
            //'poster_name' => $post->getPosterName(),
1310
            'post_date' => $post->getPostDate(),
1311
            'post_notification' => $post->getPostNotification(),
1312
            'post_parent_id' => $post->getPostParent() ? $post->getPostParent()->getIid() : 0,
1313
            'visible' => $post->getVisible(),
1314
            'status' => $post->getStatus(),
1315
            'indent_cnt' => $depth,
1316
            'entity' => $post,
1317
        ];
1318
1319
        $user = $post->getUser();
1320
        if ($user) {
1321
            $postInfo['user_id'] = $user->getId();
1322
            $postInfo['username'] = $user->getUsername();
1323
            $postInfo['username_canonical'] = $user->getUsernameCanonical();
1324
            $postInfo['lastname'] = $user->getLastname();
1325
            $postInfo['firstname'] = $user->getFirstname();
1326
            $postInfo['complete_name'] = UserManager::formatUserFullName($user);
1327
        }
1328
1329
        $list[] = $postInfo;
1330
1331
        if (!$recursive) {
1332
            continue;
1333
        }
1334
        $list = array_merge(
1335
            $list,
1336
            getPosts(
1337
                $forum,
1338
                $threadId,
1339
                $orderDirection,
1340
                $recursive,
1341
                $post->getIid(),
1342
                $depth
1343
            )
1344
        );
1345
    }
1346
1347
    return $list;
1348
}
1349
1350
/**
1351
 * This function retrieves forum thread users details.
1352
 *
1353
 * @return Doctrine\DBAL\Driver\Statement|null array Array of type
1354
 *                                             ([user_id=>w,lastname=>x,firstname=>y,threadId=>z],[])
1355
 *
1356
 * @author Christian Fasanando <[email protected]>,
1357
 *
1358
 * @todo     this function needs to be improved
1359
 *
1360
 * @version October 2008, dokeos 1.8
1361
 */
1362
function get_thread_users_details(int $thread_id)
1363
{
1364
    $t_posts = Database::get_course_table(TABLE_FORUM_POST);
1365
    $t_users = Database::get_main_table(TABLE_MAIN_USER);
1366
    $t_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1367
    $t_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
1368
1369
    $course_id = api_get_course_int_id();
1370
1371
    $is_western_name_order = api_is_western_name_order();
1372
    if ($is_western_name_order) {
1373
        $orderby = 'ORDER BY user.firstname, user.lastname ';
1374
    } else {
1375
        $orderby = 'ORDER BY user.lastname, user.firstname';
1376
    }
1377
1378
    $session = api_get_session_entity();
1379
1380
    if ($session) {
1381
        $generalCoachesId = $session->getGeneralCoaches()->map(fn(User $coach) => $coach->getId())->getValues();
1382
        $sessionAdminsId = $session->getSessionAdmins()->map(fn(User $admin) => $admin->getId())->getValues();
1383
        $coachesId = array_merge($generalCoachesId, $sessionAdminsId);
1384
        $user_to_avoid = implode(', ', $coachesId);
1385
        //not showing coaches
1386
        $sql = "SELECT DISTINCT user.id, user.lastname, user.firstname, thread_id
1387
                FROM $t_posts p, $t_users user, $t_session_rel_user session_rel_user_rel_course
1388
                WHERE
1389
                    p.poster_id = user.id AND
1390
                    user.id = session_rel_user_rel_course.user_id AND
1391
                    session_rel_user_rel_course.status = ".SessionEntity::STUDENT." AND
1392
                    session_rel_user_rel_course.user_id NOT IN ($user_to_avoid) AND
1393
                    p.thread_id = $thread_id AND
1394
                    session_id = ".api_get_session_id()." AND
1395
                    p.c_id = $course_id AND
1396
                    session_rel_user_rel_course.c_id = $course_id $orderby ";
1397
    } else {
1398
        $sql = "SELECT DISTINCT user.id, user.lastname, user.firstname, thread_id
1399
                FROM $t_posts p, $t_users user, $t_course_user course_user
1400
                WHERE
1401
                    p.poster_id = user.id
1402
                    AND user.id = course_user.user_id
1403
                    AND course_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
1404
                    AND p.thread_id = $thread_id
1405
                    AND course_user.status != '1' AND
1406
                    course_user.c_id = $course_id $orderby";
1407
    }
1408
1409
    return Database::query($sql);
1410
}
1411
1412
/**
1413
 * This function retrieves forum thread users qualify.
1414
 *
1415
 * @return Doctrine\DBAL\Driver\Statement|null Array of type ([user_id=>w,lastname=>x,firstname=>y,threadId=>z],[])
1416
 * @author Jhon Hinojosa
1417
 * @todo     this function needs to be improved
1418
 */
1419
function get_thread_users_qualify(int $thread_id)
1420
{
1421
    $t_posts = Database::get_course_table(TABLE_FORUM_POST);
1422
    $t_qualify = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY);
1423
    $t_users = Database::get_main_table(TABLE_MAIN_USER);
1424
    $t_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1425
    $t_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
1426
1427
    $course_id = api_get_course_int_id();
1428
    $sessionId = api_get_session_id();
1429
1430
    $is_western_name_order = api_is_western_name_order();
1431
    if ($is_western_name_order) {
1432
        $orderby = 'ORDER BY user.firstname, user.lastname ';
1433
    } else {
1434
        $orderby = 'ORDER BY user.lastname, user.firstname';
1435
    }
1436
1437
    $session = api_get_session_entity();
1438
1439
    if ($session) {
1440
        $generalCoachesId = $session->getGeneralCoaches()->map(fn(User $coach) => $coach->getId())->getValues();
1441
        $sessionAdminsId = $session->getSessionAdmins()->map(fn(User $admin) => $admin->getId())->getValues();
1442
        $coachesId = array_merge($generalCoachesId, $sessionAdminsId);
1443
        $user_to_avoid = implode(', ', $coachesId);
1444
        //not showing coaches
1445
        $sql = "SELECT DISTINCT post.poster_id, user.lastname, user.firstname, post.thread_id,user.id,qualify.qualify
1446
                FROM $t_posts post , $t_users user, $t_session_rel_user scu, $t_qualify qualify
1447
                WHERE poster_id = user.id
1448
                    AND post.poster_id = qualify.user_id
1449
                    AND user.id = scu.user_id
1450
                    AND scu.status = ".SessionEntity::STUDENT."
1451
                    AND scu.user_id NOT IN ($user_to_avoid)
1452
                    AND qualify.thread_id = $thread_id
1453
                    AND post.thread_id = $thread_id
1454
                    AND scu.session_id = $sessionId
1455
                    AND scu.c_id = $course_id AND
1456
                    qualify.c_id = $course_id AND
1457
                    post.c_id = $course_id
1458
                $orderby ";
1459
    } else {
1460
        $sql = "SELECT DISTINCT post.poster_id, user.lastname, user.firstname, post.thread_id,user.id,qualify.qualify
1461
                FROM $t_posts post,
1462
                     $t_qualify qualify,
1463
                     $t_users user,
1464
                     $t_course_user course_user
1465
                WHERE
1466
                     post.poster_id = user.id
1467
                     AND post.poster_id = qualify.user_id
1468
                     AND user.id = course_user.user_id
1469
                     AND course_user.relation_type<>".COURSE_RELATION_TYPE_RRHH."
1470
                     AND qualify.thread_id = $thread_id
1471
                     AND post.thread_id = $thread_id
1472
                     AND course_user.status not in('1')
1473
                     AND course_user.c_id = $course_id
1474
                     AND qualify.c_id = $course_id
1475
                     AND post.c_id = $course_id
1476
                 $orderby ";
1477
    }
1478
1479
    return Database::query($sql);
1480
}
1481
1482
/**
1483
 * This function retrieves forum thread users not qualify.
1484
 *
1485
 * @param int $threadId Thread ID
1486
 * @param   string  Course DB name (optional)
1487
 *
1488
 * @return Doctrine\DBAL\Driver\Statement|null Array of type ([user_id=>w,lastname=>x,firstname=>y,threadId=>z],[])
1489
 *
1490
 * @author   Jhon Hinojosa<[email protected]>,
1491
 *
1492
 * @version oct 2008, dokeos 1.8
1493
 */
1494
function get_thread_users_not_qualify($thread_id)
1495
{
1496
    $t_posts = Database::get_course_table(TABLE_FORUM_POST);
1497
    $t_qualify = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY);
1498
    $t_users = Database::get_main_table(TABLE_MAIN_USER);
1499
    $t_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1500
    $t_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
1501
1502
    $is_western_name_order = api_is_western_name_order();
1503
    if ($is_western_name_order) {
1504
        $orderby = 'ORDER BY user.firstname, user.lastname ';
1505
    } else {
1506
        $orderby = 'ORDER BY user.lastname, user.firstname';
1507
    }
1508
1509
    $course_id = api_get_course_int_id();
1510
1511
    $sql1 = "SELECT user_id FROM  $t_qualify
1512
             WHERE thread_id = '".$thread_id."'";
1513
    $result1 = Database::query($sql1);
1514
    $cad = '';
1515
    while ($row = Database::fetch_array($result1)) {
1516
        $cad .= $row['user_id'].',';
1517
    }
1518
    if ('' == $cad) {
1519
        $cad = '0';
1520
    } else {
1521
        $cad = substr($cad, 0, strlen($cad) - 1);
1522
    }
1523
1524
    $session = api_get_session_entity();
1525
1526
    if ($session) {
1527
        $generalCoachesId = $session->getGeneralCoaches()->map(fn(User $coach) => $coach->getId())->getValues();
1528
        $sessionAdminsId = $session->getSessionAdmins()->map(fn(User $admin) => $admin->getId())->getValues();
1529
        $coachesId = array_merge($generalCoachesId, $sessionAdminsId);
1530
        $user_to_avoid = implode(', ', $coachesId);
1531
        //not showing coaches
1532
        $sql = "SELECT DISTINCT user.id, user.lastname, user.firstname, post.thread_id
1533
                FROM $t_posts post , $t_users user, $t_session_rel_user session_rel_user_rel_course
1534
                WHERE poster_id = user.id
1535
                    AND user.id NOT IN (".$cad.")
1536
                    AND user.id = session_rel_user_rel_course.user_id
1537
                    AND session_rel_user_rel_course.status = ".SessionEntity::STUDENT."
1538
                    AND session_rel_user_rel_course.user_id NOT IN ($user_to_avoid)
1539
                    AND post.thread_id = ".(int) $thread_id.'
1540
                    AND session_id = '.api_get_session_id()."
1541
                    AND session_rel_user_rel_course.c_id = $course_id AND post.c_id = $course_id $orderby ";
1542
    } else {
1543
        $sql = "SELECT DISTINCT user.id, user.lastname, user.firstname, post.thread_id
1544
                FROM $t_posts post, $t_users user,$t_course_user course_user
1545
                WHERE post.poster_id = user.id
1546
                AND user.id NOT IN (".$cad.')
1547
                AND user.id = course_user.user_id
1548
                AND course_user.relation_type<>'.COURSE_RELATION_TYPE_RRHH.'
1549
                AND post.thread_id = '.(int) $thread_id."
1550
                AND course_user.status not in('1')
1551
                AND course_user.c_id = $course_id AND post.c_id = $course_id  $orderby";
1552
    }
1553
1554
    return Database::query($sql);
1555
}
1556
1557
/**
1558
 * This function counts the number of forums inside a given category.
1559
 *
1560
 * @param int $cat_id the id of the forum category
1561
 *
1562
 * @todo an additional parameter that takes the visibility into account. For instance $countinvisible=0 would return
1563
 *       the number of visible forums, $countinvisible=1 would return the number of visible and invisible forums
1564
 *
1565
 * @return int the number of forums inside the given category
1566
 *
1567
 * @author Patrick Cool <[email protected]>, Ghent University
1568
 *
1569
 * @version february 2006, dokeos 1.8
1570
 */
1571
function count_number_of_forums_in_category($cat_id)
1572
{
1573
    $table_forums = Database::get_course_table(TABLE_FORUM);
1574
    $course_id = api_get_course_int_id();
1575
    $cat_id = (int) $cat_id;
1576
    $sql = "SELECT count(*) AS number_of_forums
1577
            FROM $table_forums
1578
            WHERE forum_category = $cat_id";
1579
    $result = Database::query($sql);
1580
    $row = Database::fetch_array($result);
1581
1582
    return $row['number_of_forums'];
1583
}
1584
1585
/**
1586
 * This function update a thread.
1587
 *
1588
 * @param array $values - The form Values
1589
 */
1590
function updateThread($values)
1591
{
1592
    if (!api_is_allowed_to_edit()) {
1593
        return '';
1594
    }
1595
1596
    $logInfo = [
1597
        'tool' => TOOL_FORUM,
1598
        'tool_id' => $values['forum_id'],
1599
        'tool_id_detail' => $values['thread_id'],
1600
        'action' => 'edit-thread',
1601
        'action_details' => 'thread',
1602
        'info' => $values['thread_title'],
1603
    ];
1604
    Event::registerLog($logInfo);
1605
1606
    $threadTable = Database::get_course_table(TABLE_FORUM_THREAD);
1607
    $courseId = api_get_course_int_id();
1608
    $courseCode = api_get_course_id();
1609
    $sessionId = api_get_session_id();
1610
1611
    // Simple update + set gradebook values to null
1612
    $params = [
1613
        'title' => $values['thread_title'],
1614
        'thread_sticky' => $values['thread_sticky'] ?? 0,
1615
    ];
1616
    $where = ['iid = ?' => [$values['thread_id']]];
1617
    Database::update($threadTable, $params, $where);
1618
1619
    $id = $values['thread_id'];
1620
    $linkInfo = GradebookUtils::isResourceInCourseGradebook(
1621
        $courseId,
1622
        LINK_FORUM_THREAD,
1623
        $id,
1624
        $sessionId
1625
    );
1626
1627
    $gradebookLink = null;
1628
    $em = Database::getManager();
1629
    if (!empty($linkInfo) && isset($linkInfo['id'])) {
1630
        $gradebookLink = $em->getRepository(GradebookLink::class)->find($linkInfo['id']);
1631
    }
1632
1633
    // values 1 or 0
1634
    $check = isset($values['thread_qualify_gradebook']) ? $values['thread_qualify_gradebook'] : false;
1635
    if ($check) {
1636
        $title = Security::remove_XSS(stripslashes($values['calification_notebook_title']));
1637
        $value = isset($values['numeric_calification']) ? (int) ($values['numeric_calification']) : 0;
1638
        $weight = isset($values['weight_calification']) ? (float) ($values['weight_calification']) : 0;
1639
        $description = '';
1640
        // Update title
1641
        $params = [
1642
            'thread_title_qualify' => $values['calification_notebook_title'],
1643
            'thread_qualify_max' => api_float_val($values['numeric_calification']),
1644
            'thread_weight' => api_float_val($values['weight_calification']),
1645
            'thread_peer_qualify' => $values['thread_peer_qualify'],
1646
        ];
1647
        $where = ['iid = ?' => [$values['thread_id']]];
1648
        Database::update($threadTable, $params, $where);
1649
1650
        if (!$linkInfo) {
1651
            GradebookUtils::add_resource_to_course_gradebook(
1652
                $values['category_id'],
1653
                $courseId,
1654
                LINK_FORUM_THREAD,
1655
                $id,
1656
                $title,
1657
                $weight,
1658
                $value,
1659
                $description,
1660
                1,
1661
                $sessionId
1662
            );
1663
        } else {
1664
            if ($gradebookLink) {
1665
                $gradebookLink->setWeight($weight);
1666
                $em->persist($gradebookLink);
1667
                $em->flush();
1668
            }
1669
        }
1670
    } else {
1671
        $params = [
1672
            'thread_title_qualify' => '',
1673
            'thread_qualify_max' => 0,
1674
            'thread_weight' => 0,
1675
            'thread_peer_qualify' => 0,
1676
        ];
1677
        $where = ['iid = ?' => [$values['thread_id']]];
1678
        Database::update($threadTable, $params, $where);
1679
1680
        if (!empty($linkInfo)) {
1681
            if ($gradebookLink) {
1682
                $em->remove($gradebookLink);
1683
                $em->flush();
1684
            }
1685
        }
1686
    }
1687
1688
    $message = get_lang('The post has been modified').'<br />';
1689
    Display::addFlash(Display::return_message($message, 'confirmation', false));
1690
}
1691
1692
function saveThread(
1693
    CForum $forum,
1694
    array $values,
1695
    array $courseInfo = [],
1696
    $showMessage = true,
1697
    $userId = 0,
1698
    $sessionId = 0
1699
): ?CForumThread {
1700
    $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo;
1701
    $userId = $userId ?: api_get_user_id();
1702
    $course_id = $courseInfo['real_id'];
1703
    $courseCode = $courseInfo['code'];
1704
    $sessionId = $sessionId ?: api_get_session_id();
1705
    $table_threads = Database::get_course_table(TABLE_FORUM_THREAD);
1706
1707
    $post_date = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
1708
    $visible = true;
1709
    if ('1' == $forum->getApprovalDirectPost() && !api_is_allowed_to_edit(null, true)) {
1710
        $visible = false; // The post has not been approved yet.
1711
    }
1712
    $clean_post_title = $values['post_title'];
1713
1714
    $user = api_get_user_entity(api_get_user_id());
1715
    $course = api_get_course_entity($course_id);
1716
    $session = api_get_session_entity($sessionId);
1717
1718
    // We first store an entry in the forum_thread table because the threadId is used in the forum_post table.
1719
    $thread = new CForumThread();
1720
    $thread
1721
        ->setTitle($clean_post_title)
1722
        ->setForum($forum)
1723
        ->setUser($user)
1724
        ->setThreadDate($post_date)
1725
        ->setThreadSticky((bool) ($values['thread_sticky'] ?? false))
1726
        ->setThreadTitleQualify($values['calification_notebook_title'] ?? '')
1727
        ->setThreadQualifyMax(api_float_val($values['numeric_calification'] ?? 0))
1728
        ->setThreadWeight(api_float_val($values['weight_calification'] ?? 0))
1729
        ->setThreadPeerQualify(isset($values['thread_peer_qualify']) ? (bool) $values['thread_peer_qualify'] : false)
1730
        ->setParent($forum)
1731
        ->addCourseLink($course, $session)
1732
    ;
1733
    $em = Database::getManager();
1734
    $itemId = isset($values['lp_item_id']) ? (int) $values['lp_item_id'] : 0;
1735
    if (!empty($itemId)) {
1736
        $item = $em->getRepository(CLpItem::class)->find($itemId);
1737
        $thread->setItem($item);
1738
    }
1739
1740
    $repo = Container::getForumThreadRepository();
1741
    $repo->create($thread);
1742
1743
    if (!$thread->getIid()) {
1744
        return null;
1745
    }
1746
1747
    // Add option gradebook qualify.
1748
    if (isset($values['thread_qualify_gradebook']) &&
1749
        1 == $values['thread_qualify_gradebook']
1750
    ) {
1751
        // Add function gradebook.
1752
        $resourcename = stripslashes($values['calification_notebook_title']);
1753
        GradebookUtils::add_resource_to_course_gradebook(
1754
            $values['category_id'],
1755
            $course_id,
1756
            5,
1757
            $thread->getIid(),
1758
            $resourcename,
1759
            $values['weight_calification'],
1760
            $values['numeric_calification'],
1761
            '',
1762
            0,
1763
            $sessionId
1764
        );
1765
    }
1766
1767
    $logInfo = [
1768
        'tool' => TOOL_FORUM,
1769
        'tool_id' => $values['forum_id'],
1770
        'tool_id_detail' => $thread->getIid(),
1771
        'action' => 'new-thread',
1772
        'info' => $clean_post_title,
1773
    ];
1774
    Event::registerLog($logInfo);
1775
1776
    // We now store the content in the table_post table.
1777
    $post = new CForumPost();
1778
    $post
1779
        ->setTitle($clean_post_title)
1780
        ->setPostText($values['post_text'])
1781
        ->setThread($thread)
1782
        ->setForum($forum)
1783
        ->setUser(api_get_user_entity($userId))
1784
        ->setPostDate($post_date)
1785
        ->setPostNotification(isset($values['post_notification']) ? (bool) $values['post_notification'] : false)
1786
        ->setVisible($visible)
1787
        ->setStatus(CForumPost::STATUS_VALIDATED)
1788
        ->setParent($thread)
1789
        ->addCourseLink($course, $session)
1790
    ;
1791
1792
    if ($forum->isModerated()) {
1793
        $post->setStatus(
1794
            api_is_course_admin() ? CForumPost::STATUS_VALIDATED : CForumPost::STATUS_WAITING_MODERATION
1795
        );
1796
    }
1797
1798
    $repo = Container::getForumPostRepository();
1799
    $repo->create($post);
1800
    $thread->setThreadLastPost($post);
1801
    $em = Database::getManager();
1802
    $em->persist($thread);
1803
    $em->flush();
1804
1805
    $postId = $post->getIid();
1806
1807
    $logInfo = [
1808
        'tool' => TOOL_FORUM,
1809
        'tool_id' => $values['forum_id'],
1810
        'tool_id_detail' => $thread->getIid(),
1811
        'action' => 'new-post',
1812
        'info' => $clean_post_title,
1813
    ];
1814
    Event::registerLog($logInfo);
1815
1816
    // Now we have to update the thread table to fill the thread_last_post
1817
    // field (so that we know when the thread has been updated for the last time).
1818
    $sql = "UPDATE $table_threads
1819
            SET thread_last_post = '".$postId."'
1820
            WHERE
1821
                iid = '".$thread->getIid()."'";
1822
    Database::query($sql);
1823
1824
    $message = '';
1825
    if ($showMessage) {
1826
        Display::addFlash(Display::return_message(get_lang('The new thread has been added'), 'success', false));
1827
    }
1828
1829
    // Overwrite default message.
1830
    if ($forum->isModerated() &&
1831
        !api_is_allowed_to_edit(null, true)
1832
    ) {
1833
        if ($showMessage) {
1834
            Display::addFlash(Display::return_message(get_lang('Your message has to be approved before people can view it.'), 'success', false));
1835
        }
1836
    }
1837
1838
    add_forum_attachment_file(
1839
        null,
1840
        $post
1841
    );
1842
1843
    if ('1' == $forum->getApprovalDirectPost() &&
1844
        !api_is_allowed_to_edit(null, true)
1845
    ) {
1846
        $message .= get_lang('Your message has to be approved before people can view it.').'<br />';
1847
        $message .= get_lang('You can now return to the').
1848
            ' <a href="viewforum.php?'.api_get_cidreq(true, true, false).'&forum='.$values['forum_id'].'">'.
1849
            get_lang('Forum').'</a><br />';
1850
    } else {
1851
        $message .= get_lang('You can now return to the').
1852
            ' <a href="viewforum.php?'.api_get_cidreq(true, true, false).'&forum='.$values['forum_id'].'">'.
1853
            get_lang('Forum').'</a><br />';
1854
        $message .= get_lang('You can now return to the').
1855
            ' <a href="viewthread.php?'.api_get_cidreq(true, true, false).'&forum='.$values['forum_id'].'&thread='.$thread->getIid().'">'.
1856
            get_lang('Message').'</a>';
1857
    }
1858
    $reply_info['new_post_id'] = $postId;
1859
    $my_post_notification = isset($values['post_notification']) ? $values['post_notification'] : null;
1860
1861
    if (1 == $my_post_notification) {
1862
        set_notification('thread', $thread->getIid(), true);
1863
    }
1864
1865
    send_notification_mails(
1866
        $forum,
1867
        $thread,
1868
        $reply_info,
1869
        $course
1870
    );
1871
1872
    Session::erase('formelements');
1873
    Session::erase('origin');
1874
    Session::erase('breadcrumbs');
1875
    Session::erase('addedresource');
1876
    Session::erase('addedresourceid');
1877
1878
    if ($showMessage) {
1879
        Display::addFlash(Display::return_message($message, 'success', false));
1880
    }
1881
1882
    return $thread;
1883
}
1884
1885
/**
1886
 * This function displays the form that is used to add a post. This can be a new thread or a reply.
1887
 *
1888
 * @param string $action
1889
 *                            is the parameter that determines if we are
1890
 *                            2. replythread: Replying to a thread ($action = replythread) => I-frame with the complete
1891
 *                            thread (if enabled)
1892
 *                            3. replymessage: Replying to a message ($action =replymessage) => I-frame with the
1893
 *                            complete thread (if enabled)
1894
 *                            (I first thought to put and I-frame with the message only)
1895
 *                            4. quote: Quoting a message ($action= quotemessage) => I-frame with the complete thread
1896
 *                            (if enabled). The message will be in the reply. (I first thought not to put an I-frame
1897
 *                            here)
1898
 * @param array  $form_values
1899
 * @param bool   $showPreview
1900
 *
1901
 * @return FormValidator
1902
 */
1903
function show_add_post_form(CForum $forum, CForumThread $thread, CForumPost $post = null, $action, $form_values, $showPreview = true)
1904
{
1905
    $_user = api_get_user_info();
1906
    $action = isset($action) ? Security::remove_XSS($action) : '';
1907
    $threadId = $thread->getIid();
1908
    $forumId = $forum->getIid();
1909
    $giveRevision = isset($_GET['give_revision']) && 1 == $_GET['give_revision'];
1910
    $postId = $post ? $post->getIid() : 0;
1911
1912
    $url = api_get_self().'?'.http_build_query(
1913
        [
1914
            'action' => $action,
1915
            'forum' => $forumId,
1916
            'thread' => $threadId,
1917
            'post' => $postId,
1918
        ]
1919
    ).'&'.api_get_cidreq();
1920
1921
    $form = new FormValidator(
1922
        'thread',
1923
        'post',
1924
        $url
1925
    );
1926
    $form->setConstants(['forum' => '5']);
1927
1928
    // Setting the form elements.
1929
    $form->addElement('hidden', 'forum_id', $forumId);
1930
    $form->addElement('hidden', 'thread_id', $threadId);
1931
    $form->addElement('hidden', 'action', $action);
1932
1933
    // If anonymous posts are allowed we also display a form to allow the user to put his name or username in.
1934
    if (1 == $forum->getAllowAnonymous() && !isset($_user['user_id'])) {
1935
        $form->addElement('text', 'poster_name', get_lang('Name'));
1936
        $form->applyFilter('poster_name', 'html_filter');
1937
    }
1938
1939
    $form->addElement('text', 'post_title', get_lang('Title'));
1940
    $form->addHtmlEditor(
1941
        'post_text',
1942
        get_lang('Text'),
1943
        true,
1944
        false,
1945
        api_is_allowed_to_edit(null, true) ? [
1946
            'ToolbarSet' => 'Forum',
1947
            'Width' => '100%',
1948
            'Height' => '300',
1949
        ] : [
1950
            'ToolbarSet' => 'ForumStudent',
1951
            'Width' => '100%',
1952
            'Height' => '300',
1953
            'UserStatus' => 'student',
1954
        ]
1955
    );
1956
    $form->addRule('post_text', get_lang('Required field'), 'required');
1957
1958
    if (in_array($action, ['replythread', 'replymessage', 'quote'])) {
1959
        $extraFields = new ExtraField('forum_post');
1960
        $extraFields->addElements(
1961
            $form,
1962
            null,
1963
            [], //exclude
1964
            false, // filter
1965
            false, // tag as select
1966
            ['ask_for_revision'], //show only fields
1967
            [], // order fields
1968
            [] // extra data);
1969
        );
1970
    }
1971
1972
    $iframe = null;
1973
    if ($showPreview) {
1974
        if ('newthread' !== $action && !empty($threadId)) {
1975
            $iframe = '<iframe style="border: 1px solid black"
1976
            src="'.api_get_path(WEB_CODE_PATH).'forum/iframe_thread.php?'.api_get_cidreq().'&forum='.$forumId.'&thread='.$threadId.'#'.$postId.'" width="100%"></iframe>';
1977
        }
1978
        if (!empty($iframe)) {
1979
            $form->addElement('label', get_lang('Thread'), $iframe);
1980
        }
1981
    }
1982
1983
    if (in_array($action, ['quote', 'replymessage'])) {
1984
        $form->addFile('user_upload[]', get_lang('Attachment'));
1985
        $form->addButton(
1986
            'add_attachment',
1987
            get_lang('Add attachment'),
1988
            'paperclip',
1989
            'plain',
1990
            'plain',
1991
            null,
1992
            ['id' => 'reply-add-attachment']
1993
        );
1994
    } else {
1995
        $form->addFile('user_upload', get_lang('Attachment'));
1996
    }
1997
1998
    if ($giveRevision) {
1999
        $hide = ('true' === api_get_setting('forum.hide_forum_post_revision_language'));
2000
        $form->addHidden('give_revision', 1);
2001
        if (false === $hide) {
2002
            $extraField = new ExtraField('forum_post');
2003
            $extraField->addElements(
2004
                $form,
2005
                null,
2006
                [], //exclude
2007
                false, // filter
2008
                false, // tag as select
2009
                ['revision_language'], //show only fields
2010
                [], // order fields
2011
                [] // extra data
2012
            );
2013
        } else {
2014
            $form->addHidden('extra_revision_language', 1);
2015
        }
2016
    }
2017
2018
    // Setting the class and text of the form title and submit button.
2019
    if ('quote' === $action) {
2020
        $form->addButtonCreate(get_lang('Quote this message'), 'SubmitPost');
2021
    } elseif ('replythread' === $action) {
2022
        $form->addButtonCreate(get_lang('Reply to this thread'), 'SubmitPost');
2023
    } elseif ('replymessage' === $action) {
2024
        $form->addButtonCreate(get_lang('Reply to this message'), 'SubmitPost');
2025
    }
2026
2027
    $defaults['thread_peer_qualify'] = 0;
2028
    if (!empty($form_values)) {
2029
        $defaults['post_title'] = prepare4display($form_values['post_title']);
2030
        $defaults['post_text'] = prepare4display($form_values['post_text']);
2031
        $defaults['post_notification'] = (int) $form_values['post_notification'];
2032
        $defaults['thread_sticky'] = (int) $form_values['thread_sticky'];
2033
        $defaults['thread_peer_qualify'] = (int) $form_values['thread_peer_qualify'];
2034
    }
2035
2036
    // If we are quoting a message we have to retrieve the information of the post we are quoting so that
2037
    // we can add this as default to the textarea.
2038
    // We also need to put the parent_id of the post in a hidden form when
2039
    if (('quote' === $action || 'replymessage' === $action || $giveRevision) && !empty($postId)) {
2040
        // we are quoting or replying to a message (<> reply to a thread !!!)
2041
        $form->addHidden('post_parent_id', $post->getIid());
2042
        // If we are replying or are quoting then we display a default title.
2043
        $posterName = UserManager::formatUserFullName($post->getUser());
2044
        $defaults['post_title'] = get_lang('Re:').api_html_entity_decode($post->getTitle(), ENT_QUOTES);
2045
        // When we are quoting a message then we have to put that message into the wysiwyg editor.
2046
        // Note: The style has to be hardcoded here because using class="quote" didn't work.
2047
        if ('quote' === $action) {
2048
            $defaults['post_text'] = '<div>&nbsp;</div>
2049
                <div style="margin: 5px;">
2050
                    <div style="font-size: 90%; font-style: italic;">'.
2051
                get_lang('Quoting').' '.$posterName.':</div>
2052
                        <div style="color: #006600; font-size: 90%;  font-style: italic; background-color: #FAFAFA; border: #D1D7DC 1px solid; padding: 3px;">'.
2053
                prepare4display($post->getPostText()).'
2054
                        </div>
2055
                    </div>
2056
                <div>&nbsp;</div>
2057
                <div>&nbsp;</div>
2058
            ';
2059
        }
2060
        if ($giveRevision) {
2061
            $defaults['post_text'] = prepare4display($post->getPostText());
2062
        }
2063
    }
2064
2065
    $form->setDefaults(isset($defaults) ? $defaults : []);
2066
2067
    // The course admin can make a thread sticky (=appears with special icon and always on top).
2068
    $form->addRule('post_title', get_lang('Required field'), 'required');
2069
    if (1 == $forum->getAllowAnonymous() && !isset($_user['user_id'])) {
2070
        $form->addRule(
2071
            'poster_name',
2072
            get_lang('Required field'),
2073
            'required'
2074
        );
2075
    }
2076
2077
    // Validation or display
2078
    if ($form->validate()) {
2079
        $check = Security::check_token('post');
2080
        if ($check) {
2081
            $values = $form->getSubmitValues();
2082
            if (isset($values['thread_qualify_gradebook']) &&
2083
                '1' == $values['thread_qualify_gradebook'] &&
2084
                empty($values['weight_calification'])
2085
            ) {
2086
                Display::addFlash(
2087
                    Display::return_message(
2088
                        get_lang('You must assign a score to this activity').'&nbsp;<a href="javascript:window.history.go(-1);">'.get_lang('Back').'</a>',
2089
                        'error',
2090
                        false
2091
                    )
2092
                );
2093
2094
                return false;
2095
            }
2096
2097
            $postId = 0;
2098
            $threadId = 0;
2099
2100
            switch ($action) {
2101
                case 'quote':
2102
                case 'replythread':
2103
                case 'replymessage':
2104
                    $postId = store_reply($forum, $thread, $values);
2105
2106
                    break;
2107
            }
2108
2109
            if ($postId) {
2110
                if (isset($values['give_revision']) && 1 == $values['give_revision']) {
2111
                    $extraFieldValues = new ExtraFieldValue('forum_post');
2112
                    $revisionLanguage = isset($values['extra_revision_language']) ? $values['extra_revision_language'] : '';
2113
                    $params = [
2114
                        'item_id' => $postId,
2115
                        'extra_revision_language' => $revisionLanguage,
2116
                    ];
2117
2118
                    $extraFieldValues->saveFieldValues(
2119
                        $params,
2120
                        false,
2121
                        false,
2122
                        ['revision_language']
2123
                    );
2124
                }
2125
2126
                if (in_array($action, ['replythread', 'replymessage', 'quote'])) {
2127
                    $extraFieldValues = new ExtraFieldValue('forum_post');
2128
                    $params = [
2129
                        'item_id' => $postId,
2130
                        'extra_ask_for_revision' => isset($values['extra_ask_for_revision']) ? $values['extra_ask_for_revision'] : '',
2131
                    ];
2132
                    $extraFieldValues->saveFieldValues(
2133
                        $params,
2134
                        false,
2135
                        false,
2136
                        ['ask_for_revision']
2137
                    );
2138
                }
2139
            }
2140
2141
            $url = api_get_path(WEB_CODE_PATH).'forum/viewthread.php?'.api_get_cidreq().'&'.http_build_query(
2142
                [
2143
                    'forum' => $forumId,
2144
                    'thread' => $thread->getIid(),
2145
                ]
2146
            );
2147
2148
            Security::clear_token();
2149
            header('Location: '.$url);
2150
            exit;
2151
        }
2152
    } else {
2153
        $token = Security::get_token();
2154
        $form->addElement('hidden', 'sec_token');
2155
        $form->setConstants(['sec_token' => $token]);
2156
2157
        // Delete from $_SESSION forum attachment from other posts
2158
        // and keep only attachments for new post
2159
        //clearAttachedFiles(FORUM_NEW_POST);
2160
        // Get forum attachment ajax table to add it to form
2161
        $attachmentAjaxTable = getAttachmentsAjaxTable(0, $forum->getIid());
2162
        $ajaxHtml = $attachmentAjaxTable;
2163
        $form->addElement('html', $ajaxHtml);
2164
2165
        return $form;
2166
    }
2167
}
2168
2169
function newThread(CForum $forum, $form_values = '', $showPreview = true)
2170
{
2171
    $_user = api_get_user_info();
2172
    $forumId = $forum->getIid();
2173
    $my_post = isset($_GET['post']) ? (int) $_GET['post'] : '';
2174
    $giveRevision = isset($_GET['give_revision']) && 1 == $_GET['give_revision'];
2175
    $action = 'new_thread';
2176
2177
    $url = api_get_self().'?'.http_build_query(
2178
            [
2179
                'action' => $action,
2180
                'forum' => $forumId,
2181
                'post' => $my_post,
2182
            ]
2183
        ).'&'.api_get_cidreq();
2184
2185
    $form = new FormValidator(
2186
        'thread',
2187
        'post',
2188
        $url
2189
    );
2190
2191
    // Setting the form elements.
2192
    $form->addElement('hidden', 'forum_id', $forumId);
2193
    $form->addElement('hidden', 'thread_id', 0);
2194
    $form->addElement('hidden', 'action', $action);
2195
2196
    // If anonymous posts are allowed we also display a form to allow the user to put his name or username in.
2197
    if (1 == $forum->getAllowAnonymous() && !isset($_user['user_id'])) {
2198
        $form->addElement('text', 'poster_name', get_lang('Name'));
2199
        $form->applyFilter('poster_name', 'html_filter');
2200
    }
2201
2202
    $form->addElement('text', 'post_title', get_lang('Title'));
2203
    $form->addHtmlEditor(
2204
        'post_text',
2205
        get_lang('Text'),
2206
        true,
2207
        false,
2208
        api_is_allowed_to_edit(null, true) ? [
2209
            'ToolbarSet' => 'Forum',
2210
            'Width' => '100%',
2211
            'Height' => '300',
2212
        ] : [
2213
            'ToolbarSet' => 'ForumStudent',
2214
            'Width' => '100%',
2215
            'Height' => '300',
2216
            'UserStatus' => 'student',
2217
        ]
2218
    );
2219
    $form->addRule('post_text', get_lang('Required field'), 'required');
2220
2221
    $extraFields = new ExtraField('forum_post');
2222
    $extraFields->addElements(
2223
        $form,
2224
        null,
2225
        [], //exclude
2226
        false, // filter
2227
        false, // tag as select
2228
        ['ask_for_revision'], //show only fields
2229
        [], // order fields
2230
        [] // extra data);
2231
    );
2232
2233
    if (Gradebook::is_active() &&
2234
        (api_is_course_admin() || api_is_session_general_coach() || api_is_course_tutor())
2235
    ) {
2236
        $form->addElement('advanced_settings', 'advanced_params', get_lang('Advanced settings'));
2237
        $form->addElement('html', '<div id="advanced_params_options" style="display:none">');
2238
2239
        // Thread qualify
2240
        if (Gradebook::is_active()) {
2241
            //Loading gradebook select
2242
            GradebookUtils::load_gradebook_select_in_tool($form);
2243
            $form->addCheckBox(
2244
                'thread_qualify_gradebook',
2245
                '',
2246
                get_lang('Grade this thread'),
2247
                ['onclick' => 'javascript:if(this.checked==true){document.getElementById(\'options_field\').style.display = \'block\';}else{document.getElementById(\'options_field\').style.display = \'none\';}']
2248
            );
2249
        } else {
2250
            $form->addElement('hidden', 'thread_qualify_gradebook', false);
2251
        }
2252
2253
        $form->addElement('html', '<div id="options_field" style="display:none">');
2254
        $form->addElement('text', 'numeric_calification', get_lang('Maximum score'));
2255
        $form->applyFilter('numeric_calification', 'html_filter');
2256
        $form->addElement('text', 'calification_notebook_title', get_lang('Column header in Competences Report'));
2257
        $form->applyFilter('calification_notebook_title', 'html_filter');
2258
2259
        $form->addElement(
2260
            'text',
2261
            'weight_calification',
2262
            get_lang('Weight in Report'),
2263
            ['value' => '0.00', 'onfocus' => 'javascript: this.select();']
2264
        );
2265
        $form->applyFilter('weight_calification', 'html_filter');
2266
2267
        $group = [];
2268
        $group[] = $form->createElement('radio', 'thread_peer_qualify', null, get_lang('Yes'), 1);
2269
        $group[] = $form->createElement('radio', 'thread_peer_qualify', null, get_lang('No'), 0);
2270
        $form->addGroup(
2271
            $group,
2272
            '',
2273
            [
2274
                get_lang('Thread scored by peers'),
2275
                get_lang('Thread scored by peersComment'),
2276
            ]
2277
        );
2278
        $form->addElement('html', '</div>');
2279
        $form->addElement('html', '</div>');
2280
    }
2281
2282
    SkillModel::addSkillsToForm($form, ITEM_TYPE_FORUM_THREAD, 0);
2283
    $form->addElement('checkbox', 'thread_sticky', '', get_lang('This is a sticky message (appears always on top and has a special sticky icon)'));
2284
2285
    $form->addFile('user_upload', get_lang('Attachment'));
2286
2287
    if ($giveRevision) {
2288
        $hide = ('true' === api_get_setting('forum.hide_forum_post_revision_language'));
2289
        $form->addHidden('give_revision', 1);
2290
        if (false === $hide) {
2291
            $extraField = new ExtraField('forum_post');
2292
            $extraField->addElements(
2293
                $form,
2294
                null,
2295
                [], //exclude
2296
                false, // filter
2297
                false, // tag as select
2298
                ['revision_language'], //show only fields
2299
                [], // order fields
2300
                [] // extra data
2301
            );
2302
        } else {
2303
            $form->addHidden('extra_revision_language', 1);
2304
        }
2305
    }
2306
    $form->addButtonCreate(get_lang('Create thread'), 'SubmitPost');
2307
2308
    $defaults['thread_peer_qualify'] = 0;
2309
    if (!empty($form_values)) {
2310
        $defaults['post_title'] = prepare4display($form_values['post_title']);
2311
        $defaults['post_text'] = prepare4display($form_values['post_text']);
2312
        $defaults['post_notification'] = (int) $form_values['post_notification'];
2313
        $defaults['thread_sticky'] = (int) $form_values['thread_sticky'];
2314
        $defaults['thread_peer_qualify'] = (int) $form_values['thread_peer_qualify'];
2315
    }
2316
2317
    $form->setDefaults(isset($defaults) ? $defaults : []);
2318
2319
    // The course admin can make a thread sticky (=appears with special icon and always on top).
2320
    $form->addRule('post_title', get_lang('Required field'), 'required');
2321
    if (1 == $forum->getAllowAnonymous() && !isset($_user['user_id'])) {
2322
        $form->addRule(
2323
            'poster_name',
2324
            get_lang('Required field'),
2325
            'required'
2326
        );
2327
    }
2328
2329
    // Validation or display
2330
    if ($form->validate()) {
2331
        $check = Security::check_token('post');
2332
        if ($check) {
2333
            $values = $form->getSubmitValues();
2334
            if (isset($values['thread_qualify_gradebook']) &&
2335
                '1' == $values['thread_qualify_gradebook'] &&
2336
                empty($values['weight_calification'])
2337
            ) {
2338
                Display::addFlash(
2339
                    Display::return_message(
2340
                        get_lang('You must assign a score to this activity').'&nbsp;<a href="javascript:window.history.go(-1);">'.get_lang('Back').'</a>',
2341
                        'error',
2342
                        false
2343
                    )
2344
                );
2345
2346
                return false;
2347
            }
2348
2349
            $newThread = saveThread($forum, $values);
2350
            if ($newThread) {
2351
                SkillModel::saveSkills($form, ITEM_TYPE_FORUM_THREAD, $newThread->getIid());
2352
                $post = $newThread->getThreadLastPost();
2353
2354
                if ($post) {
2355
                    $postId = $post->getIid();
2356
2357
                    if (isset($values['give_revision']) && 1 == $values['give_revision']) {
2358
                        $extraFieldValues = new ExtraFieldValue('forum_post');
2359
                        $revisionLanguage = isset($values['extra_revision_language']) ? $values['extra_revision_language'] : '';
2360
2361
                        $params = [
2362
                            'item_id' => $postId,
2363
                            'extra_revision_language' => $revisionLanguage,
2364
                        ];
2365
2366
                        $extraFieldValues->saveFieldValues(
2367
                            $params,
2368
                            false,
2369
                            false,
2370
                            ['revision_language']
2371
                        );
2372
                    }
2373
                    $extraFieldValues = new ExtraFieldValue('forum_post');
2374
                    $params = [
2375
                        'item_id' => $postId,
2376
                        'extra_ask_for_revision' => $values['extra_ask_for_revision'] ?? '',
2377
                    ];
2378
                    $extraFieldValues->saveFieldValues(
2379
                        $params,
2380
                        false,
2381
                        false,
2382
                        ['ask_for_revision']
2383
                    );
2384
                }
2385
            }
2386
2387
            $url = api_get_path(WEB_CODE_PATH).'forum/viewthread.php?'.api_get_cidreq().'&'.http_build_query(
2388
                [
2389
                    'forum' => $forumId,
2390
                    'thread' => $newThread->getIid(),
2391
                ]
2392
            );
2393
2394
            Security::clear_token();
2395
            header('Location: '.$url);
2396
            exit;
2397
        }
2398
    } else {
2399
        $token = Security::get_token();
2400
        $form->addElement('hidden', 'sec_token');
2401
        $form->setConstants(['sec_token' => $token]);
2402
2403
        // Delete from $_SESSION forum attachment from other posts
2404
        // and keep only attachments for new post
2405
        //clearAttachedFiles(FORUM_NEW_POST);
2406
        // Get forum attachment ajax table to add it to form
2407
        $attachmentAjaxTable = getAttachmentsAjaxTable(0, $forum->getIid());
2408
        $ajaxHtml = $attachmentAjaxTable;
2409
        $form->addElement('html', $ajaxHtml);
2410
2411
        return $form;
2412
    }
2413
}
2414
2415
/**
2416
 * @param CForumThread $threadInfo
2417
 * @param int          $user_id
2418
 * @param int          $thread_id
2419
 * @param int          $thread_qualify
2420
 * @param int          $qualify_time
2421
 * @param int          $session_id
2422
 *
2423
 * @return string
2424
 *
2425
 * @author Isaac Flores <[email protected]>, U.N.A.S University
2426
 *
2427
 * @version October 2008, dokeos  1.8.6
2428
 */
2429
function saveThreadScore(
2430
    CForumThread $threadEntity,
2431
    $user_id,
2432
    $thread_id,
2433
    $thread_qualify,
2434
    $qualify_time,
2435
    $session_id
2436
) {
2437
    $table_threads_qualify = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY);
2438
2439
    $course_id = api_get_course_int_id();
2440
    $session_id = (int) $session_id;
2441
    $thread_id = (int) $thread_id;
2442
    $user_id = (int) $user_id;
2443
    $thread_qualify = (float) $thread_qualify;
2444
    $currentUserId = api_get_user_id();
2445
    $qualify_time = Database::escape_string($qualify_time);
2446
2447
    $max = $threadEntity->getThreadQualifyMax();
2448
    if ($thread_qualify <= $max) {
2449
        if ($threadEntity->isThreadPeerQualify()) {
2450
            $sql = "SELECT COUNT(*) FROM $table_threads_qualify
2451
                    WHERE
2452
                        user_id = $user_id AND
2453
                        qualify_user_id = $currentUserId AND
2454
                        thread_id = ".$thread_id;
2455
        } else {
2456
            $sql = "SELECT COUNT(*) FROM $table_threads_qualify
2457
                    WHERE
2458
                        user_id = $user_id AND
2459
                        thread_id = ".$thread_id;
2460
        }
2461
2462
        $result = Database::query($sql);
2463
        $row = Database::fetch_array($result);
2464
2465
        if (0 == $row[0]) {
2466
            $sql = "INSERT INTO $table_threads_qualify (c_id, user_id, thread_id,qualify,qualify_user_id,qualify_time,session_id)
2467
                    VALUES (".$course_id.", '".$user_id."','".$thread_id."',".$thread_qualify.", '".$currentUserId."','".$qualify_time."','".$session_id."')";
2468
            Database::query($sql);
2469
2470
            return 'insert';
2471
        } else {
2472
            saveThreadScoreHistory(
2473
                '1',
2474
                $course_id,
2475
                $user_id,
2476
                $thread_id
2477
            );
2478
2479
            // Update
2480
            $sql = "UPDATE $table_threads_qualify
2481
                    SET
2482
                        qualify = '".$thread_qualify."',
2483
                        qualify_time = '".$qualify_time."'
2484
                    WHERE
2485
                        user_id=".$user_id.' AND
2486
                        thread_id='.$thread_id." AND
2487
                        qualify_user_id = $currentUserId
2488
                    ";
2489
            Database::query($sql);
2490
2491
            return 'update';
2492
        }
2493
    }
2494
2495
    return '';
2496
}
2497
2498
/**
2499
 * This function shows qualify.
2500
 *
2501
 * @param string $option    contains the information of option to run
2502
 * @param int    $user_id   contains the information the current user id
2503
 * @param int    $thread_id contains the information the current thread id
2504
 *
2505
 * @return int qualify
2506
 *             <code> $option=1 obtained the qualification of the current thread</code>
2507
 *
2508
 * @author Isaac Flores <[email protected]>, U.N.A.S University
2509
 *
2510
 * @version October 2008, dokeos  1.8.6
2511
 */
2512
function showQualify($option, $user_id, $thread_id)
2513
{
2514
    $table_threads_qualify = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY);
2515
    $table_threads = Database::get_course_table(TABLE_FORUM_THREAD);
2516
2517
    $course_id = api_get_course_int_id();
2518
    $user_id = (int) $user_id;
2519
    $thread_id = (int) $thread_id;
2520
2521
    if (empty($user_id) || empty($thread_id)) {
2522
        return 0;
2523
    }
2524
2525
    $sql = '';
2526
    switch ($option) {
2527
        case 1:
2528
            $sql = "SELECT qualify FROM $table_threads_qualify
2529
                    WHERE
2530
                        c_id = $course_id AND
2531
                        user_id=".$user_id.' AND
2532
                        thread_id='.$thread_id;
2533
2534
            break;
2535
        case 2:
2536
            $sql = "SELECT thread_qualify_max FROM $table_threads
2537
                    WHERE c_id = $course_id AND iid=".$thread_id;
2538
2539
            break;
2540
    }
2541
2542
    if (!empty($sql)) {
2543
        $rs = Database::query($sql);
2544
        $row = Database::fetch_array($rs);
2545
        if ($row) {
2546
            return $row[0];
2547
        }
2548
    }
2549
2550
    return 0;
2551
}
2552
2553
/**
2554
 * This function gets qualify historical.
2555
 *
2556
 * @param int  $user_id   contains the information the current user id
2557
 * @param int  $thread_id contains the information the current thread id
2558
 * @param bool $opt       contains the information of option to run
2559
 *
2560
 * @return array
2561
 *
2562
 * @author Christian Fasanando <[email protected]>,
2563
 * @author Isaac Flores <[email protected]>,
2564
 *
2565
 * @version October 2008, dokeos  1.8.6
2566
 */
2567
function getThreadScoreHistory($user_id, $thread_id, $opt)
2568
{
2569
    $user_id = (int) $user_id;
2570
    $thread_id = (int) $thread_id;
2571
2572
    $table_threads_qualify_log = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY_LOG);
2573
    $course_id = api_get_course_int_id();
2574
2575
    if ('false' == $opt) {
2576
        $sql = "SELECT * FROM $table_threads_qualify_log
2577
                WHERE
2578
                    c_id = $course_id AND
2579
                    thread_id='".$thread_id."' AND
2580
                    user_id='".$user_id."'
2581
                ORDER BY qualify_time";
2582
    } else {
2583
        $sql = "SELECT * FROM $table_threads_qualify_log
2584
                WHERE
2585
                    c_id = $course_id AND
2586
                    thread_id='".$thread_id."' AND
2587
                    user_id='".$user_id."'
2588
                ORDER BY qualify_time DESC";
2589
    }
2590
    $rs = Database::query($sql);
2591
    $log = [];
2592
    while ($row = Database::fetch_assoc($rs)) {
2593
        $log[] = $row;
2594
    }
2595
2596
    return $log;
2597
}
2598
2599
/**
2600
 * This function stores qualify historical.
2601
 *
2602
 * @param bool contains the information of option to run
2603
 * @param string contains the information the current course id
2604
 * @param int contains the information the current forum id
2605
 * @param int contains the information the current user id
2606
 * @param int contains the information the current thread id
2607
 * @param int contains the information the current qualify
2608
 * @param string $option
2609
 * @param int    $course_id
2610
 * @param int    $user_id
2611
 * @param int    $thread_id
2612
 *
2613
 * @author Isaac Flores <[email protected]>, U.N.A.S University
2614
 *
2615
 * @version October 2008, dokeos  1.8.6
2616
 */
2617
function saveThreadScoreHistory(
2618
    $option,
2619
    $course_id,
2620
    $user_id,
2621
    $thread_id
2622
) {
2623
    $table_threads_qualify = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY);
2624
    $table_threads_qualify_log = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY_LOG);
2625
2626
    $thread_id = (int) $thread_id;
2627
    $course_id = (int) $course_id;
2628
    $user_id = (int) $user_id;
2629
    $qualify_user_id = api_get_user_id();
2630
2631
    if (1 == $option) {
2632
        // Extract information of thread_qualify.
2633
        $sql = "SELECT qualify, qualify_time
2634
                FROM $table_threads_qualify
2635
                WHERE
2636
                    c_id = $course_id AND
2637
                    user_id = ".$user_id.' AND
2638
                    thread_id = '.$thread_id." AND
2639
                    qualify_user_id = $qualify_user_id
2640
                ";
2641
        $rs = Database::query($sql);
2642
        $row = Database::fetch_array($rs);
2643
2644
        // Insert thread_historical.
2645
        $sql = "INSERT INTO $table_threads_qualify_log (c_id, user_id, thread_id, qualify, qualify_user_id,qualify_time,session_id)
2646
                VALUES(".$course_id.", '".$user_id."','".$thread_id."',".(float) $row[0].", '".$qualify_user_id."','".$row[1]."','')";
2647
        Database::query($sql);
2648
    }
2649
}
2650
2651
/**
2652
 * This function shows current thread qualify .
2653
 *
2654
 * @param int $threadId
2655
 * @param int $sessionId
2656
 * @param int $userId
2657
 *
2658
 * @return array or null if is empty
2659
 *
2660
 * @author Isaac Flores <[email protected]>, U.N.A.S University
2661
 *
2662
 * @version December 2008, dokeos  1.8.6
2663
 */
2664
function current_qualify_of_thread($threadId, $sessionId, $userId)
2665
{
2666
    $table_threads_qualify = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY);
2667
2668
    $course_id = api_get_course_int_id();
2669
    $currentUserId = api_get_user_id();
2670
    $sessionId = (int) $sessionId;
2671
    $threadId = (int) $threadId;
2672
2673
    $sql = "SELECT qualify FROM $table_threads_qualify
2674
            WHERE
2675
                c_id = $course_id AND
2676
                thread_id = $threadId AND
2677
                session_id = $sessionId AND
2678
                qualify_user_id = $currentUserId AND
2679
                user_id = $userId
2680
            ";
2681
    $res = Database::query($sql);
2682
    $row = Database::fetch_assoc($res);
2683
2684
    if ($row) {
2685
        return $row['qualify'];
2686
    }
2687
2688
    return 0;
2689
}
2690
2691
/**
2692
 * This function stores a reply in the forum_post table.
2693
 * It also updates the forum_threads table (thread_replies +1 , thread_last_post, thread_date).
2694
 *
2695
 * @param array $values
2696
 * @param int   $courseId Optional
2697
 * @param int   $userId   Optional
2698
 *
2699
 * @return int post id
2700
 */
2701
function store_reply(CForum $forum, CForumThread $thread, $values, $courseId = 0, $userId = 0)
2702
{
2703
    $courseId = !empty($courseId) ? $courseId : api_get_course_int_id();
2704
    $post_date = api_get_utc_datetime();
2705
    $userId = $userId ?: api_get_user_id();
2706
2707
    if (1 == $forum->getAllowAnonymous()) {
2708
        if (api_is_anonymous() && empty($userId)) {
2709
            $userId = api_get_anonymous_id();
2710
        }
2711
    }
2712
2713
    if (empty($userId)) {
2714
        return false;
2715
    }
2716
2717
    $visible = 1;
2718
    if ('1' == $forum->getApprovalDirectPost() &&
2719
        !api_is_allowed_to_edit(null, true)
2720
    ) {
2721
        $visible = 0;
2722
    }
2723
2724
    $upload_ok = 1;
2725
    $new_post_id = 0;
2726
2727
    if ($upload_ok) {
2728
        $course = api_get_course_entity($courseId);
2729
        $session = api_get_session_entity();
2730
2731
        $repo = Container::getForumPostRepository();
2732
        $post = new CForumPost();
2733
        $text = empty($values['post_text']) ? '' : $values['post_text'];
2734
        $post
2735
            ->setTitle($values['post_title'])
2736
            ->setPostText($text)
2737
            ->setThread($thread)
2738
            ->setForum($forum)
2739
            ->setUser(api_get_user_entity($userId))
2740
            ->setPostNotification(isset($values['post_notification']) ? (bool) $values['post_notification'] : false)
2741
            ->setVisible($visible)
2742
            ->setPostDate(api_get_utc_datetime(null, false, true))
2743
            ->setParent($thread)
2744
            ->addCourseLink($course, $session)
2745
        ;
2746
2747
        if (isset($values['post_parent_id']) && !empty($values['post_parent_id'])) {
2748
            $parent = $repo->find($values['post_parent_id']);
2749
            $post->setPostParent($parent);
2750
        }
2751
        $repo->create($post);
2752
2753
        $new_post_id = $post->getIid();
2754
        if ($new_post_id) {
2755
            $values['new_post_id'] = $new_post_id;
2756
            $message = get_lang('The reply has been added');
2757
2758
            if (!empty($_POST['file_ids']) && is_array($_POST['file_ids'])) {
2759
                foreach ($_POST['file_ids'] as $key => $id) {
2760
                    editAttachedFile(
2761
                        [
2762
                            'comment' => $_POST['file_comments'][$key],
2763
                            'post_id' => $new_post_id,
2764
                        ],
2765
                        $id
2766
                    );
2767
                }
2768
            }
2769
2770
            // Update the thread.
2771
            updateThreadInfo($values['thread_id'], $new_post_id, $post_date);
2772
2773
            if ('1' == $forum->getApprovalDirectPost() &&
2774
                !api_is_allowed_to_edit(null, true)
2775
            ) {
2776
                $message .= '<br />'.get_lang('Your message has to be approved before people can view it.').'<br />';
2777
            }
2778
2779
            if ($forum->isModerated() &&
2780
                !api_is_allowed_to_edit(null, true)
2781
            ) {
2782
                $message .= '<br />'.get_lang('Your message has to be approved before people can view it.').'<br />';
2783
            }
2784
2785
            // Setting the notification correctly.
2786
            $my_post_notification = isset($values['post_notification']) ? $values['post_notification'] : null;
2787
            if (1 == $my_post_notification) {
2788
                set_notification('thread', $values['thread_id'], true);
2789
            }
2790
2791
            send_notification_mails(
2792
                $forum,
2793
                $thread,
2794
                $values,
2795
                $course
2796
            );
2797
            add_forum_attachment_file('', $post);
2798
2799
            $logInfo = [
2800
                'tool' => TOOL_FORUM,
2801
                'tool_id' => $values['forum_id'],
2802
                'tool_id_detail' => $values['thread_id'],
2803
                'action' => 'new-post',
2804
                'action_details' => $values['action'],
2805
                'info' => $values['post_title'],
2806
            ];
2807
            Event::registerLog($logInfo);
2808
        }
2809
2810
        Session::erase('formelements');
2811
        Session::erase('origin');
2812
        Session::erase('breadcrumbs');
2813
        Session::erase('addedresource');
2814
        Session::erase('addedresourceid');
2815
2816
        Display::addFlash(Display::return_message($message, 'confirmation', false));
2817
    } else {
2818
        Display::addFlash(
2819
            Display::return_message(
2820
                get_lang('No file was uploaded.').' '.get_lang('Please select a file before pressing the upload button.'),
2821
                'error'
2822
            )
2823
        );
2824
    }
2825
2826
    return $new_post_id;
2827
}
2828
2829
/**
2830
 * This function displays the form that is used to edit a post. This can be a new thread or a reply.
2831
 *
2832
 * @param CForumPost   $post        contains all the information about the current post
2833
 * @param CForumThread $thread      contains all the information about the current thread
2834
 * @param CForum       $forum       contains all info about the current forum (to check if attachments are allowed)
2835
 * @param array        $form_values contains the default values to fill the form
2836
 *
2837
 * @author Patrick Cool <[email protected]>, Ghent University
2838
 *
2839
 * @version february 2006, dokeos 1.8
2840
 */
2841
function show_edit_post_form(
2842
    $post,
2843
    $thread,
2844
    $forum,
2845
    $form_values,
2846
    $id_attach = 0
2847
) {
2848
    // Initialize the object.
2849
    $form = new FormValidator(
2850
        'edit_post',
2851
        'post',
2852
        api_get_self().'?'.api_get_cidreq().'&forum='.(int) ($_GET['forum']).'&thread='.(int) ($_GET['thread']).'&post='.(int) ($_GET['post'])
2853
    );
2854
    $form->addElement('header', get_lang('Edit a post'));
2855
    // Setting the form elements.
2856
    $form->addElement('hidden', 'post_id', $post->getIid());
2857
    $form->addElement('hidden', 'thread_id', $thread->getIid());
2858
    $form->addElement('hidden', 'id_attach', $id_attach);
2859
2860
    if (null === $post->getPostParent()) {
2861
        $form->addElement('hidden', 'is_first_post_of_thread', '1');
2862
    }
2863
2864
    $form->addElement('text', 'post_title', get_lang('Title'));
2865
    $form->applyFilter('post_title', 'html_filter');
2866
    $form->addElement(
2867
        'html_editor',
2868
        'post_text',
2869
        get_lang('Text'),
2870
        null,
2871
        api_is_allowed_to_edit(null, true) ? [
2872
            'ToolbarSet' => 'Forum',
2873
            'Width' => '100%',
2874
            'Height' => '400',
2875
        ] : [
2876
            'ToolbarSet' => 'ForumStudent',
2877
            'Width' => '100%',
2878
            'Height' => '400',
2879
            'UserStatus' => 'student',
2880
        ]
2881
    );
2882
    $form->addRule('post_text', get_lang('Required field'), 'required');
2883
2884
    $extraFields = new ExtraField('forum_post');
2885
    $extraFields->addElements($form, $post->getIid());
2886
2887
    $form->addButtonAdvancedSettings('advanced_params');
2888
    $form->addElement('html', '<div id="advanced_params_options" style="display:none">');
2889
2890
    if ($forum->isModerated() && api_is_allowed_to_edit(null, true)) {
2891
        $group = [];
2892
        $group[] = $form->createElement(
2893
            'radio',
2894
            'status',
2895
            null,
2896
            get_lang('Validated'),
2897
            1
2898
        );
2899
        $group[] = $form->createElement(
2900
            'radio',
2901
            'status',
2902
            null,
2903
            get_lang('Waiting for moderation'),
2904
            2
2905
        );
2906
        $group[] = $form->createElement(
2907
            'radio',
2908
            'status',
2909
            null,
2910
            get_lang('Rejected'),
2911
            3
2912
        );
2913
        $form->addGroup($group, 'status', get_lang('Status'));
2914
    }
2915
2916
    $defaults['status']['status'] = $post->getStatus();
2917
2918
    $form->addElement(
2919
        'checkbox',
2920
        'post_notification',
2921
        '',
2922
        get_lang('Notify me by e-mail when somebody replies')
2923
    );
2924
2925
    if (api_is_allowed_to_edit(null, true) && null === $post->getPostParent()) {
2926
        // The sticky checkbox only appears when it is the first post of a thread.
2927
        $form->addElement(
2928
            'checkbox',
2929
            'thread_sticky',
2930
            '',
2931
            get_lang('This is a sticky message (appears always on top and has a special sticky icon)')
2932
        );
2933
        if (1 == $thread->getThreadSticky()) {
2934
            $defaults['thread_sticky'] = true;
2935
        }
2936
    }
2937
2938
    $form->addElement('html', '</div>');
2939
2940
    $form->addFile('user_upload[]', get_lang('Attachment'));
2941
    $form->addButton(
2942
        'add_attachment',
2943
        get_lang('Add attachment'),
2944
        'paperclip',
2945
        'default',
2946
        'default',
2947
        null,
2948
        ['id' => 'reply-add-attachment']
2949
    );
2950
2951
    $form->addButtonUpdate(get_lang('Edit'), 'SubmitPost');
2952
2953
    // Setting the default values for the form elements.
2954
    $defaults['post_title'] = $post->getTitle();
2955
    $defaults['post_text'] = $post->getPostText();
2956
2957
    if (1 == $post->getPostNotification()) {
2958
        $defaults['post_notification'] = true;
2959
    }
2960
2961
    if (!empty($form_values)) {
2962
        $defaults['post_notification'] = Security::remove_XSS($form_values['post_notification']);
2963
        $defaults['thread_sticky'] = Security::remove_XSS($form_values['thread_sticky']);
2964
    }
2965
2966
    $form->setDefaults($defaults);
2967
2968
    // The course admin can make a thread sticky (=appears with special icon and always on top).
2969
    $form->addRule('post_title', get_lang('Required field'), 'required');
2970
2971
    // Validation or display
2972
    if ($form->validate()) {
2973
        $values = $form->exportValues();
2974
        $values['item_id'] = $post->getIid();
2975
        $extraFieldValues = new ExtraFieldValue('forum_post');
2976
        $extraFieldValues->saveFieldValues($values);
2977
2978
        store_edit_post($forum, $values);
2979
    } else {
2980
        // Delete from $_SESSION forum attachment from other posts
2981
        clearAttachedFiles($post->getIid());
2982
        // Get forum attachment ajax table to add it to form
2983
        $fileData = getAttachmentsAjaxTable($post->getIid(), $forum->getIid());
2984
        $form->addElement('html', $fileData);
2985
        $form->display();
2986
    }
2987
}
2988
2989
/**
2990
 * This function stores the edit of a post in the forum_post table.
2991
 *
2992
 * @author Patrick Cool <[email protected]>, Ghent University
2993
 *
2994
 * @version february 2006, dokeos 1.8
2995
 */
2996
function store_edit_post(CForum $forum, $values)
2997
{
2998
    $logInfo = [
2999
        'tool' => TOOL_FORUM,
3000
        'tool_id' => $_GET['forum'],
3001
        'tool_id_detail' => $values['thread_id'],
3002
        'action' => 'edit-post',
3003
        'action_details' => 'post',
3004
        'info' => $values['post_title'],
3005
    ];
3006
    Event::registerLog($logInfo);
3007
3008
    $threadTable = Database::get_course_table(TABLE_FORUM_THREAD);
3009
3010
    //check if this post is the first of the thread
3011
    // First we check if the change affects the thread and if so we commit
3012
    // the changes (sticky and post_title=thread_title are relevant).
3013
    $posts = getPosts($forum, $values['thread_id']);
3014
    $first_post = null;
3015
    if (!empty($posts) && count($posts) > 0 && isset($posts[0])) {
3016
        $first_post = $posts[0];
3017
    }
3018
3019
    if (!empty($first_post) && $first_post['post_id'] == $values['post_id']) {
3020
        // Simple edit
3021
        $params = [
3022
            'title' => $values['post_title'],
3023
            'thread_sticky' => isset($values['thread_sticky']) ? $values['thread_sticky'] : 0,
3024
        ];
3025
        $where = ['iid = ?' => [$values['thread_id']]];
3026
        Database::update($threadTable, $params, $where);
3027
    }
3028
3029
    $status = '';
3030
    $updateStatus = false;
3031
    if ($forum->isModerated()) {
3032
        if (api_is_allowed_to_edit(null, true)) {
3033
            $status = $values['status']['status'];
3034
            $updateStatus = true;
3035
        } else {
3036
            $status = CForumPost::STATUS_WAITING_MODERATION;
3037
            $updateStatus = true;
3038
        }
3039
    }
3040
3041
    $postId = $values['post_id'];
3042
    $repo = Container::getForumPostRepository();
3043
    /** @var CForumPost $post */
3044
    $post = $repo->find($postId);
3045
    if ($post) {
3046
        $post
3047
            ->setTitle($values['post_title'])
3048
            ->setPostText($values['post_text'])
3049
            ->setPostNotification(isset($values['post_notification']))
3050
        ;
3051
3052
        if ($updateStatus) {
3053
            $post->setStatus($status);
3054
        }
3055
        $repo->update($post);
3056
    }
3057
3058
    // Update attached files
3059
    if (!empty($_POST['file_ids']) && is_array($_POST['file_ids'])) {
3060
        foreach ($_POST['file_ids'] as $key => $id) {
3061
            editAttachedFile(
3062
                [
3063
                    'comment' => $_POST['file_comments'][$key],
3064
                    'post_id' => $values['post_id'],
3065
                ],
3066
                $id
3067
            );
3068
        }
3069
    }
3070
3071
    if (!empty($values['remove_attach'])) {
3072
        throw new Exception('remove_attach');
3073
    }
3074
3075
    if (empty($values['id_attach'])) {
3076
        add_forum_attachment_file(
3077
            isset($values['file_comment']) ? $values['file_comment'] : null,
3078
            $post
3079
        );
3080
    }
3081
3082
    $message = get_lang('The post has been modified').'<br />';
3083
    $message .= get_lang('You can now return to the').
3084
        ' <a href="viewforum.php?'.api_get_cidreq().'&forum='.(int) ($_GET['forum']).'&">'.
3085
        get_lang('Forum').'</a><br />';
3086
    $message .= get_lang('You can now return to the').
3087
        ' <a
3088
            href="viewthread.php?'.api_get_cidreq().'&forum='.(int) ($_GET['forum']).'&thread='.$values['thread_id'].'&post='.Security::remove_XSS($_GET['post']).'">'.
3089
        get_lang('Message').'</a>';
3090
3091
    Session::erase('formelements');
3092
    Session::erase('origin');
3093
    Session::erase('breadcrumbs');
3094
    Session::erase('addedresource');
3095
    Session::erase('addedresourceid');
3096
3097
    echo Display::return_message($message, 'confirmation', false);
3098
}
3099
3100
function displayUserLink(User $user)
3101
{
3102
    return '<a href="'.$user->getProfileUrl().'">'.
3103
        Security::remove_XSS(UserManager::formatUserFullName($user)).'</a>';
3104
}
3105
3106
function displayUserImage(User $user)
3107
{
3108
    $url = Container::getIllustrationRepository()->getIllustrationUrl($user, '', ICON_SIZE_BIG);
3109
3110
    return '<div class="thumbnail"><img src="'.$url.'?w=100"/></div>';
3111
}
3112
3113
/**
3114
 * The relies counter gets increased every time somebody replies to the thread.
3115
 *
3116
 * @author Patrick Cool <[email protected]>, Ghent University
3117
 *
3118
 * @version february 2006, dokeos 1.8
3119
 *
3120
 * @param int    $threadId
3121
 * @param string $lastPostId
3122
 * @param string $post_date
3123
 */
3124
function updateThreadInfo($threadId, $lastPostId, $post_date)
3125
{
3126
    $table_threads = Database::get_course_table(TABLE_FORUM_THREAD);
3127
    $threadId = (int) $threadId;
3128
    $lastPostId = (int) $lastPostId;
3129
3130
    $sql = "UPDATE $table_threads SET
3131
            thread_replies = thread_replies+1,
3132
            thread_last_post = '".$lastPostId."',
3133
            thread_date = '".Database::escape_string($post_date)."'
3134
            WHERE
3135
                iid ='".$threadId."'"; // this needs to be cleaned first
3136
    Database::query($sql);
3137
}
3138
3139
function approvePost(CForumPost $post, $action)
3140
{
3141
    if ('invisible' === $action) {
3142
        $visibility = 0;
3143
    }
3144
3145
    if ('visible' === $action) {
3146
        $visibility = 1;
3147
        handle_mail_cue('post', $post->getIid());
3148
    }
3149
3150
    $post->setVisible($visibility);
3151
    Database::getManager()->persist($post);
3152
    Database::getManager()->flush();
3153
3154
    Display::addFlash(Display::return_message(get_lang('The visibility has been changed')));
3155
}
3156
3157
/**
3158
 * This function retrieves all the unapproved messages for a given forum
3159
 * This is needed to display the icon that there are unapproved messages in that thread (only the courseadmin can see
3160
 * this).
3161
 *
3162
 * @param int $forum_id forum where we want to know the unapproved messages of
3163
 *
3164
 * @return array returns
3165
 *
3166
 * @author Patrick Cool <[email protected]>, Ghent University
3167
 *
3168
 * @version february 2006, dokeos 1.8
3169
 */
3170
function get_unaproved_messages($forum_id)
3171
{
3172
    $table_posts = Database::get_course_table(TABLE_FORUM_POST);
3173
    $course_id = api_get_course_int_id();
3174
3175
    $return_array = [];
3176
    $sql = "SELECT DISTINCT thread_id FROM $table_posts
3177
            WHERE
3178
                c_id = $course_id AND
3179
                forum_id='".Database::escape_string($forum_id)."' AND
3180
                visible='0' ";
3181
    $result = Database::query($sql);
3182
    while ($row = Database::fetch_array($result)) {
3183
        $return_array[] = $row['thread_id'];
3184
    }
3185
3186
    return $return_array;
3187
}
3188
3189
/**
3190
 * This function sends the notification mails to everybody who stated that they wanted to be informed when a new post
3191
 * was added to a given thread.
3192
 */
3193
function send_notification_mails(CForum $forum, CForumThread $thread, $reply_info, ?Course $course = null)
3194
{
3195
    $courseEntity = api_get_course_entity();
3196
3197
    if (null !== $course) {
3198
        $courseEntity = $course;
3199
    }
3200
3201
    $courseId = $courseEntity->getId();
3202
3203
    $sessionId = api_get_session_id();
3204
    $sessionEntity = api_get_session_entity($sessionId);
3205
3206
    $table = Database::get_course_table(TABLE_FORUM_MAIL_QUEUE);
3207
3208
    // First we need to check if
3209
    // 1. the forum category is visible
3210
    // 2. the forum is visible
3211
    // 3. the thread is visible
3212
    // 4. the reply is visible (=when there is)
3213
3214
    $current_forum_category = null;
3215
    if ($forum->getForumCategory()) {
3216
        $current_forum_category = $forum->getForumCategory();
3217
    }
3218
3219
    $send_mails = false;
3220
    if ($thread->isVisible($courseEntity) &&
3221
        $forum->isVisible($courseEntity) &&
3222
        ($current_forum_category && $forum->getForumCategory()->isVisible($courseEntity)) &&
3223
        '1' != $forum->getApprovalDirectPost()
3224
    ) {
3225
        $send_mails = true;
3226
    }
3227
3228
    // The forum category, the forum, the thread and the reply are visible to the user
3229
    if ($send_mails && !empty($forum)) {
3230
        $postId = isset($reply_info['new_post_id']) ? $reply_info['new_post_id'] : 0;
3231
        send_notifications($forum, $thread, $postId);
3232
    } else {
3233
        $table_notification = Database::get_course_table(TABLE_FORUM_NOTIFICATION);
3234
        if ($forum) {
3235
            $sql = "SELECT * FROM $table_notification
3236
                    WHERE
3237
                        c_id = ".$courseId." AND
3238
                        (
3239
                            forum_id = '".$forum->getIid()."' OR
3240
                            thread_id = '".$thread->getIid()."'
3241
                        ) ";
3242
            $result = Database::query($sql);
3243
            $user_id = api_get_user_id();
3244
            while ($row = Database::fetch_array($result)) {
3245
                $sql = "INSERT INTO $table (c_id, thread_id, post_id, user_id)
3246
                        VALUES (".$courseId.", '".$thread->getIid()."', '".(int) ($reply_info['new_post_id'])."', '$user_id' )";
3247
                Database::query($sql);
3248
            }
3249
        }
3250
    }
3251
}
3252
3253
/**
3254
 * This function is called whenever something is made visible because there might
3255
 * be new posts and the user might have indicated that (s)he wanted to be
3256
 * informed about the new posts by mail.
3257
 *
3258
 * @param string $content Content type (post, thread, forum, forum_category)
3259
 * @param int    $id      Item DB ID of the corresponding content type
3260
 *
3261
 * @return string language variable
3262
 *
3263
 * @author Patrick Cool <[email protected]>, Ghent University
3264
 *
3265
 * @version february 2006, dokeos 1.8
3266
 */
3267
function handle_mail_cue($content, $id)
3268
{
3269
    $table_mailcue = Database::get_course_table(TABLE_FORUM_MAIL_QUEUE);
3270
    $table_forums = Database::get_course_table(TABLE_FORUM);
3271
    $table_threads = Database::get_course_table(TABLE_FORUM_THREAD);
3272
    $table_posts = Database::get_course_table(TABLE_FORUM_POST);
3273
    $table_users = Database::get_main_table(TABLE_MAIN_USER);
3274
3275
    $course_id = api_get_course_int_id();
3276
    $id = (int) $id;
3277
3278
    /* If the post is made visible we only have to send mails to the people
3279
     who indicated that they wanted to be informed for that thread.*/
3280
    if ('post' === $content) {
3281
        // Getting the information about the post (need the threadId).
3282
        /** @var CForumPost $post */
3283
        $post = Container::getForumPostRepository()->find($id);
3284
        $thread_id = $post->getThread()->getIid();
3285
3286
        // Sending the mail to all the users that wanted to be informed for replies on this thread.
3287
        $sql = "SELECT users.firstname, users.lastname, users.id as user_id, users.email
3288
                FROM $table_mailcue mailcue, $table_posts posts, $table_users users
3289
                WHERE
3290
                    mailcue.c_id = $course_id AND
3291
                    posts.thread_id = $thread_id AND
3292
                    posts.post_notification = '1' AND
3293
                    mailcue.thread_id = $thread_id AND
3294
                    users.id = posts.poster_id AND
3295
                    users.active = 1
3296
                GROUP BY users.email";
3297
3298
        $result = Database::query($sql);
3299
        $forum = Container::getForumRepository()->find($post->getForum()->getIid());
3300
3301
        while ($row = Database::fetch_array($result)) {
3302
            send_mail($row, $forum, $post->getThread(), $post);
3303
        }
3304
    } elseif ('thread' === $content) {
3305
        // Sending the mail to all the users that wanted to be informed for replies on this thread.
3306
        $sql = "SELECT users.firstname, users.lastname, users.id as user_id, users.email, posts.forum_id
3307
                FROM $table_mailcue mailcue, $table_posts posts, $table_users users
3308
                WHERE
3309
                    mailcue.c_id = $course_id AND
3310
                    posts.thread_id = $id AND
3311
                    posts.post_notification = '1' AND
3312
                    mailcue.thread_id = $id AND
3313
                    users.id = posts.poster_id AND
3314
                    users.active = 1
3315
                GROUP BY users.email";
3316
        $result = Database::query($sql);
3317
        while ($row = Database::fetch_array($result)) {
3318
            $forum = Container::getForumRepository()->find($row['forum_id']);
3319
            $thread = Container::getForumThreadRepository()->find($id);
3320
            send_mail($row, $forum, $thread);
3321
        }
3322
3323
        // Deleting the relevant entries from the mailcue.
3324
        $sql = "DELETE FROM $table_mailcue
3325
                WHERE c_id = $course_id AND thread_id = $id";
3326
        Database::query($sql);
3327
    } elseif ('forum' === $content) {
3328
        $sql = "SELECT iid FROM $table_threads
3329
                WHERE forum_id = $id";
3330
        $result = Database::query($sql);
3331
        while ($row = Database::fetch_array($result)) {
3332
            handle_mail_cue('thread', $row['iid']);
3333
        }
3334
    } elseif ('forum_category' === $content) {
3335
        $sql = "SELECT iid FROM $table_forums
3336
                WHERE forum_category = $id";
3337
        $result = Database::query($sql);
3338
        while ($row = Database::fetch_array($result)) {
3339
            handle_mail_cue('forum', $row['iid']);
3340
        }
3341
    } else {
3342
        return get_lang('Error');
3343
    }
3344
}
3345
3346
/**
3347
 * This function sends the mails for the mail notification.
3348
 */
3349
function send_mail($userInfo, CForum $forum, CForumThread $thread, CForumPost $postInfo = null)
3350
{
3351
    if (empty($userInfo) || empty($forum) || empty($thread)) {
3352
        return false;
3353
    }
3354
3355
    $_course = api_get_course_info();
3356
    $user_id = api_get_user_id();
3357
    $forumId = $forum->getIid();
3358
    $threadId = $thread->getIid();
3359
3360
    $thread_link = api_get_path(WEB_CODE_PATH).
3361
        'forum/viewthread.php?'.api_get_cidreq(true, true, false).'&forum='.$forumId.'&thread='.$threadId;
3362
3363
    $email_body = get_lang('Dear').' '.
3364
        api_get_person_name($userInfo['firstname'], $userInfo['lastname'], null, PERSON_NAME_EMAIL_ADDRESS).", <br />\n\r";
3365
    $email_body .= get_lang('New Post in the forum').
3366
        ': '.$forum->getTitle().' - '.$thread->getTitle()." <br />\n";
3367
3368
    $courseId = (int) api_get_setting('forum.global_forums_course_id');
3369
    $subject = get_lang('New Post in the forum').' - '.
3370
        $_course['official_code'].': '.$forum->getTitle().' - '.$thread->getTitle()." <br />\n";
3371
3372
    $courseInfoTitle = get_lang('Course').': '.$_course['name'].' - ['.$_course['official_code']."] - <br />\n";
3373
    if (!empty($courseId) && $_course['real_id'] == $courseId) {
3374
        $subject = get_lang('New Post in the forum').': '.
3375
            $forum->getTitle().' - '.$thread->getTitle()." <br />\n";
3376
        $courseInfoTitle = " <br />\n";
3377
    }
3378
    $email_body .= $courseInfoTitle;
3379
3380
    if (!empty($postInfo)) {
3381
        $text = cut(strip_tags($postInfo->getPostText()), 100);
3382
        if (!empty($text)) {
3383
            $email_body .= get_lang('Message').": <br />\n ";
3384
            $email_body .= $text;
3385
            $email_body .= "<br /><br />\n";
3386
        }
3387
    }
3388
3389
    $email_body .= get_lang('You stated that you wanted to be informed by e-mail whenever somebody replies on the thread')."<br />\n";
3390
3391
    if (!empty($thread_link)) {
3392
        $email_body .= get_lang('The thread can be found here').' : <br /><a href="'.$thread_link.'">'.$thread_link."</a>\n";
3393
    }
3394
3395
    if ($userInfo['user_id'] != $user_id) {
3396
        MessageManager::send_message(
3397
            $userInfo['user_id'],
3398
            $subject,
3399
            $email_body,
3400
            [],
3401
            [],
3402
            null,
3403
            null,
3404
            null,
3405
            null,
3406
            $user_id
3407
        );
3408
    }
3409
}
3410
3411
/**
3412
 * This function displays the form for moving a thread to a different (already existing) forum.
3413
 *
3414
 * @author Patrick Cool <[email protected]>, Ghent University
3415
 *
3416
 * @version february 2006, dokeos 1.8
3417
 */
3418
function move_thread_form()
3419
{
3420
    $form = new FormValidator(
3421
        'movepost',
3422
        'post',
3423
        api_get_self().'?forum='.(int) ($_GET['forum']).'&thread='.(int) ($_GET['thread']).'&action='.Security::remove_XSS($_GET['action']).'&'.api_get_cidreq()
3424
    );
3425
    $form->addHeader(get_lang('Move Thread'));
3426
    // Invisible form: the threadId
3427
    $form->addHidden('thread_id', (int) ($_GET['thread']));
3428
    $forum_categories = get_forum_categories();
3429
3430
    $htmlcontent = '<div class="row">
3431
        <div class="label">
3432
            <span class="form_required">*</span>'.get_lang('Move to').'
3433
        </div>
3434
        <div class="formw">';
3435
    $htmlcontent .= '<select name="forum">';
3436
    foreach ($forum_categories as $category) {
3437
        $htmlcontent .= '<optgroup label="'.$category->getTitle().'">';
3438
        $forums = $category->getForums();
3439
        foreach ($forums as $forum) {
3440
            $htmlcontent .= '<option value="'.$forum->getIid().'">'.$forum->getTitle().'</option>';
3441
        }
3442
        $htmlcontent .= '</optgroup>';
3443
    }
3444
    $htmlcontent .= '</select>';
3445
    $htmlcontent .= '   </div>
3446
                    </div>';
3447
3448
    $form->addElement('html', $htmlcontent);
3449
3450
    // The OK button
3451
    $form->addButtonSave(get_lang('Move Thread'), 'SubmitForum');
3452
3453
    // Validation or display
3454
    if ($form->validate()) {
3455
        $values = $form->exportValues();
3456
        if (isset($_POST['forum'])) {
3457
            store_move_thread($values);
3458
            Display::addFlash(Display::return_message(get_lang('Moved')));
3459
        }
3460
    } else {
3461
        return $form->returnForm();
3462
    }
3463
}
3464
3465
/**
3466
 * This function displays the form for moving a post message to a different (already existing) or a new thread.
3467
 *
3468
 * @author Patrick Cool <[email protected]>, Ghent University
3469
 *
3470
 * @version february 2006, dokeos 1.8
3471
 */
3472
function move_post_form()
3473
{
3474
    $form = new FormValidator(
3475
        'movepost',
3476
        'post',
3477
        api_get_self().'?'.api_get_cidreq().'&forum='.(int) ($_GET['forum']).'&thread='.(int) ($_GET['thread']).'&post='.Security::remove_XSS($_GET['post']).'&action='.Security::remove_XSS($_GET['action']).'&post='.Security::remove_XSS($_GET['post'])
3478
    );
3479
    // The header for the form
3480
    $form->addElement('header', '', get_lang('Move post'));
3481
3482
    // Invisible form: the post_id
3483
    $form->addElement('hidden', 'post_id', (int) ($_GET['post']));
3484
3485
    // Dropdown list: Threads of this forum
3486
    $threads = get_threads($_GET['forum']);
3487
    $threads_list[0] = get_lang('A new thread');
3488
    foreach ($threads as $thread) {
3489
        $threads_list[$thread->getIid()] = $thread->getTitle();
3490
    }
3491
    $form->addSelect('thread', get_lang('Move toThread'), $threads_list);
3492
    $form->applyFilter('thread', 'html_filter');
3493
3494
    // The OK button
3495
    $form->addButtonSave(get_lang('Move post'), 'submit');
3496
3497
    // Setting the rules
3498
    $form->addRule('thread', get_lang('Required field'), 'required');
3499
3500
    return $form;
3501
}
3502
3503
/**
3504
 * @param array $values
3505
 *
3506
 * @return string HTML language variable
3507
 *
3508
 * @author Patrick Cool <[email protected]>, Ghent University
3509
 *
3510
 * @version february 2006, dokeos 1.8
3511
 */
3512
function store_move_post($values)
3513
{
3514
    $_course = api_get_course_info();
3515
    $course_id = api_get_course_int_id();
3516
3517
    $table_forums = Database::get_course_table(TABLE_FORUM);
3518
    $table_threads = Database::get_course_table(TABLE_FORUM_THREAD);
3519
    $table_posts = Database::get_course_table(TABLE_FORUM_POST);
3520
3521
    $user = api_get_user_entity(api_get_user_id());
3522
    $course = api_get_course_entity($course_id);
3523
    $session = api_get_session_entity();
3524
3525
    if ('0' == $values['thread']) {
3526
        $repoPost = Container::getForumPostRepository();
3527
3528
        /** @var CForumPost $post */
3529
        $post = $repoPost->find($values['post_id']);
3530
        $forumId = $post->getForum()->getIid();
3531
        $threadId = $post->getThread()->getIid();
3532
3533
        $thread = new CForumThread();
3534
        $thread
3535
            ->setTitle($post->getTitle())
3536
            ->setForum($post->getForum())
3537
            ->setUser($post->getUser())
3538
            ->setThreadLastPost($post)
3539
            ->setThreadDate($post->getPostDate())
3540
            ->setParent($post->getForum())
3541
            ->addCourseLink(
3542
                $user,
3543
                $course,
3544
                $session
3545
            );
3546
3547
        $repo = Container::getForumThreadRepository();
3548
        $repo->create($thread);
3549
        $new_thread_id = $thread->getIid();
3550
3551
        // Moving the post to the newly created thread.
3552
        $sql = "UPDATE $table_posts SET thread_id='".$new_thread_id."', post_parent_id = NULL
3553
                WHERE iid ='".(int) ($values['post_id'])."'";
3554
        Database::query($sql);
3555
3556
        // Resetting the parent_id of the thread to 0 for all those who had this moved post as parent.
3557
        $sql = "UPDATE $table_posts SET post_parent_id = NULL
3558
                WHERE post_parent_id='".(int) ($values['post_id'])."'";
3559
        Database::query($sql);
3560
3561
        // Updating updating the number of threads in the forum.
3562
        $sql = "UPDATE $table_forums SET forum_threads=forum_threads+1
3563
                WHERE iid ='".$forumId."'";
3564
        Database::query($sql);
3565
3566
        // Resetting the last post of the old thread and decreasing the number of replies and the thread.
3567
        $sql = "SELECT * FROM $table_posts
3568
                WHERE thread_id='".$threadId."'
3569
                ORDER BY iid DESC";
3570
        $result = Database::query($sql);
3571
        $row = Database::fetch_array($result);
3572
        $sql = "UPDATE $table_threads SET
3573
                    thread_last_post='".$row['iid']."',
3574
                    thread_replies=thread_replies-1
3575
                WHERE
3576
                    iid ='".$threadId."'";
3577
        Database::query($sql);
3578
    } else {
3579
        // Moving to the chosen thread.
3580
        $sql = 'SELECT thread_id FROM '.$table_posts."
3581
                WHERE iid = '".$values['post_id']."' ";
3582
        $result = Database::query($sql);
3583
        $row = Database::fetch_array($result);
3584
3585
        $original_thread_id = $row['thread_id'];
3586
        $sql = 'SELECT thread_last_post FROM '.$table_threads."
3587
                WHERE iid = '".$original_thread_id."' ";
3588
3589
        $result = Database::query($sql);
3590
        $row = Database::fetch_array($result);
3591
        $thread_is_last_post = $row['thread_last_post'];
3592
        // If is this thread, update the thread_last_post with the last one.
3593
3594
        if ($thread_is_last_post == $values['post_id']) {
3595
            $sql = 'SELECT iid as post_id FROM '.$table_posts."
3596
                    WHERE
3597
                        thread_id = '".$original_thread_id."' AND
3598
                        iid <> '".$values['post_id']."'
3599
                    ORDER BY post_date DESC LIMIT 1";
3600
            $result = Database::query($sql);
3601
3602
            $row = Database::fetch_array($result);
3603
            $thread_new_last_post = $row['post_id'];
3604
3605
            $sql = 'UPDATE '.$table_threads."
3606
                    SET thread_last_post = '".$thread_new_last_post."'
3607
                    WHERE iid = '".$original_thread_id."' ";
3608
            Database::query($sql);
3609
        }
3610
3611
        $sql = "UPDATE $table_threads SET thread_replies=thread_replies-1
3612
                WHERE iid ='".$original_thread_id."'";
3613
        Database::query($sql);
3614
3615
        // moving to the chosen thread
3616
        $sql = "UPDATE $table_posts SET thread_id='".(int) ($_POST['thread'])."', post_parent_id = NULL
3617
                WHERE iid ='".(int) ($values['post_id'])."'";
3618
        Database::query($sql);
3619
3620
        // resetting the parent_id of the thread to 0 for all those who had this moved post as parent
3621
        $sql = "UPDATE $table_posts SET post_parent_id = NULL
3622
                WHERE post_parent_id='".(int) ($values['post_id'])."'";
3623
        Database::query($sql);
3624
3625
        $sql = "UPDATE $table_threads SET thread_replies=thread_replies+1
3626
                WHERE iid ='".(int) ($_POST['thread'])."'";
3627
        Database::query($sql);
3628
    }
3629
3630
    return get_lang('Thread moved');
3631
}
3632
3633
/**
3634
 * @param array $values
3635
 *
3636
 * @return string HTML language variable
3637
 *
3638
 * @author Patrick Cool <[email protected]>, Ghent University
3639
 *
3640
 * @version february 2006, dokeos 1.8
3641
 */
3642
function store_move_thread($values)
3643
{
3644
    $table_threads = Database::get_course_table(TABLE_FORUM_THREAD);
3645
    $table_posts = Database::get_course_table(TABLE_FORUM_POST);
3646
3647
    $courseId = api_get_course_int_id();
3648
    $sessionId = api_get_session_id();
3649
3650
    $forumId = (int) ($_POST['forum']);
3651
    $threadId = (int) ($_POST['thread_id']);
3652
    //$forumInfo = get_forums($forumId);
3653
3654
    // Change the thread table: Setting the forum_id to the new forum.
3655
    $sql = "UPDATE $table_threads SET forum_id = $forumId
3656
            WHERE c_id = $courseId AND iid = $threadId";
3657
    Database::query($sql);
3658
3659
    // Changing all the posts of the thread: setting the forum_id to the new forum.
3660
    $sql = "UPDATE $table_posts SET forum_id = $forumId
3661
            WHERE c_id = $courseId AND thread_id= $threadId";
3662
    Database::query($sql);
3663
3664
    // Fix group id, if forum is moved to a different group
3665
    /*if (!empty($forumInfo['to_group_id'])) {
3666
        $groupId = $forumInfo['to_group_id'];
3667
        $item = api_get_item_property_info(
3668
            $courseId,
3669
            TABLE_FORUM_THREAD,
3670
            $threadId,
3671
            $sessionId,
3672
            $groupId
3673
        );
3674
        $table = Database::get_course_table(TABLE_ITEM_PROPERTY);
3675
        $sessionCondition = api_get_session_condition($sessionId);
3676
3677
        if (!empty($item)) {
3678
            if ($item['to_group_id'] != $groupId) {
3679
                $sql = "UPDATE $table
3680
                    SET to_group_id = $groupId
3681
                    WHERE
3682
                      tool = '".TABLE_FORUM_THREAD."' AND
3683
                      c_id = $courseId AND
3684
                      ref = ".$item['ref']."
3685
                      $sessionCondition
3686
                ";
3687
                Database::query($sql);
3688
            }
3689
        } else {
3690
            $sql = "UPDATE $table
3691
                    SET to_group_id = $groupId
3692
                    WHERE
3693
                      tool = '".TABLE_FORUM_THREAD."' AND
3694
                      c_id = $courseId AND
3695
                      ref = ".$threadId."
3696
                      $sessionCondition
3697
            ";
3698
            Database::query($sql);
3699
        }
3700
    }*/
3701
3702
    return get_lang('Thread moved');
3703
}
3704
3705
/**
3706
 * Prepares a string for displaying by highlighting the search results inside, if any.
3707
 *
3708
 * @param string $input the input string
3709
 *
3710
 * @return string the same string with highlighted hits inside
3711
 *
3712
 * @author Patrick Cool <[email protected]>, Ghent University, February 2006 - the initial version.
3713
 * @author Ivan Tcholakov, March 2011 - adaptation for Chamilo LMS.
3714
 */
3715
function prepare4display($input)
3716
{
3717
    static $highlightcolors = ['yellow', '#33CC33', '#3399CC', '#9999FF', '#33CC33'];
3718
    static $search;
3719
3720
    if (!isset($search)) {
3721
        if (isset($_POST['search_term'])) {
3722
            $search = $_POST['search_term']; // No html at all.
3723
        } elseif (isset($_GET['search'])) {
3724
            $search = $_GET['search'];
3725
        } else {
3726
            $search = '';
3727
        }
3728
    }
3729
3730
    if (!empty($search)) {
3731
        if (strstr($search, '+')) {
3732
            $search_terms = explode('+', $search);
3733
        } else {
3734
            $search_terms[] = trim($search);
3735
        }
3736
        $counter = 0;
3737
        foreach ($search_terms as $key => $search_term) {
3738
            $input = api_preg_replace(
3739
                '/'.preg_quote(trim($search_term), '/').'/i',
3740
                '<span style="background-color: '.$highlightcolors[$counter].'">$0</span>',
3741
                $input
3742
            );
3743
            $counter++;
3744
        }
3745
    }
3746
3747
    // TODO: Security should be implemented outside this function.
3748
    // Change this to COURSEMANAGERLOWSECURITY or COURSEMANAGER to lower filtering and allow more styles
3749
    // (see comments of Security::remove_XSS() method to learn about other levels).
3750
3751
    return Security::remove_XSS($input, STUDENT, true);
3752
}
3753
3754
/**
3755
 * Display the search form for the forum and display the search results.
3756
 *
3757
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
3758
 *
3759
 * @version march 2008, dokeos 1.8.5
3760
 */
3761
function forum_search()
3762
{
3763
    $form = new FormValidator(
3764
        'forumsearch',
3765
        'post',
3766
        'forumsearch.php?'.api_get_cidreq()
3767
    );
3768
3769
    // Setting the form elements.
3770
    $form->addElement('header', '', get_lang('Search in the Forum'));
3771
    $form->addElement('text', 'search_term', get_lang('Search term'), ['autofocus']);
3772
    $form->applyFilter('search_term', 'html_filter');
3773
    $form->addElement('static', 'search_information', '', get_lang('Search in the Forum information'));
3774
    $form->addButtonSearch(get_lang('Search'));
3775
3776
    // Setting the rules.
3777
    $form->addRule('search_term', get_lang('Required field'), 'required');
3778
    $form->addRule('search_term', get_lang('Too short'), 'minlength', 3);
3779
3780
    // Validation or display.
3781
    if ($form->validate()) {
3782
        $values = $form->exportValues();
3783
        $form->setDefaults($values);
3784
        $form->display();
3785
        // Display the search results.
3786
        displayForumSearchResults($values['search_term']);
3787
    } else {
3788
        $form->display();
3789
    }
3790
}
3791
3792
/**
3793
 * Displays the search results for forums, threads, and posts within a course.
3794
 */
3795
function displayForumSearchResults(string $searchTerm): void
3796
{
3797
    $forumRepo = Container::getForumRepository();
3798
    $threadRepo = Container::getForumThreadRepository();
3799
    $postRepo = Container::getForumPostRepository();
3800
3801
    $courseId = api_get_course_int_id();
3802
    $sessionId = api_get_session_id();
3803
    $course = api_get_course_entity($courseId);
3804
    $session = api_get_session_entity($sessionId);
3805
3806
    $searchTerms = explode(' ', $searchTerm);
3807
    $searchTerms = array_filter($searchTerms);
3808
3809
    $forumQb = $forumRepo->getResourcesByCourse($course, $session);
3810
    foreach ($searchTerms as $term) {
3811
        $forumQb->andWhere('resource.title LIKE :term OR resource.forumComment LIKE :term')
3812
            ->setParameter('term', '%' . $term . '%');
3813
    }
3814
    $forums = $forumQb->getQuery()->getResult();
3815
3816
    $threadQb = $threadRepo->getResourcesByCourse($course, $session);
3817
    foreach ($searchTerms as $term) {
3818
        $threadQb->andWhere('resource.title LIKE :term')
3819
            ->setParameter('term', '%' . $term . '%');
3820
    }
3821
    $threads = $threadQb->getQuery()->getResult();
3822
3823
    $postQb = $postRepo->getResourcesByCourse($course, $session);
3824
    foreach ($searchTerms as $term) {
3825
        $postQb->andWhere('resource.title LIKE :term OR resource.postText LIKE :term')
3826
            ->setParameter('term', '%' . $term . '%');
3827
    }
3828
    $posts = $postQb->getQuery()->getResult();
3829
3830
    $search_results = [];
3831
    foreach ($forums as $forum) {
3832
        if ($forum->isVisible($course) && $forum->getForumCategory()->isVisible($course)) {
3833
            $search_results[] = '<li class="mb-2"><a href="viewforum.php?'.api_get_cidreq().'&forum='.$forum->getIid().'" class="text-blue-500 hover:text-blue-600">'.htmlspecialchars($forum->getTitle()).'</a></li>';
3834
        }
3835
    }
3836
    foreach ($threads as $thread) {
3837
        if ($thread->isVisible($course)) {
3838
            $search_results[] = '<li class="mb-2"><a href="viewthread.php?'.api_get_cidreq().'&thread='.$thread->getIid().'&forum='.$thread->getForum()->getIid().'" class="text-blue-500 hover:text-blue-600">'.htmlspecialchars($thread->getTitle()).'</a></li>';
3839
        }
3840
    }
3841
    foreach ($posts as $post) {
3842
        if ($post->isVisible($course)) {
3843
            $search_results[] = '<li class="mb-2"><a href="viewthread.php?'.api_get_cidreq().'&thread='.$post->getThread()->getIid().'&forum='.$post->getForum()->getIid().'" class="text-blue-500 hover:text-blue-600">'.htmlspecialchars($post->getTitle()).'</a></li>';
3844
        }
3845
    }
3846
3847
    echo '<div class="text-lg font-semibold mb-4">'.count($search_results).' '.get_lang('Search results').'</div>';
3848
    echo '<ol class="list-decimal pl-5">'.implode($search_results).'</ol>';
3849
}
3850
3851
/**
3852
 * Return the link to the forum search page.
3853
 *
3854
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
3855
 *
3856
 * @version April 2008, dokeos 1.8.5
3857
 */
3858
function search_link()
3859
{
3860
    $return = '';
3861
    $origin = api_get_origin();
3862
    if ('learnpath' != $origin) {
3863
        $return = '<a href="forumsearch.php?'.api_get_cidreq().'&action=search"> ';
3864
        $return .= Display::getMdiIcon('magnify-plus-outline	', 'ch-tool-icon', null, ICON_SIZE_MEDIUM, get_lang('Search')).'</a>';
3865
3866
        if (!empty($_GET['search'])) {
3867
            $return .= ': '.Security::remove_XSS($_GET['search']).' ';
3868
            $url = api_get_self().'?';
3869
            $url_parameter = [];
3870
            foreach ($_GET as $key => $value) {
3871
                if ('search' != $key) {
3872
                    $url_parameter[] = Security::remove_XSS($key).'='.Security::remove_XSS($value);
3873
                }
3874
            }
3875
            $url .= implode('&', $url_parameter);
3876
            $return .= '<a href="'.$url.'">'.Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', '', ICON_SIZE_MEDIUM,  get_lang('Clean search results')).'</a>';
3877
        }
3878
    }
3879
3880
    return $return;
3881
}
3882
3883
/**
3884
 * This function adds an attachment file into a forum.
3885
 *
3886
 * @param string     $file_comment a comment about file
3887
 * @param CForumPost $post         from forum_post table
3888
 *
3889
 * @return false|null
3890
 */
3891
function add_forum_attachment_file($file_comment, CForumPost $post)
3892
{
3893
    $request = Container::getRequest();
3894
    if (false === $request->files->has('user_upload')) {
3895
        return false;
3896
    }
3897
3898
    $file = $request->files->get('user_upload');
3899
    if (empty($file)) {
3900
        return false;
3901
    }
3902
3903
    $files = [];
3904
    if ($file instanceof UploadedFile) {
3905
        $files[] = $file;
3906
    } else {
3907
        $files = $file;
3908
    }
3909
3910
    /** @var UploadedFile $attachment */
3911
    foreach ($files as $file) {
3912
        $valid = process_uploaded_file($file);
3913
3914
        if (!$valid) {
3915
            continue;
3916
        }
3917
3918
        // Try to add an extension to the file if it hasn't one.
3919
        $new_file_name = add_ext_on_mime(
3920
            stripslashes($file->getPathname()),
3921
            $file->getType()
3922
        );
3923
3924
        // User's file name
3925
        $file_name = $file->getClientOriginalName();
3926
3927
        if (!filter_extension($new_file_name)) {
3928
            Display::addFlash(
3929
                Display::return_message(
3930
                    get_lang('File upload failed: this file extension or file type is prohibited'),
3931
                    'error'
3932
                )
3933
            );
3934
3935
            return;
3936
        }
3937
3938
        $safe_file_comment = Database::escape_string($file_comment);
3939
3940
        $user = api_get_user_entity(api_get_user_id());
3941
        $course = api_get_course_entity(api_get_course_int_id());
3942
        $session = api_get_session_entity(api_get_session_id());
3943
3944
        $attachment = (new CForumAttachment())
3945
            ->setCId(api_get_course_int_id())
3946
            ->setComment($safe_file_comment)
3947
            ->setFilename($file_name)
3948
            ->setPath($file_name)
3949
            ->setPost($post)
3950
            ->setSize($file->getSize())
3951
            ->setParent($post)
3952
            ->addCourseLink(
3953
                $course,
3954
                $session
3955
            );
3956
3957
        $repo = Container::getForumAttachmentRepository();
3958
        $repo->create($attachment);
3959
        $repo->addFile($attachment, $file);
3960
        $repo->update($attachment);
3961
    }
3962
}
3963
3964
/**
3965
 * This function edits an attachment file into a forum.
3966
 *
3967
 * @param string $file_comment a comment about file
3968
 * @param int    $post_id
3969
 * @param int    $id_attach    attachment file Id
3970
 */
3971
function edit_forum_attachment_file($file_comment, $post_id, $id_attach)
3972
{
3973
    throw new Exception('edit_forum_attachment_file');
3974
    /*
3975
    $_course = api_get_course_info();
3976
    $table_forum_attachment = Database::get_course_table(TABLE_FORUM_ATTACHMENT);
3977
    $course_id = api_get_course_int_id();
3978
3979
    $filesData = [];
3980
3981
    if (!is_array($_FILES['user_upload']['name'])) {
3982
        $filesData[] = $_FILES['user_upload'];
3983
    } else {
3984
        $fileCount = count($_FILES['user_upload']['name']);
3985
        $fileKeys = array_keys($_FILES['user_upload']);
3986
3987
        for ($i = 0; $i < $fileCount; $i++) {
3988
            foreach ($fileKeys as $key) {
3989
                $filesData[$i][$key] = $_FILES['user_upload'][$key][$i];
3990
            }
3991
        }
3992
    }
3993
3994
    foreach ($filesData as $attachment) {
3995
        if (empty($attachment['name'])) {
3996
            continue;
3997
        }
3998
3999
        $upload_ok = process_uploaded_file($attachment);
4000
4001
        if (!$upload_ok) {
4002
            continue;
4003
        }
4004
4005
        $course_dir = $_course['path'].'/upload/forum';
4006
        $sys_course_path = api_get_path(SYS_COURSE_PATH);
4007
        $updir = $sys_course_path.$course_dir;
4008
4009
        // Try to add an extension to the file if it hasn't one.
4010
        $new_file_name = add_ext_on_mime(stripslashes($attachment['name']), $attachment['type']);
4011
        // User's file name
4012
        $file_name = $attachment['name'];
4013
4014
        if (!filter_extension($new_file_name)) {
4015
            Display::addFlash(
4016
                Display::return_message(
4017
                    get_lang('File upload failed: this file extension or file type is prohibited'),
4018
                    'error'
4019
                )
4020
            );
4021
        } else {
4022
            $new_file_name = uniqid('');
4023
            $new_path = $updir.'/'.$new_file_name;
4024
            $result = @move_uploaded_file($attachment['tmp_name'], $new_path);
4025
            $safe_file_comment = Database::escape_string($file_comment);
4026
            $safe_file_name = Database::escape_string($file_name);
4027
            $safe_new_file_name = Database::escape_string($new_file_name);
4028
            $safe_post_id = (int) $post_id;
4029
            $safe_id_attach = (int) $id_attach;
4030
            // Storing the attachments if any.
4031
            if ($result) {
4032
                $sql = "UPDATE $table_forum_attachment
4033
                        SET
4034
                            filename = '$safe_file_name',
4035
                            comment = '$safe_file_comment',
4036
                            path = '$safe_new_file_name',
4037
                            post_id = '$safe_post_id',
4038
                            size ='".$attachment['size']."'
4039
                        WHERE c_id = $course_id AND id = '$safe_id_attach'";
4040
                Database::query($sql);
4041
                api_item_property_update(
4042
                    $_course,
4043
                    TOOL_FORUM_ATTACH,
4044
                    $safe_id_attach,
4045
                    'ForumAttachmentUpdated',
4046
                    api_get_user_id()
4047
                );
4048
            }
4049
        }
4050
    }*/
4051
}
4052
4053
/**
4054
 * Show a list with all the attachments according to the post's id.
4055
 *
4056
 * @param int $postId
4057
 *
4058
 * @return array with the post info
4059
 *
4060
 * @author Julio Montoya
4061
 *
4062
 * @version avril 2008, dokeos 1.8.5
4063
 */
4064
function get_attachment($postId)
4065
{
4066
    $table = Database::get_course_table(TABLE_FORUM_ATTACHMENT);
4067
    $course_id = api_get_course_int_id();
4068
    $row = [];
4069
    $postId = (int) $postId;
4070
4071
    if (empty($postId)) {
4072
        return [];
4073
    }
4074
4075
    $sql = "SELECT iid, path, filename, comment
4076
            FROM $table
4077
            WHERE c_id = $course_id AND post_id = $postId";
4078
    $result = Database::query($sql);
4079
    if (0 != Database::num_rows($result)) {
4080
        $row = Database::fetch_array($result);
4081
    }
4082
4083
    return $row;
4084
}
4085
4086
/**
4087
 * Delete the all the attachments from the DB and the file according to the post's id or attach id(optional).
4088
 *
4089
 * @param int $postId
4090
 * @param int $attachmentId
4091
 *
4092
 * @return bool
4093
 */
4094
function delete_attachment($postId, $attachmentId)
4095
{
4096
    $repo = Container::getForumPostRepository();
4097
    $em = Database::getManager();
4098
    /** @var CForumPost $post */
4099
    $post = $repo->find($postId);
4100
    if ($post) {
4101
        $repoAttachment = Container::getForumAttachmentRepository();
4102
        $attachment = $repoAttachment->find($attachmentId);
4103
        if ($attachment) {
4104
            $post->removeAttachment($attachment);
4105
            $em->remove($attachment);
4106
        }
4107
        $repo->update($post);
4108
4109
        Display::addFlash(Display::return_message(get_lang('The attached file has been deleted'), 'confirmation'));
4110
    }
4111
4112
    return true;
4113
}
4114
4115
/**
4116
 * This function gets all the forum information of the all the forum of the group.
4117
 *
4118
 * @param array $groupInfo the id of the group we need the fora of (see forum.forum_of_group)
4119
 *
4120
 * @return CForum[]
4121
 *
4122
 * @todo this is basically the same code as the get_forums function. Consider merging the two.
4123
 */
4124
function get_forums_of_group(CGroup $group)
4125
{
4126
    $course = api_get_course_entity();
4127
    $session = api_get_session_entity();
4128
    $repo = Container::getForumRepository();
4129
    $qb = $repo->getResourcesByCourse($course, $session, $group);
4130
4131
    return $qb->getQuery()->getResult();
4132
}
4133
4134
/**
4135
 * This function stores which users have to be notified of which forums or threads.
4136
 *
4137
 * @param string $content    does the user want to be notified about a forum or about a thread
4138
 * @param int    $id         the id of the forum or thread
4139
 * @param bool   $addOnly
4140
 * @param array  $userInfo
4141
 * @param array  $courseInfo
4142
 *
4143
 * @return string language variable
4144
 *
4145
 * @author  Patrick Cool <[email protected]>, Ghent University, Belgium
4146
 * @author  Julio Montoya
4147
 *
4148
 * @since   May 2008 v1.8.5
4149
 */
4150
function set_notification($content, $id, $addOnly = false, $userInfo = [], $courseInfo = [])
4151
{
4152
    $userInfo = empty($userInfo) ? api_get_user_info() : $userInfo;
4153
    $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo;
4154
    $id = (int) $id;
4155
4156
    if (empty($userInfo) || empty($courseInfo) || empty($id) || empty($content)) {
4157
        return false;
4158
    }
4159
4160
    // Database table definition
4161
    $table_notification = Database::get_course_table(TABLE_FORUM_NOTIFICATION);
4162
    $course_id = $courseInfo['real_id'];
4163
4164
    // Which database field do we have to store the id in?
4165
    $field = 'thread_id';
4166
    if ('forum' === $content) {
4167
        $field = 'forum_id';
4168
    }
4169
4170
    $userId = $userInfo['user_id'];
4171
4172
    // First we check if the notification is already set for this.
4173
    $sql = "SELECT * FROM $table_notification
4174
            WHERE
4175
                c_id = $course_id AND
4176
                $field = $id AND
4177
                user_id = $userId ";
4178
    $result = Database::query($sql);
4179
    $total = Database::num_rows($result);
4180
4181
    // If the user did not indicate that (s)he wanted to be notified already
4182
    // then we store the notification request (to prevent double notification requests).
4183
    if ($total <= 0) {
4184
        $notification = new CForumNotification();
4185
        $notification
4186
            ->setCId($course_id)
4187
            ->setUserId($userId)
4188
        ;
4189
4190
        if ('forum' === $content) {
4191
            $notification->setForumId($id);
4192
        } else {
4193
            $notification->setThreadId($id);
4194
        }
4195
4196
        $em = Database::getManager();
4197
        $em->persist($notification);
4198
        $em->flush();
4199
4200
        Session::erase('forum_notification');
4201
        getNotificationsPerUser(0, true);
4202
4203
        return get_lang('You will be notified of new posts by e-mail.');
4204
    } else {
4205
        if (!$addOnly) {
4206
            $sql = "DELETE FROM $table_notification
4207
                    WHERE
4208
                        c_id = $course_id AND
4209
                        $field = $id AND
4210
                        user_id = $userId ";
4211
            Database::query($sql);
4212
            Session::erase('forum_notification');
4213
            getNotificationsPerUser(0, true);
4214
4215
            return get_lang('You will no longer be notified of new posts by email');
4216
        }
4217
    }
4218
}
4219
4220
/**
4221
 * This function retrieves all the email adresses of the users who wanted to be notified
4222
 * about a new post in a certain forum or thread.
4223
 *
4224
 * @param string $content does the user want to be notified about a forum or about a thread
4225
 * @param int $id      the id of the forum or thread
4226
 *
4227
 * @return array returns
4228
 *
4229
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
4230
 * @author Julio Montoya
4231
 *
4232
 * @version May 2008, dokeos 1.8.5
4233
 *
4234
 * @since May 2008, dokeos 1.8.5
4235
 */
4236
function get_notifications(string $content, int $id): array
4237
{
4238
    // Database table definition
4239
    $table_users = Database::get_main_table(TABLE_MAIN_USER);
4240
    $table_notification = Database::get_course_table(TABLE_FORUM_NOTIFICATION);
4241
    $course_id = api_get_course_int_id();
4242
4243
    // Which database field contains the notification?
4244
    $field = 'thread_id';
4245
    if ('forum' === $content) {
4246
        $field = 'forum_id';
4247
    }
4248
4249
    $sql = "SELECT user.id as user_id, user.firstname, user.lastname, user.email, user.id user
4250
            FROM $table_users user, $table_notification notification
4251
            WHERE
4252
                notification.c_id = $course_id AND user.active = 1 AND
4253
                user.id = notification.user_id AND
4254
                notification.$field = $id ";
4255
4256
    $result = Database::query($sql);
4257
    $return = [];
4258
4259
    while ($row = Database::fetch_array($result)) {
4260
        $return['user'.$row['user_id']] = ['email' => $row['email'], 'user_id' => $row['user_id']];
4261
    }
4262
4263
    return $return;
4264
}
4265
4266
/**
4267
 * Get all the users who need to receive a notification of a new post (those subscribed to
4268
 * the forum or the thread).
4269
 *
4270
 * @param int $post_id the id of the post
4271
 *
4272
 * @return false|null
4273
 *
4274
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
4275
 *
4276
 * @version May 2008, dokeos 1.8.5
4277
 *
4278
 * @since May 2008, dokeos 1.8.5
4279
 */
4280
function send_notifications(CForum $forum, CForumThread $thread, $post_id = 0)
4281
{
4282
    if (!$forum) {
4283
        return false;
4284
    }
4285
4286
    // Users who subscribed to the forum
4287
    $users_to_be_notified_by_forum = get_notifications('forum', $forum->getIid());
4288
4289
    // User who subscribed to the thread
4290
    $users_to_be_notified_by_thread = [];
4291
    if (!$thread) {
4292
        $users_to_be_notified_by_thread = get_notifications('thread', $thread->getIid());
4293
    }
4294
4295
    $postInfo = null;
4296
    if (!empty($post_id)) {
4297
        $postInfo = Container::getForumPostRepository()->find($post_id);
4298
    }
4299
4300
    // Merging the two
4301
    $users_to_be_notified = array_merge($users_to_be_notified_by_forum, $users_to_be_notified_by_thread);
4302
4303
    $subscribe = (int) api_get_course_setting('subscribe_users_to_forum_notifications');
4304
    if (1 === $subscribe) {
4305
        $sessionId = api_get_session_id();
4306
        if (!empty($sessionId)) {
4307
            $users_to_be_notified = SessionManager::getAllUserIdsInSession($sessionId);
4308
        } else {
4309
            $users_to_be_notified = CourseManager::get_user_list_from_course_code(api_get_course_id());
4310
        }
4311
    }
4312
4313
    if (is_array($users_to_be_notified)) {
4314
        foreach ($users_to_be_notified as $value) {
4315
            $userInfo = api_get_user_info($value['user_id']);
4316
            send_mail($userInfo, $forum, $thread, $postInfo);
4317
        }
4318
    }
4319
}
4320
4321
/**
4322
 * Get all the notification subscriptions of the user
4323
 * = which forums and which threads does the user wants to be informed of when a new
4324
 * post is added to this thread.
4325
 *
4326
 * @param int  $user_id the user_id of a user (default = 0 => the current user)
4327
 * @param bool $force   force get the notification subscriptions (even if the information is already in the session
4328
 *
4329
 * @return array
4330
 *
4331
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
4332
 *
4333
 * @version May 2008, dokeos 1.8.5
4334
 *
4335
 * @since May 2008, dokeos 1.8.5
4336
 */
4337
function getNotificationsPerUser($user_id = 0, $force = false, $course_id = 0)
4338
{
4339
    // Database table definition
4340
    $table_notification = Database::get_course_table(TABLE_FORUM_NOTIFICATION);
4341
    $course_id = empty($course_id) ? api_get_course_int_id() : (int) $course_id;
4342
    if (empty($course_id) || -1 == $course_id) {
4343
        return null;
4344
    }
4345
4346
    $user_id = empty($user_id) ? api_get_user_id() : (int) $user_id;
4347
4348
    if (!isset($_SESSION['forum_notification']) ||
4349
        $_SESSION['forum_notification']['course'] != $course_id ||
4350
        true == $force
4351
    ) {
4352
        $_SESSION['forum_notification']['course'] = $course_id;
4353
        $sql = "SELECT * FROM $table_notification
4354
                WHERE c_id = $course_id AND user_id='".$user_id."'";
4355
4356
        $result = Database::query($sql);
4357
        while ($row = Database::fetch_array($result)) {
4358
            if (null !== $row['forum_id']) {
4359
                $_SESSION['forum_notification']['forum'][] = $row['forum_id'];
4360
            }
4361
            if (null !== $row['thread_id']) {
4362
                $_SESSION['forum_notification']['thread'][] = $row['thread_id'];
4363
            }
4364
        }
4365
    }
4366
}
4367
4368
/**
4369
 * This function counts the number of post inside a thread.
4370
 *
4371
 * @param int $thread_id
4372
 *
4373
 * @return int the number of post inside a thread
4374
 *
4375
 * @author Jhon Hinojosa <[email protected]>,
4376
 *
4377
 * @version octubre 2008, dokeos 1.8
4378
 */
4379
function count_number_of_post_in_thread($thread_id)
4380
{
4381
    $table_posts = Database::get_course_table(TABLE_FORUM_POST);
4382
    $course_id = api_get_course_int_id();
4383
    if (empty($course_id)) {
4384
        return 0;
4385
    }
4386
    $sql = "SELECT count(*) count FROM $table_posts
4387
            WHERE
4388
                c_id = $course_id AND
4389
                thread_id='".(int) $thread_id."' ";
4390
    $result = Database::query($sql);
4391
4392
    $count = 0;
4393
    if (Database::num_rows($result) > 0) {
4394
        $row = Database::fetch_array($result);
4395
        $count = $row['count'];
4396
    }
4397
4398
    return $count;
4399
}
4400
4401
/**
4402
 * This function counts the number of post inside a thread user.
4403
 *
4404
 * @param int $thread_id
4405
 * @param int $user_id
4406
 *
4407
 * @return int the number of post inside a thread user
4408
 */
4409
function count_number_of_post_for_user_thread($thread_id, $user_id)
4410
{
4411
    $table_posts = Database::get_course_table(TABLE_FORUM_POST);
4412
    $sql = "SELECT count(iid) as count
4413
            FROM $table_posts
4414
            WHERE
4415
                  thread_id=".(int) $thread_id.' AND
4416
                  poster_id = '.(int) $user_id.' AND
4417
                  visible = 1 ';
4418
    $result = Database::query($sql);
4419
    $count = 0;
4420
    if (Database::num_rows($result) > 0) {
4421
        $count = Database::fetch_array($result);
4422
        $count = $count['count'];
4423
    }
4424
4425
    return $count;
4426
}
4427
4428
/**
4429
 * This function retrieves information of statistical.
4430
 *
4431
 * @param int $thread_id
4432
 * @param int $user_id
4433
 * @param int $course_id
4434
 *
4435
 * @return array the information of statistical
4436
 *
4437
 * @author Jhon Hinojosa <[email protected]>,
4438
 *
4439
 * @version oct 2008, dokeos 1.8
4440
 */
4441
function get_statistical_information($thread_id, $user_id, $course_id)
4442
{
4443
    $result = [];
4444
    $courseInfo = api_get_course_info_by_id($course_id);
4445
    $result['user_course'] = CourseManager::get_users_count_in_course($courseInfo['code']);
4446
    $result['post'] = count_number_of_post_in_thread($thread_id);
4447
    $result['user_post'] = count_number_of_post_for_user_thread($thread_id, $user_id);
4448
4449
    return $result;
4450
}
4451
4452
/**
4453
 * This function return the posts inside a thread from a given user.
4454
 *
4455
 * @param int $thread_id
4456
 * @param int $user_id
4457
 *
4458
 * @return array posts inside a thread
4459
 *
4460
 * @author  Jhon Hinojosa <[email protected]>,
4461
 *
4462
 * @version oct 2008, dokeos 1.8
4463
 */
4464
function get_thread_user_post(Course $course, $thread_id, $user_id)
4465
{
4466
    $table_posts = Database::get_course_table(TABLE_FORUM_POST);
4467
    $table_users = Database::get_main_table(TABLE_MAIN_USER);
4468
    $thread_id = (int) $thread_id;
4469
    $user_id = (int) $user_id;
4470
    $course_id = $course->getId();
4471
4472
    if (empty($course_id)) {
4473
        $course_id = api_get_course_int_id();
4474
    }
4475
    $sql = "SELECT *, user.id as user_id FROM $table_posts posts
4476
            LEFT JOIN $table_users user
4477
            ON posts.poster_id = user.id
4478
            WHERE
4479
                posts.c_id = $course_id AND
4480
                posts.thread_id='$thread_id' AND
4481
                posts.poster_id='$user_id'
4482
            ORDER BY posts.iid ASC";
4483
4484
    $result = Database::query($sql);
4485
    $post_list = [];
4486
    while ($row = Database::fetch_array($result)) {
4487
        $row['status'] = '1';
4488
        $post_list[] = $row;
4489
        $sql = "SELECT * FROM $table_posts posts
4490
                LEFT JOIN $table_users users
4491
                ON (posts.poster_id=users.id)
4492
                WHERE
4493
                    posts.c_id = $course_id AND
4494
                    posts.thread_id='$thread_id'
4495
                    AND posts.post_parent_id='".$row['iid']."'
4496
                ORDER BY posts.iid ASC";
4497
        $result2 = Database::query($sql);
4498
        while ($row2 = Database::fetch_array($result2)) {
4499
            $row2['status'] = '0';
4500
            $post_list[] = $row2;
4501
        }
4502
    }
4503
4504
    return $post_list;
4505
}
4506
4507
/**
4508
 * This function get the name of an thread by id.
4509
 *
4510
 * @param int $threadId
4511
 *
4512
 * @return string
4513
 *
4514
 * @author Christian Fasanando
4515
 * @author Julio Montoya <[email protected]> Adding security
4516
 */
4517
function get_name_thread_by_id(int $threadId): string
4518
{
4519
    $tForumThread = Database::get_course_table(TABLE_FORUM_THREAD);
4520
    $course_id = api_get_course_int_id();
4521
    $sql = "SELECT title
4522
            FROM $tForumThread
4523
            WHERE iid = $threadId";
4524
    $result = Database::query($sql);
4525
    $row = Database::fetch_array($result);
4526
4527
    return $row[0];
4528
}
4529
4530
/**
4531
 * This function gets all the post written by an user.
4532
 *
4533
 * @param int $user_id
4534
 * @param int $courseId
4535
 *
4536
 * @return string
4537
 */
4538
function get_all_post_from_user(int $user_id, int $courseId): string
4539
{
4540
    $j = 0;
4541
    $forums = get_forums($courseId);
4542
    krsort($forums);
4543
    $forum_results = '';
4544
4545
    foreach ($forums as $forum) {
4546
        $forumId = $forum->getIid();
4547
4548
        /*if (0 == $forum['visibility']) {
4549
            continue;
4550
        }*/
4551
        if ($j <= 4) {
4552
            $threads = get_threads($forumId);
4553
4554
            if ($threads) {
4555
                $i = 0;
4556
                $hand_forums = '';
4557
                $post_counter = 0;
4558
                foreach ($threads as $thread) {
4559
                    /*if (0 == $thread['visibility']) {
4560
                        continue;
4561
                    }*/
4562
                    if ($i <= 4) {
4563
                        $post_list = get_thread_user_post_limit(
4564
                            $courseId,
4565
                            $thread->getIid(),
4566
                            $user_id,
4567
                            1
4568
                        );
4569
                        $post_counter = count($post_list);
4570
                        if (is_array($post_list) && count($post_list) > 0) {
4571
                            $hand_forums .= '<div id="social-thread">';
4572
                            $hand_forums .= Display::getMdiIcon('format-quote-open', 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Thread'));
4573
                            $hand_forums .= '&nbsp;'.Security::remove_XSS($thread->getTitle(), STUDENT);
4574
                            $hand_forums .= '</div>';
4575
4576
                            foreach ($post_list as $posts) {
4577
                                $hand_forums .= '<div id="social-post">';
4578
                                $hand_forums .= '<strong>'.Security::remove_XSS($posts['post_title'], STUDENT).'</strong>';
4579
                                $hand_forums .= '<br / >';
4580
                                $hand_forums .= Security::remove_XSS($posts['post_text'], STUDENT);
4581
                                $hand_forums .= '</div>';
4582
                                $hand_forums .= '<br / >';
4583
                            }
4584
                        }
4585
                    }
4586
                    $i++;
4587
                }
4588
                $forum_results .= '<div id="social-forum">';
4589
                $forum_results .= '<div class="clear"></div><br />';
4590
                $forum_results .= '<div id="social-forum-title">'.
4591
                    Display::getMdiIcon('comment-quote', 'ch-tool-icon', '', ICON_SIZE_SMALL, get_lang('Forum')).'&nbsp;'.Security::remove_XSS($forum->getTitle(), STUDENT).
4592
                    '<div style="float:right;margin-top:-35px">
4593
                        <a href="../forum/viewforum.php?'.api_get_cidreq_params($courseId).'&forum='.$forum->getIid().' " >'.
4594
                    get_lang('See forum').'
4595
                        </a>
4596
                     </div></div>';
4597
                $forum_results .= '<br / >';
4598
                if ($post_counter > 0) {
4599
                    $forum_results .= $hand_forums;
4600
                }
4601
                $forum_results .= '</div>';
4602
            }
4603
            $j++;
4604
        }
4605
    }
4606
4607
    return $forum_results;
4608
}
4609
4610
/**
4611
 * @param int $thread_id
4612
 * @param int $user_id
4613
 * @param int $limit
4614
 *
4615
 * @return array
4616
 */
4617
function get_thread_user_post_limit($courseId, $thread_id, $user_id, $limit = 10)
4618
{
4619
    $table_posts = Database::get_course_table(TABLE_FORUM_POST);
4620
    $table_users = Database::get_main_table(TABLE_MAIN_USER);
4621
4622
    $courseId = (int) $courseId;
4623
    $limit = (int) $limit;
4624
4625
    $sql = "SELECT * FROM $table_posts posts
4626
            LEFT JOIN $table_users users
4627
                ON posts.poster_id=users.id
4628
            WHERE
4629
                posts.c_id = $courseId AND
4630
                posts.thread_id='".Database::escape_string($thread_id)."' AND
4631
                posts.poster_id='".Database::escape_string($user_id)."'
4632
            ORDER BY posts.post_id DESC LIMIT $limit ";
4633
    $result = Database::query($sql);
4634
    $post_list = [];
4635
    while ($row = Database::fetch_array($result)) {
4636
        $row['status'] = '1';
4637
        $post_list[] = $row;
4638
    }
4639
4640
    return $post_list;
4641
}
4642
4643
/**
4644
 * @param string $userId
4645
 * @param array  $courseInfo
4646
 * @param int    $sessionId
4647
 *
4648
 * @return array
4649
 */
4650
function getForumCreatedByUser($userId, $courseInfo, $sessionId)
4651
{
4652
    if (empty($userId) || empty($courseInfo)) {
4653
        return [];
4654
    }
4655
4656
    $courseId = $courseInfo['real_id'];
4657
4658
    $repo = Container::getForumRepository();
4659
4660
    $courseEntity = api_get_course_entity($courseId);
4661
    $sessionEntity = api_get_session_entity($sessionId);
4662
4663
    $qb = $repo->getResourcesByCourse($courseEntity, $sessionEntity);
4664
4665
    $qb->andWhere('node.creator = :creator');
4666
    $qb->setParameter('creator', $userId);
4667
    $items = $qb->getQuery()->getResult();
4668
4669
    $forumList = [];
4670
    if (!empty($items)) {
4671
        /** @var CForum $forum */
4672
        foreach ($items as $forum) {
4673
            $forumList[] = [
4674
                $forum->getTitle(),
4675
                api_get_local_time($forum->getResourceNode()->getCreatedAt()),
4676
                api_get_local_time($forum->getResourceNode()->getUpdatedAt()),
4677
            ];
4678
        }
4679
    }
4680
4681
    return $forumList;
4682
}
4683
4684
/**
4685
 * This function builds an array of all the posts in a given thread
4686
 * where the key of the array is the post_id
4687
 * It also adds an element children to the array which itself is an array
4688
 * that contains all the id's of the first-level children.
4689
 *
4690
 * @return array containing all the information on the posts of a thread
4691
 *
4692
 * @author Patrick Cool <[email protected]>, Ghent University
4693
 */
4694
function calculate_children($rows)
4695
{
4696
    $sorted_rows = [0 => []];
4697
    if (!empty($rows)) {
4698
        foreach ($rows as $row) {
4699
            $rows_with_children[$row['post_id']] = $row;
4700
            $rows_with_children[$row['post_parent_id']]['children'][] = $row['post_id'];
4701
        }
4702
4703
        $rows = $rows_with_children;
4704
        forumRecursiveSort($rows, $sorted_rows);
4705
        unset($sorted_rows[0]);
4706
    }
4707
4708
    return $sorted_rows;
4709
}
4710
4711
/**
4712
 * @param $rows
4713
 * @param $threads
4714
 * @param int $seed
4715
 * @param int $indent
4716
 */
4717
function forumRecursiveSort($rows, &$threads, $seed = 0, $indent = 0)
4718
{
4719
    if ($seed > 0) {
4720
        $threads[$rows[$seed]['post_id']] = $rows[$seed];
4721
        $threads[$rows[$seed]['post_id']]['indent_cnt'] = $indent;
4722
        $indent++;
4723
    }
4724
4725
    if (isset($rows[$seed]['children'])) {
4726
        foreach ($rows[$seed]['children'] as $child) {
4727
            forumRecursiveSort($rows, $threads, $child, $indent);
4728
        }
4729
    }
4730
}
4731
4732
/**
4733
 * Update forum attachment data, used to update comment and post ID.
4734
 *
4735
 * @param array $array    (field => value) to update forum attachment row
4736
 * @param int   $id       ID to find row to update
4737
 * @param int   $courseId course ID to find row to update
4738
 *
4739
 * @return int number of affected rows
4740
 */
4741
function editAttachedFile($array, $id, $courseId = null)
4742
{
4743
    // Init variables
4744
    $setString = '';
4745
    $id = (int) $id;
4746
    $courseId = (int) $courseId;
4747
    if (empty($courseId)) {
4748
        // $courseId can be null, use api method
4749
        $courseId = api_get_course_int_id();
4750
    }
4751
    /*
4752
     * Check if Attachment ID and Course ID are greater than zero
4753
     * and array of field values is not empty
4754
     */
4755
    if ($id > 0 && $courseId > 0 && !empty($array) && is_array($array)) {
4756
        foreach ($array as $key => &$item) {
4757
            $item = Database::escape_string($item);
4758
            $setString .= $key.' = "'.$item.'", ';
4759
        }
4760
        // Delete last comma
4761
        $setString = substr($setString, 0, strlen($setString) - 2);
4762
        $forumAttachmentTable = Database::get_course_table(TABLE_FORUM_ATTACHMENT);
4763
        $sql = "UPDATE $forumAttachmentTable
4764
                SET $setString WHERE c_id = $courseId AND iid = $id";
4765
        $result = Database::query($sql);
4766
        if (false !== $result) {
4767
            $affectedRows = Database::affected_rows($result);
4768
            if ($affectedRows > 0) {
4769
                /*
4770
                 * If exist in $_SESSION variable, then delete them from it
4771
                 * because they would be deprecated
4772
                 */
4773
                if (!empty($_SESSION['forum']['upload_file'][$courseId][$id])) {
4774
                    unset($_SESSION['forum']['upload_file'][$courseId][$id]);
4775
                }
4776
            }
4777
4778
            return $affectedRows;
4779
        }
4780
    }
4781
4782
    return 0;
4783
}
4784
4785
/**
4786
 * Return a table where the attachments will be set.
4787
 *
4788
 * @param int $postId Forum Post ID
4789
 *
4790
 * @return string The Forum Attachments Ajax Table
4791
 */
4792
function getAttachmentsAjaxTable($postId = 0)
4793
{
4794
    $postId = (int) $postId;
4795
    $courseId = api_get_course_int_id();
4796
    $attachIds = getAttachmentIdsByPostId($postId, $courseId);
4797
    $fileDataContent = '';
4798
    // Update comment to show if form did not pass validation
4799
    if (!empty($_REQUEST['file_ids']) && is_array($_REQUEST['file_ids'])) {
4800
        // 'file_ids is the name from forum attachment ajax form
4801
        foreach ($_REQUEST['file_ids'] as $key => $attachId) {
4802
            if (!empty($_SESSION['forum']['upload_file'][$courseId][$attachId]) &&
4803
                is_array($_SESSION['forum']['upload_file'][$courseId][$attachId])
4804
            ) {
4805
                // If exist forum attachment then update into $_SESSION data
4806
                $_SESSION['forum']['upload_file'][$courseId][$attachId]['comment'] = $_POST['file_comments'][$key];
4807
            }
4808
        }
4809
    }
4810
4811
    // Get data to fill into attachment files table
4812
    if (!empty($_SESSION['forum']['upload_file'][$courseId]) &&
4813
        is_array($_SESSION['forum']['upload_file'][$courseId])
4814
    ) {
4815
        $uploadedFiles = $_SESSION['forum']['upload_file'][$courseId];
4816
        foreach ($uploadedFiles as $k => $uploadedFile) {
4817
            if (!empty($uploadedFile) && in_array($uploadedFile['id'], $attachIds)) {
4818
                // Buil html table including an input with attachmentID
4819
                $fileDataContent .= '<tr id="'.$uploadedFile['id'].'" ><td>'.$uploadedFile['name'].'</td><td>'.$uploadedFile['size'].'</td><td>&nbsp;'.$uploadedFile['result'].
4820
                    ' </td><td> <input style="width:90%;" type="text" value="'.$uploadedFile['comment'].'" name="file_comments[]"> </td><td>'.
4821
                    $uploadedFile['delete'].'</td>'.
4822
                    '<input type="hidden" value="'.$uploadedFile['id'].'" name="file_ids[]">'.'</tr>';
4823
            } else {
4824
                /*
4825
                 * If attachment data is empty, then delete it from $_SESSION
4826
                 * because could generate and empty row into html table
4827
                 */
4828
                unset($_SESSION['forum']['upload_file'][$courseId][$k]);
4829
            }
4830
        }
4831
    }
4832
    $style = empty($fileDataContent) ? 'display: none;' : '';
4833
    // Forum attachment Ajax table
4834
    return '
4835
    <div class="control-group " style="'.$style.'">
4836
        <label class="control-label">'.get_lang('Attachments list').'</label>
4837
        <div class="controls">
4838
            <table id="attachmentFileList" class="files data_table span10">
4839
                <tr>
4840
                    <th>'.get_lang('Filename').'</th>
4841
                    <th>'.get_lang('Size').'</th>
4842
                    <th>'.get_lang('Status').'</th>
4843
                    <th>'.get_lang('Comment').'</th>
4844
                    <th>'.get_lang('Delete').'</th>
4845
                </tr>
4846
                '.$fileDataContent.'
4847
            </table>
4848
        </div>
4849
    </div>';
4850
}
4851
4852
/**
4853
 * Return an array of prepared attachment data to build forum attachment table
4854
 * Also, save this array into $_SESSION to do available the attachment data.
4855
 *
4856
 * @param int $forumId
4857
 * @param int $threadId
4858
 * @param int $postId
4859
 * @param int $attachId
4860
 * @param int $courseId
4861
 *
4862
 * @return array
4863
 */
4864
function getAttachedFiles(
4865
    $forumId,
4866
    $threadId,
4867
    $postId = 0,
4868
    $attachId = 0,
4869
    $courseId = 0
4870
) {
4871
    $forumId = (int) $forumId;
4872
    $courseId = (int) $courseId;
4873
    $attachId = (int) $attachId;
4874
    $postId = (int) $postId;
4875
    $threadId = (int) $threadId;
4876
4877
    if (empty($courseId)) {
4878
        // $courseId can be null, use api method
4879
        $courseId = api_get_course_int_id();
4880
    }
4881
    if (empty($forumId)) {
4882
        if (!empty($_REQUEST['forum'])) {
4883
            $forumId = (int) $_REQUEST['forum'];
4884
        } else {
4885
            // if forum ID is empty, cannot generate delete url
4886
4887
            return [];
4888
        }
4889
    }
4890
    // Check if exist at least one of them to filter forum attachment select query
4891
    if (empty($postId) && empty($attachId)) {
4892
        return [];
4893
    }
4894
4895
    if (empty($postId)) {
4896
        $filter = "AND iid = $attachId";
4897
    } elseif (empty($attachId)) {
4898
        $filter = "AND post_id = $postId";
4899
    } else {
4900
        $filter = "AND post_id = $postId AND iid = $attachId";
4901
    }
4902
    $forumAttachmentTable = Database::get_course_table(TABLE_FORUM_ATTACHMENT);
4903
    $sql = "SELECT iid
4904
            FROM $forumAttachmentTable
4905
            WHERE c_id = $courseId $filter";
4906
    $result = Database::query($sql);
4907
    $json = [];
4908
    if (Database::num_rows($result) > 0) {
4909
        $repo = Container::getForumAttachmentRepository();
4910
        while ($row = Database::fetch_assoc($result)) {
4911
            /** @var CForumAttachment $attachment */
4912
            $attachment = $repo->find($row['iid']);
4913
            $downloadUrl = $repo->getResourceFileDownloadUrl($attachment);
4914
4915
            // name contains an URL to download attachment file and its filename
4916
            $json['name'] = Display::url(
4917
                api_htmlentities($attachment->getFilename()),
4918
                $downloadUrl,
4919
                ['target' => '_blank', 'class' => 'attachFilename']
4920
            );
4921
            $json['id'] = $row['iid'];
4922
            $json['comment'] = $attachment->getComment();
4923
            // Format file size
4924
            $json['size'] = format_file_size($attachment->getSize());
4925
            // Check if $row is consistent
4926
            if ($attachment) {
4927
                // Set result as success and bring delete URL
4928
                $json['result'] = Display::getMdiIcon('check-circle', 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Uploaded.'));
4929
                $url = api_get_path(WEB_CODE_PATH).'forum/viewthread.php?'.api_get_cidreq().'&action=delete_attach&forum='.$forumId.'&thread='.$threadId.'&id_attach='.$row['iid'];
4930
                $json['delete'] = Display::url(
4931
                    Display::getMdiIcon(ActionIcon::DELETE, 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Delete')),
4932
                    $url,
4933
                    ['class' => 'deleteLink']
4934
                );
4935
            } else {
4936
                // If not, set an exclamation result
4937
                $json['result'] = Display::getMdiIcon('close-circle', 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Error'));
4938
            }
4939
            // Store array data into $_SESSION
4940
            $_SESSION['forum']['upload_file'][$courseId][$json['id']] = $json;
4941
        }
4942
    }
4943
4944
    return $json;
4945
}
4946
4947
/**
4948
 * Clear forum attachment data stored in $_SESSION,
4949
 * If is not defined post, it will clear all forum attachment data from course.
4950
 *
4951
 * @param int $postId   -1 : Clear all attachments from course stored in $_SESSION
4952
 *                      0 : Clear attachments from course, except from temporal post "0"
4953
 *                      but without delete them from file system and database
4954
 *                      Other values : Clear attachments from course except specified post
4955
 *                      and delete them from file system and database
4956
 * @param int $courseId : Course ID, if it is null, will use api_get_course_int_id()
4957
 *
4958
 * @return array
4959
 */
4960
function clearAttachedFiles($postId = 0, $courseId = 0)
4961
{
4962
    // Init variables
4963
    $courseId = (int) $courseId;
4964
    $postId = (int) $postId;
4965
    $array = [];
4966
    if (empty($courseId)) {
4967
        // $courseId can be null, use api method
4968
        $courseId = api_get_course_int_id();
4969
    }
4970
    if (-1 === $postId) {
4971
        // If post ID is -1 then delete course's attachment data from $_SESSION
4972
        if (!empty($_SESSION['forum']['upload_file'][$courseId])) {
4973
            $array = array_keys($_SESSION['forum']['upload_file'][$courseId]);
4974
            unset($_SESSION['forum']['upload_file'][$courseId]);
4975
        }
4976
    } else {
4977
        $attachIds = getAttachmentIdsByPostId($postId, $courseId);
4978
        if (!empty($_SESSION['forum']['upload_file'][$courseId]) &&
4979
            is_array($_SESSION['forum']['upload_file'][$courseId])) {
4980
            foreach ($_SESSION['forum']['upload_file'][$courseId] as $attachId => $attach) {
4981
                if (!in_array($attachId, $attachIds)) {
4982
                    // If attach ID is not into specified post, delete attachment
4983
                    // Save deleted attachment ID
4984
                    $array[] = $attachId;
4985
                    if (0 !== $postId) {
4986
                        // Post 0 is temporal, delete them from file system and DB
4987
                        delete_attachment(0, $attachId);
4988
                    }
4989
                    // Delete attachment data from $_SESSION
4990
                    unset($_SESSION['forum']['upload_file'][$courseId][$attachId]);
4991
                }
4992
            }
4993
        }
4994
    }
4995
4996
    return $array;
4997
}
4998
4999
/**
5000
 * Returns an array of forum attachment ids into a course and forum post.
5001
 *
5002
 * @param int $postId
5003
 * @param int $courseId
5004
 *
5005
 * @return array
5006
 */
5007
function getAttachmentIdsByPostId($postId, $courseId = 0)
5008
{
5009
    $array = [];
5010
    $courseId = (int) $courseId;
5011
    $postId = (int) $postId;
5012
    if (empty($courseId)) {
5013
        // $courseId can be null, use api method
5014
        $courseId = api_get_course_int_id();
5015
    }
5016
    if ($courseId > 0) {
5017
        $forumAttachmentTable = Database::get_course_table(TABLE_FORUM_ATTACHMENT);
5018
        $sql = "SELECT iid FROM $forumAttachmentTable
5019
                WHERE c_id = $courseId AND post_id = $postId";
5020
        $result = Database::query($sql);
5021
        if (false !== $result && Database::num_rows($result) > 0) {
5022
            while ($row = Database::fetch_assoc($result)) {
5023
                $array[] = $row['iid'];
5024
            }
5025
        }
5026
    }
5027
5028
    return $array;
5029
}
5030
5031
function getPostStatus(CForum $forum, array $row, bool $addWrapper = true): string
5032
{
5033
    $statusIcon = '';
5034
    if ($forum->isModerated()) {
5035
        if ($addWrapper) {
5036
            $statusIcon = '<br /><br /><span id="status_post_'.$row['iid'].'">';
5037
        }
5038
        $row['status'] = empty($row['status']) ? 2 : $row['status'];
5039
5040
        $addUrl = false;
5041
        $showStatus = false;
5042
        if (api_is_allowed_to_edit(false, true)) {
5043
            $addUrl = true;
5044
        } else {
5045
            if ($row['user_id'] == api_get_user_id()) {
5046
                $showStatus = true;
5047
            }
5048
        }
5049
5050
        $label = '';
5051
        $icon = '';
5052
        $buttonType = '';
5053
        switch ($row['status']) {
5054
            case CForumPost::STATUS_VALIDATED:
5055
                $label = get_lang('Validated');
5056
                $icon = 'check-circle';
5057
                $buttonType = 'success';
5058
5059
                break;
5060
            case CForumPost::STATUS_WAITING_MODERATION:
5061
                $label = get_lang('Waiting for moderation');
5062
                $icon = 'alert';
5063
                $buttonType = 'warning';
5064
5065
                break;
5066
            case CForumPost::STATUS_REJECTED:
5067
                $label = get_lang('Rejected');
5068
                $icon = 'minus-circle';
5069
                $buttonType = 'danger';
5070
5071
                break;
5072
        }
5073
5074
        if ($addUrl) {
5075
            $statusIcon .= Display::toolbarButton(
5076
                $label.'&nbsp;',
5077
                'javascript:void(0)',
5078
                $icon,
5079
                $buttonType,
5080
                ['class' => 'change_post_status']
5081
            );
5082
        } else {
5083
            if ($showStatus) {
5084
                $statusIcon .= Display::label(
5085
                    Display::getMdiIcon($icon).$label,
5086
                    $buttonType
5087
                );
5088
            }
5089
        }
5090
5091
        if ($addWrapper) {
5092
            $statusIcon .= '</span>';
5093
        }
5094
    }
5095
5096
    return $statusIcon;
5097
}
5098
5099
/**
5100
 * @param CForum $forum
5101
 * @param int    $threadId
5102
 * @param int    $status
5103
 */
5104
function getCountPostsWithStatus($status, $forum, $threadId = null)
5105
{
5106
    $em = Database::getManager();
5107
    $criteria = Criteria::create();
5108
    $criteria
5109
        ->where(Criteria::expr()->eq('status', $status))
5110
        //->andWhere(Criteria::expr()->eq('cId', $forum->getCId()))
5111
        ->andWhere(Criteria::expr()->eq('visible', 1))
5112
    ;
5113
5114
    if (!empty($threadId)) {
5115
        $criteria->andWhere(Criteria::expr()->eq('thread', $threadId));
5116
    }
5117
5118
    $qb = $em->getRepository(CForumPost::class)->createQueryBuilder('p');
5119
    $qb->select('count(p.iid)')
5120
        ->addCriteria($criteria);
5121
5122
    return $qb->getQuery()->getSingleScalarResult();
5123
}
5124
5125
/**
5126
 * @param CForum     $forum
5127
 * @param CForumPost $post
5128
 *
5129
 * @return bool
5130
 */
5131
function postIsEditableByStudent($forum, $post)
5132
{
5133
    if (api_is_platform_admin() || api_is_allowed_to_edit()) {
5134
        return true;
5135
    }
5136
5137
    if (1 == $forum->isModerated()) {
5138
        if (null === $post->getStatus()) {
5139
            return true;
5140
        } else {
5141
            return in_array(
5142
                $post->getStatus(),
5143
                [
5144
                    CForumPost::STATUS_WAITING_MODERATION,
5145
                    CForumPost::STATUS_REJECTED,
5146
                ]
5147
            );
5148
        }
5149
    }
5150
5151
    return true;
5152
}
5153
5154
/**
5155
 * @return bool
5156
 */
5157
function savePostRevision(CForumPost $post)
5158
{
5159
    $userId = api_get_user_id();
5160
5161
    if ($post->getUser()->getId() != $userId) {
5162
        return false;
5163
    }
5164
5165
    $status = (int) !postNeedsRevision($post);
5166
    $extraFieldValue = new ExtraFieldValue('forum_post');
5167
    $params = [
5168
        'item_id' => $post->getIid(),
5169
        'extra_ask_for_revision' => ['extra_ask_for_revision' => $status],
5170
    ];
5171
    if (empty($status)) {
5172
        unset($params['extra_ask_for_revision']);
5173
    }
5174
    $extraFieldValue->saveFieldValues(
5175
        $params,
5176
        true,
5177
        false,
5178
        ['ask_for_revision']
5179
    );
5180
}
5181
5182
/**
5183
 * @param int $postId
5184
 *
5185
 * @return string
5186
 */
5187
function getPostRevision($postId)
5188
{
5189
    $extraFieldValue = new ExtraFieldValue('forum_post');
5190
    $value = $extraFieldValue->get_values_by_handler_and_field_variable(
5191
        $postId,
5192
        'revision_language'
5193
    );
5194
    $revision = '';
5195
    if ($value && isset($value['value'])) {
5196
        $revision = $value['value'];
5197
    }
5198
5199
    return $revision;
5200
}
5201
5202
function postNeedsRevision(CForumPost $post): bool
5203
{
5204
    $postId = $post->getIid();
5205
    $extraFieldValue = new ExtraFieldValue('forum_post');
5206
    $value = $extraFieldValue->get_values_by_handler_and_field_variable(
5207
        $postId,
5208
        'ask_for_revision'
5209
    );
5210
    $hasRevision = false;
5211
    if ($value && isset($value['value'])) {
5212
        return 1 == $value['value'];
5213
    }
5214
5215
    return $hasRevision;
5216
}
5217
5218
/**
5219
 * Generates an HTML button to ask for a review
5220
 */
5221
function getAskRevisionButton(CForumPost $post, CForumThread $threadInfo): string
5222
{
5223
    if ('false' === api_get_setting('forum.allow_forum_post_revisions')) {
5224
        return '';
5225
    }
5226
5227
    $postId = $post->getIid();
5228
5229
    $status = 'btn--plain';
5230
    if (postNeedsRevision($post)) {
5231
        $status = 'btn--success';
5232
    }
5233
5234
    return Display::url(
5235
        get_lang('Ask for a revision'),
5236
        api_get_path(WEB_CODE_PATH).'forum/viewthread.php?'.
5237
        api_get_cidreq().'&action=ask_revision&post_id='.$postId.'&forum='.$threadInfo->getForum()->getIid().'&thread='.$threadInfo->getIid(),
5238
        ['class' => "btn $status", 'title' => get_lang('Ask for a revision')]
5239
    );
5240
}
5241
5242
/**
5243
 * Generates an HTML button to give a review
5244
 */
5245
function getGiveRevisionButton(int $postId, CForumThread $threadInfo): string
5246
{
5247
    return Display::toolbarButton(
5248
        get_lang('Give revision'),
5249
        api_get_path(WEB_CODE_PATH).'forum/reply.php?'.api_get_cidreq().'&'.http_build_query(
5250
            [
5251
                'forum' => $threadInfo->getForum()->getIid(),
5252
                'thread' => $threadInfo->getIid(),
5253
                'post' => $postId,
5254
                'action' => 'replymessage',
5255
                'give_revision' => 1,
5256
            ]
5257
        ),
5258
        'reply',
5259
        'primary',
5260
        ['id' => "reply-to-post-{$postId}"]
5261
    );
5262
}
5263
5264
/**
5265
 * Generates an HTML button to report a post as offensive
5266
 */
5267
function getReportButton(int $postId, CForumThread $threadInfo): string
5268
{
5269
    return Display::url(
5270
        Display::getMdiIcon('flag'),
5271
        api_get_path(WEB_CODE_PATH).'forum/viewthread.php?'.
5272
        api_get_cidreq().'&action=report&post_id='.$postId.
5273
        '&forum='.$threadInfo->getForum()->getIid().'&thread='.$threadInfo->getIid(),
5274
        ['class' => 'btn btn--danger', 'title' => get_lang('Report')]
5275
    );
5276
}
5277
5278
/**
5279
 * @return bool
5280
 */
5281
function reportAvailable()
5282
{
5283
    $extraFieldValue = new ExtraFieldValue('course');
5284
    $value = $extraFieldValue->get_values_by_handler_and_field_variable(
5285
        api_get_course_int_id(),
5286
        'allow_forum_report_button'
5287
    );
5288
    $allowReport = false;
5289
    if ($value && isset($value['value']) && 1 == $value['value']) {
5290
        $allowReport = true;
5291
    }
5292
5293
    return $allowReport;
5294
}
5295
5296
/**
5297
 * @return array
5298
 */
5299
function getReportRecipients()
5300
{
5301
    $extraFieldValue = new ExtraFieldValue('course');
5302
    $value = $extraFieldValue->get_values_by_handler_and_field_variable(
5303
        api_get_course_int_id(),
5304
        'forum_report_recipients'
5305
    );
5306
    $users = [];
5307
    if ($value && isset($value['value'])) {
5308
        $usersType = explode(';', $value['value']);
5309
5310
        foreach ($usersType as $type) {
5311
            switch ($type) {
5312
                case 'teachers':
5313
                    $teachers = CourseManager::get_teacher_list_from_course_code(api_get_course_id());
5314
                    if (!empty($teachers)) {
5315
                        $users = array_merge($users, array_column($teachers, 'user_id'));
5316
                    }
5317
5318
                break;
5319
                case 'admins':
5320
                    $admins = UserManager::get_all_administrators();
5321
                    if (!empty($admins)) {
5322
                        $users = array_merge($users, array_column($admins, 'user_id'));
5323
                    }
5324
5325
                    break;
5326
                case 'community_managers':
5327
                    $managers = api_get_setting('profile.community_managers_user_list', true);
5328
                    if (!empty($managers) && isset($managers['users'])) {
5329
                        $users = array_merge($users, $managers['users']);
5330
                    }
5331
5332
                    break;
5333
            }
5334
        }
5335
5336
        $users = array_unique(array_filter($users));
5337
    }
5338
5339
    return $users;
5340
}
5341
5342
/**
5343
 * Sends an e-mail to all users from getReportRecipients() (users with extra field 'forum_report_recipients')
5344
 */
5345
function reportPost(CForumPost $post, CForum $forumInfo, CForumThread $threadInfo): bool
5346
{
5347
    if (!reportAvailable()) {
5348
        return false;
5349
    }
5350
5351
    if (empty($forumInfo) || empty($threadInfo)) {
5352
        return false;
5353
    }
5354
5355
    $postId = $post->getIid();
5356
5357
    $currentUser = api_get_user_info();
5358
    $users = getReportRecipients();
5359
    if (!empty($users)) {
5360
        $url = api_get_path(WEB_CODE_PATH).
5361
            'forum/viewthread.php?forum='.$forumInfo->getIid().'&thread='.$threadInfo->getIid().'&'.api_get_cidreq(true, true, false).'&post_id='.$postId.'#post_id_'.$postId;
5362
        $postLink = Display::url(
5363
            $post->getTitle(),
5364
            $url
5365
        );
5366
        $subject = get_lang('Post reported');
5367
        $content = sprintf(
5368
            get_lang('User %s has reported the message %s in the forum %s'),
5369
            $currentUser['complete_name'],
5370
            $postLink,
5371
            $forumInfo->getTitle()
5372
        );
5373
        foreach ($users as $userId) {
5374
            MessageManager::send_message_simple($userId, $subject, $content);
5375
        }
5376
    }
5377
5378
    return true;
5379
}
5380
5381
function getVisibleForums($courseId, $sessionId): array
5382
{
5383
    $forums = get_forums($courseId, $sessionId);
5384
    $visibleForums = [];
5385
5386
    foreach ($forums as $forum) {
5387
        $forumSession = $forum->getFirstResourceLink()->getSession();
5388
        if ($sessionId > 0) {
5389
            if (null === $forumSession) {
5390
                $threads = get_threads($forum->getIid(), $courseId, $sessionId, true);
5391
                if (!empty($threads)) {
5392
                    $visibleForums[] = $forum;
5393
                }
5394
            } else {
5395
                $visibleForums[] = $forum;
5396
            }
5397
        } else {
5398
            $visibleForums[] = $forum;
5399
        }
5400
    }
5401
5402
    return $visibleForums;
5403
}
5404
5405
function getVisibleForumsInCategory($categoryId, $courseId, $sessionId): array
5406
{
5407
    $forumsInCategory = get_forums_in_category($categoryId, $courseId, $sessionId);
5408
    $visibleForums = [];
5409
5410
    foreach ($forumsInCategory as $forum) {
5411
        $forumSession = $forum->getFirstResourceLink()->getSession();
5412
        if ($sessionId > 0) {
5413
            if (null === $forumSession) {
5414
                $threads = get_threads($forum->getIid(), $courseId, $sessionId, true);
5415
                if (!empty($threads)) {
5416
                    $visibleForums[] = $forum;
5417
                }
5418
            } else {
5419
                $visibleForums[] = $forum;
5420
            }
5421
        } else {
5422
            $visibleForums[] = $forum;
5423
        }
5424
    }
5425
5426
    return $visibleForums;
5427
}
5428