Passed
Push — master ( 4c9773...86da80 )
by Yannick
10:11 queued 17s
created

getForum()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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