Passed
Push — master ( 65453d...b1f8c2 )
by
unknown
16:36 queued 07:54
created

deletePost()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 61
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 36
nc 6
nop 1
dl 0
loc 61
rs 8.7217
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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