Passed
Push — master ( 2055ed...3377e7 )
by Julito
06:59
created

deletePost()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 49
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

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