Passed
Push — master ( 146d49...08a262 )
by Julito
07:06 queued 12s
created

returnVisibleInvisibleIcon()   B

Complexity

Conditions 7
Paths 9

Size

Total Lines 31
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

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

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

    return false;
}

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

Loading history...
497
}
498
499
/**
500
 * Generates an HTML form to edit the forum category
501
 * @throws Exception
502
 */
503
function editForumCategoryForm(CForumCategory $category): string
504
{
505
    $categoryId = $category->getIid();
506
    $form = new FormValidator(
507
        'forumcategory',
508
        'post',
509
        'index.php?action=edit_category&'.api_get_cidreq().'&id='.$categoryId
510
    );
511
    // Setting the form elements.
512
    $form->addElement('header', '', get_lang('Edit forumCategory'));
513
514
    $form->addElement('hidden', 'action', 'edit_category');
515
    $form->addElement('hidden', 'forum_category_id');
516
    $form->addElement('text', 'forum_category_title', get_lang('Title'));
517
518
    $form->addElement(
519
        'html_editor',
520
        'forum_category_comment',
521
        get_lang('Comment'),
522
        null,
523
        ['ToolbarSet' => 'Forum', 'Width' => '98%', 'Height' => '200']
524
    );
525
526
    $extraField = new ExtraField('forum_category');
527
    $extraField->addElements(
528
        $form,
529
        $categoryId,
530
        [], //exclude
531
        false, // filter
532
        false, // tag as select
533
        [], //show only fields
534
        [], // order fields
535
        [] // extra data
536
    );
537
538
    $form->addButtonUpdate(get_lang('Edit category'), 'SubmitEdit forumCategory');
539
540
    // Setting the default values.
541
    $defaultvalues['forum_category_id'] = $categoryId;
542
    $defaultvalues['forum_category_title'] = $category->getCatTitle();
543
    $defaultvalues['forum_category_comment'] = $category->getCatComment();
544
    $form->setDefaults($defaultvalues);
545
546
    // Setting the rules.
547
    $form->addRule('forum_category_title', get_lang('Required field'), 'required');
548
549
    // Validation or display
550
    if ($form->validate()) {
551
        $check = Security::check_token('post');
552
        if ($check) {
553
            $values = $form->exportValues();
554
            saveForumCategory($values);
555
        }
556
        Security::clear_token();
557
    } else {
558
        $token = Security::get_token();
559
        $form->addElement('hidden', 'sec_token');
560
        $form->setConstants(['sec_token' => $token]);
561
562
        return $form->returnForm();
563
    }
564
565
    return '';
566
}
567
568
/**
569
 * This function stores the forum category in the database.
570
 * The new category is added to the end.
571
 * @throws Exception
572
 */
573
function saveForumCategory(array $values, array $courseInfo = [], bool $showMessage = true): CForumCategory
574
{
575
    $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo;
576
    $course_id = $courseInfo['real_id'];
577
    $new_max = 1;
578
    $session_id = api_get_session_id();
579
    $clean_cat_title = $values['forum_category_title'];
580
    $repo = Container::getForumCategoryRepository();
581
582
    $message = '';
583
    if (isset($values['forum_category_id'])) {
584
        /** @var CForumCategory $category */
585
        $category = $repo->find($values['forum_category_id']);
586
        $category
587
            ->setCatComment($values['forum_category_comment'] ?? '')
588
            ->setCatTitle($values['forum_category_title'])
589
        ;
590
        $repo->update($category);
591
        $message = get_lang('The forum category has been modified');
592
593
        $logInfo = [
594
            'tool' => TOOL_FORUM,
595
            'action' => 'update-forumcategory',
596
            'action_details' => 'forumcategory',
597
            'info' => $clean_cat_title,
598
        ];
599
        Event::registerLog($logInfo);
600
601
        $values['item_id'] = $values['forum_category_id'];
602
    } else {
603
        $course = api_get_course_entity($course_id);
604
        $session = api_get_session_entity($session_id);
605
606
        $category = new CForumCategory();
607
        $category
608
            ->setCatTitle($clean_cat_title)
609
            ->setCatComment($values['forum_category_comment'] ?? '')
610
            ->setCatOrder($new_max)
611
            ->setParent($course)
612
            ->addCourseLink($course, $session)
613
        ;
614
        $repo->create($category);
615
616
        $last_id = $category->getIid();
617
        if ($last_id > 0) {
618
            $message = get_lang('The forum category has been added');
619
        }
620
621
        $logInfo = [
622
            'tool' => TOOL_FORUM,
623
            'action' => 'new-forumcategory',
624
            'action_details' => 'forumcategory',
625
            'info' => $clean_cat_title,
626
        ];
627
        Event::registerLog($logInfo);
628
629
        $values['item_id'] = $last_id;
630
    }
631
632
    $extraFieldValue = new ExtraFieldValue('forum_category');
633
    $extraFieldValue->saveFieldValues($values);
634
635
    if ($showMessage) {
636
        Display::addFlash(Display::return_message($message, 'confirmation'));
637
    }
638
639
    return $category;
640
}
641
642
/**
643
 * Stores a new or edited forum in the database.
644
 * The new forum is added to the end of the others.
645
 * Returns an ID if the option 'returnId' was set to true, otherwise returns a confirmation message
646
 */
647
function store_forum(array $values, array $courseInfo = [], bool $returnId = false): string|int
648
{
649
    $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo;
650
    $courseId = $courseInfo['real_id'];
651
    $session_id = api_get_session_id();
652
653
    // Find the max forum_order for the given category. The new forum is added at the end => max cat_order + &
654
    if (null === $values['forum_category']) {
655
        $new_max = null;
656
    } else {
657
        /*$sql = "SELECT MAX(forum_order) as sort_max
658
                FROM $table_forums
659
                WHERE
660
                    c_id = $courseId AND
661
                    forum_category='".Database::escape_string($values['forum_category'])."'";
662
        $result = Database::query($sql);
663
        $row = Database::fetch_array($result);
664
        $new_max = $row['sort_max'] + 1;*/
665
    }
666
667
    $new_max = 0;
668
669
    // Forum images
670
    $has_attachment = false;
671
    $image_moved = true;
672
    if (!empty($_FILES['picture']['name'])) {
673
        $upload_ok = process_uploaded_file($_FILES['picture']);
674
        $has_attachment = true;
675
    }
676
677
    // Remove existing picture if it was requested.
678
    if (!empty($_POST['remove_picture'])) {
679
        deleteForumImage($values['forum_id']);
680
    }
681
682
    $new_file_name = '';
683
    if (isset($upload_ok)) {
684
        if ($has_attachment) {
685
            throw new Exception('$has_attachment');
686
            /*$course_dir = $courseInfo['path'].'/upload/forum/images';
687
            $sys_course_path = api_get_path(SYS_COURSE_PATH);
688
            $updir = $sys_course_path.$course_dir;
689
            // Try to add an extension to the file if it hasn't one.
690
            $new_file_name = add_ext_on_mime(
691
                Database::escape_string($_FILES['picture']['name']),
692
                $_FILES['picture']['type']
693
            );
694
            if (!filter_extension($new_file_name)) {
695
                //Display::addFlash(Display::return_message(get_lang('File upload failed: this file extension or file type is prohibited'), 'error'));
696
                $image_moved = false;
697
            } else {
698
                $file_extension = explode('.', $_FILES['picture']['name']);
699
                $file_extension = strtolower($file_extension[count($file_extension) - 1]);
700
                $new_file_name = uniqid('').'.'.$file_extension;
701
                $new_path = $updir.'/'.$new_file_name;
702
                $result = @move_uploaded_file($_FILES['picture']['tmp_name'], $new_path);
703
                // Storing the attachments if any
704
                if ($result) {
705
                    $image_moved = true;
706
                }
707
            }*/
708
        }
709
    }
710
711
    $repo = Container::getForumRepository();
712
713
    if (!isset($values['forum_id'])) {
714
        $forum = new CForum();
715
        $forum->setForumOrder($new_max ?? null);
716
    } else {
717
        /** @var CForum $forum */
718
        $forum = $repo->find($values['forum_id']);
719
    }
720
721
    $forumCategory = null;
722
    if (!empty($values['forum_category'])) {
723
        $repoForumCategory = Container::getForumCategoryRepository();
724
        $forumCategory = $repoForumCategory->find($values['forum_category']);
725
    }
726
727
    $lpId = $values['lp_id'] ?? 0;
728
    $lpRepo = Container::getLpRepository();
729
    $lp = null;
730
    if (!empty($lpId)) {
731
        /** @var CLp $lp */
732
        $lp = $lpRepo->find($lpId);
733
    }
734
735
    $forum
736
        ->setForumTitle($values['forum_title'])
737
        ->setForumComment($values['forum_comment'] ?? '')
738
        ->setForumCategory($forumCategory)
739
        ->setAllowAnonymous($values['allow_anonymous_group']['allow_anonymous'] ?? 0)
740
        ->setAllowEdit($values['students_can_edit_group']['students_can_edit'] ?? 0)
741
        ->setApprovalDirectPost((string) ($values['approval_direct_group']['approval_direct'] ?? '0'))
742
        ->setAllowAttachments($values['allow_attachments_group']['allow_attachments'] ?? 0)
743
        ->setAllowNewThreads((int) ($values['allow_new_threads_group']['allow_new_threads'] ?? 0))
744
        ->setDefaultView($values['default_view_type_group']['default_view_type'] ?? '')
745
        ->setForumOfGroup((string) ($values['group_forum'] ?? ''))
746
        ->setForumGroupPublicPrivate($values['public_private_group_forum_group']['public_private_group_forum'] ?? '')
747
        ->setModerated((bool) ($values['moderated']['moderated'] ?? false))
748
        ->setStartTime(!empty($values['start_time']) ? api_get_utc_datetime($values['start_time'], true, true) : null)
749
        ->setEndTime(!empty($values['end_time']) ? api_get_utc_datetime($values['end_time'], true, true) : null)
750
        ->setLp($lp)
751
    ;
752
753
    $course = api_get_course_entity($courseId);
754
    $session = api_get_session_entity($session_id);
755
756
    if (isset($values['forum_id'])) {
757
        // Update.
758
        $repo->update($forum);
759
760
        if (isset($upload_ok)) {
761
            if ($has_attachment) {
762
                //$params['forum_image'] = $new_file_name;
763
            }
764
        }
765
766
        if (isset($values['remove_picture']) && 1 == $values['remove_picture']) {
767
            /*$params['forum_image'] = '';
768
            deleteForumImage($forum);*/
769
        }
770
771
        // Move groups from one group to another
772
        if (isset($values['group_forum']) && false) {
773
            $forumData = get_forums($values['forum_id']);
774
            $currentGroupId = $forumData['forum_of_group'];
775
        }
776
777
        $return_message = get_lang('The forum has been modified');
778
        $forumId = $forum->getIid();
779
780
        $logInfo = [
781
            'tool' => TOOL_FORUM,
782
            'tool_id' => $values['forum_id'],
783
            'action' => 'update-forum',
784
            'action_details' => 'forum',
785
            'info' => $values['forum_title'],
786
        ];
787
        Event::registerLog($logInfo);
788
    } else {
789
        if ($image_moved) {
790
            $new_file_name = isset($new_file_name) ? $new_file_name : '';
791
        }
792
        $forum
793
            ->setParent($forumCategory)
794
            ->addCourseLink($course, $session);
795
        $repo->create($forum);
796
797
        $forumId = $forum->getIid();
798
        if ($forumId > 0) {
799
            $courseCode = $courseInfo['code'];
800
            $subscribe = (int) api_get_course_setting('subscribe_users_to_forum_notifications');
801
802
            $status = STUDENT;
803
            if (!empty($session_id)) {
804
                $status = 0;
805
            }
806
            if (1 === $subscribe) {
807
                $userList = CourseManager::get_user_list_from_course_code(
808
                    $courseCode,
809
                    $session_id,
810
                    null,
811
                    null,
812
                    $status
813
                );
814
                foreach ($userList as $userInfo) {
815
                    set_notification('forum', $forumId, false, $userInfo, $courseInfo);
816
                }
817
            }
818
819
            $logInfo = [
820
                'tool' => TOOL_FORUM,
821
                'tool_id' => $forumId,
822
                'action' => 'new-forum',
823
                'action_details' => 'forum',
824
                'info' => $values['forum_title'],
825
            ];
826
            Event::registerLog($logInfo);
827
        }
828
        $return_message = get_lang('The forum has been added');
829
    }
830
831
    if ($returnId) {
832
        return $forumId;
833
    }
834
835
    return $return_message;
836
}
837
838
function deletePost(CForumPost $post): void
839
{
840
    $table_threads = Database::get_course_table(TABLE_FORUM_THREAD);
841
    $em = Database::getManager();
842
    if ($post) {
843
        $em
844
            ->createQuery('
845
                UPDATE ChamiloCourseBundle:CForumPost p
846
                SET p.postParent = :parent_of_deleted_post
847
                WHERE
848
                    p.postParent = :post AND
849
                    p.thread = :thread_of_deleted_post AND
850
                    p.forum = :forum_of_deleted_post
851
            ')
852
            ->execute([
853
                'parent_of_deleted_post' => $post->getPostParent(),
854
                'post' => $post->getIid(),
855
                'thread_of_deleted_post' => $post->getThread() ? $post->getThread()->getIid() : 0,
856
                'forum_of_deleted_post' => $post->getForum(),
857
            ]);
858
859
        $attachments = $post->getAttachments();
860
        if (!empty($attachments)) {
861
            foreach ($attachments as $attachment) {
862
                $em->remove($attachment);
863
            }
864
        }
865
866
        $em->remove($post);
867
        $em->flush();
868
    }
869
870
    $lastPostOfTheTread = getLastPostOfThread($post->getThread()->getIid());
871
872
    if (!empty($lastPostOfTheTread)) {
873
        // Decreasing the number of replies for this thread and also changing the last post information.
874
        $sql = "UPDATE $table_threads
875
                SET
876
                    thread_replies = thread_replies - 1,
877
                    thread_last_post = ".$lastPostOfTheTread['iid'].",
878
                    thread_date = '".$lastPostOfTheTread['post_date']."'
879
                WHERE iid = ".$post->getThread()->getIid();
880
        Database::query($sql);
881
        Display::addFlash(Display::return_message(get_lang('Post has been deleted')));
882
    } else {
883
        // We deleted the very last post of the thread, so we need to delete the thread as well.
884
        $sql = "DELETE FROM $table_threads
885
                WHERE iid = ".$post->getThread()->getIid();
886
        Database::query($sql);
887
888
        Display::addFlash(Display::return_message(get_lang('Thread deleted')));
889
    }
890
}
891
892
/**
893
 * This function gets the all information of the last (=most recent) post of the thread
894
 * This can be done by sorting the posts that have the field threadId=$threadId and sort them by post_date.
895
 * @author Patrick Cool <[email protected]>, Ghent University
896
 * @version february 2006, dokeos 1.8
897
 */
898
function getLastPostOfThread(int $threadId): array
899
{
900
    $table_posts = Database::get_course_table(TABLE_FORUM_POST);
901
    $sql = "SELECT iid, post_date FROM $table_posts
902
            WHERE threadId = ".(int) $threadId.'
903
            ORDER BY post_date DESC LIMIT 1';
904
    $result = Database::query($sql);
905
    if (Database::num_rows($result) > 0) {
906
        return Database::fetch_array($result);
907
    }
908
909
    return [];
910
}
911
912
/**
913
 * @param string $content                   Type of content forum category, forum, thread, post
914
 * @param int    $id                        the id of the content we want to make invisible
915
 * @param int    $current_visibility_status what is the current status of the visibility (0 = invisible, 1 = visible)
916
 * @param array  $additional_url_parameters
917
 *
918
 * @return string HTML
919
 */
920
function returnVisibleInvisibleIcon(
921
    string $content,
922
    int $id,
923
    int $current_visibility_status,
924
    array $additional_url_parameters = []
925
): string
926
{
927
    $html = '';
928
929
    if (1 == $current_visibility_status) {
930
        $html .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&';
931
        if (is_array($additional_url_parameters)) {
932
            foreach ($additional_url_parameters as $key => $value) {
933
                $html .= $key.'='.$value.'&';
934
            }
935
        }
936
        $html .= 'action=invisible&content='.$content.'&id='.$id.'">'.
937
            Display::return_icon('visible.png', get_lang('MakeInvisible'), [], ICON_SIZE_SMALL).'</a>';
938
    }
939
    if (0 == $current_visibility_status) {
940
        $html .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&';
941
        if (is_array($additional_url_parameters)) {
942
            foreach ($additional_url_parameters as $key => $value) {
943
                $html .= $key.'='.$value.'&';
944
            }
945
        }
946
        $html .= 'action=visible&content='.$content.'&id='.$id.'">'.
947
            Display::return_icon('invisible.png', get_lang('Make Visible'), [], ICON_SIZE_SMALL).'</a>';
948
    }
949
950
    return $html;
951
}
952
953
/**
954
 * Returns an HTML string with the appropriate icon
955
 * @param string $content                   Type of content forum category, forum, thread, post
956
 * @param int    $id                        the id of the content we want to make invisible
957
 * @param int    $current_lock_status what is the current status of the visibility (0 = unlocked, 1 = locked)
958
 * @param array  $additional_url_parameters
959
 */
960
function returnLockUnlockIcon(
961
    string $content,
962
    int $id,
963
    int $current_lock_status,
964
    array $additional_url_parameters = []
965
): string
966
{
967
    $html = '';
968
    //check if the forum is blocked due
969
    if ('thread' === $content) {
970
        if (api_resource_is_locked_by_gradebook($id, LINK_FORUM_THREAD)) {
971
            return $html.Display::return_icon(
972
                    'lock_na.png',
973
                    get_lang(
974
                        'This option is not available because this activity is contained by an assessment, which is currently locked. To unlock the assessment, ask your platform administrator.'
975
                    ),
976
                    [],
977
                    ICON_SIZE_SMALL
978
                );
979
        }
980
    }
981
    if ('1' == $current_lock_status) {
982
        $html .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&';
983
        if (is_array($additional_url_parameters)) {
984
            foreach ($additional_url_parameters as $key => $value) {
985
                $html .= $key.'='.$value.'&';
986
            }
987
        }
988
        $html .= 'action=unlock&content='.$content.'&id='.$id.'">'.
989
            Display::return_icon('lock.png', get_lang('Unlock'), [], ICON_SIZE_SMALL).'</a>';
990
    }
991
    if ('0' == $current_lock_status) {
992
        $html .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&';
993
        if (is_array($additional_url_parameters)) {
994
            foreach ($additional_url_parameters as $key => $value) {
995
                $html .= $key.'='.$value.'&';
996
            }
997
        }
998
        $html .= 'action=lock&content='.$content.'&id='.$id.'">'.
999
            Display::return_icon('unlock.png', get_lang('Lock'), [], ICON_SIZE_SMALL).'</a>';
1000
    }
1001
1002
    return $html;
1003
}
1004
1005
/**
1006
 * This function takes care of the display of the up and down icon.
1007
 *
1008
 * @param string $content what is it that we want to make (in)visible: forum category, forum, thread, post
1009
 * @param int    $id      is the id of the item we want to display the icons for
1010
 * @param array  $list    is an array of all the items. All items in this list should have
1011
 *                        an up and down icon except for the first (no up icon) and the last (no down icon)
1012
 *                        The key of this $list array is the id of the item.
1013
 *
1014
 * @return string HTML
1015
 */
1016
function returnUpDownIcon(string $content, int $id, array $list): string
1017
{
1018
    $total_items = count($list);
1019
    $position = 0;
1020
    $internal_counter = 0;
1021
    $forumCategory = isset($_GET['forumcategory']) ? Security::remove_XSS($_GET['forumcategory']) : null;
1022
1023
    if (!empty($list)) {
1024
        foreach ($list as $item) {
1025
            $internal_counter++;
1026
            if ($id == $item->getIid()) {
1027
                $position = $internal_counter;
1028
            }
1029
        }
1030
    }
1031
1032
    if ($position > 1) {
1033
        $return_value = '<a
1034
                href="'.api_get_self().'?'.api_get_cidreq().'&action=move&direction=up&content='.$content.'&forumcategory='.$forumCategory.'&id='.$id.'"
1035
                title="'.get_lang('Move up').'">'.
1036
            Display::return_icon('up.png', get_lang('Move up'), [], ICON_SIZE_SMALL).'</a>';
1037
    } else {
1038
        $return_value = Display::url(
1039
            Display::return_icon('up_na.png', '-', [], ICON_SIZE_SMALL),
1040
            'javascript:void(0)'
1041
        );
1042
    }
1043
1044
    if ($position < $total_items) {
1045
        $return_value .= '<a
1046
            href="'.api_get_self().'?'.api_get_cidreq().'&action=move&direction=down&content='.$content.'&forumcategory='.$forumCategory.'&id='.$id.'"
1047
            title="'.get_lang('Move down').'" >'.
1048
            Display::return_icon('down.png', get_lang('Move down'), [], ICON_SIZE_SMALL).'</a>';
1049
    } else {
1050
        $return_value = Display::url(
1051
            Display::return_icon('down_na.png', '-', [], ICON_SIZE_SMALL),
1052
            'javascript:void(0)'
1053
        );
1054
    }
1055
1056
    return $return_value;
1057
}
1058
1059
/**
1060
 * This function moves a forum or a forum category up or down.
1061
 *
1062
 * @param string $content   is it that we want to make (in)visible: forum category, forum, thread, post
1063
 * @param string $direction we want to move it up or down
1064
 * @param int    $id        id of the content we want to make invisible
1065
 *
1066
 * @return string language variable
1067
 *
1068
 * @todo consider removing the table_item_property calls here but this can
1069
 * prevent unwanted side effects when a forum does not have an entry in
1070
 * the item_property table but does have one in the forum table.
1071
 *
1072
 * @author Patrick Cool <[email protected]>, Ghent University
1073
 *
1074
 * @version february 2006, dokeos 1.8
1075
 */
1076
function moveUpDown(string $content, string $direction, int $id): string
1077
{
1078
    $table_categories = Database::get_course_table(TABLE_FORUM_CATEGORY);
1079
    $table_forums = Database::get_course_table(TABLE_FORUM);
1080
    $course_id = api_get_course_int_id();
1081
1082
    // Determine which field holds the sort order.
1083
    if ('forumcategory' === $content) {
1084
        $table = $table_categories;
1085
        $sort_column = 'cat_order';
1086
        $id_column = 'cat_id';
1087
        $sort_column = 'cat_order';
1088
    } elseif ('forum' === $content) {
1089
        $table = $table_forums;
1090
        $sort_column = 'forum_order';
1091
        $id_column = 'forum_id';
1092
        $sort_column = 'forum_order';
1093
        // We also need the forum_category of this forum.
1094
        $sql = "SELECT forum_category FROM $table_forums
1095
                WHERE forum_id = ".$id;
1096
        $result = Database::query($sql);
1097
        $row = Database::fetch_array($result);
1098
        $forum_category = $row['forum_category'];
1099
    } else {
1100
        return '';
1101
    }
1102
1103
    // Determine the need for sorting ascending or descending order.
1104
    if ('down' === $direction) {
1105
        $sort_direction = 'ASC';
1106
    } elseif ('up' === $direction) {
1107
        $sort_direction = 'DESC';
1108
    } else {
1109
        return '';
1110
    }
1111
1112
    // The SQL statement
1113
    if ('forumcategory' === $content) {
1114
        $sql = "SELECT *
1115
                FROM $table_categories forum_categories
1116
                WHERE
1117
                    forum_categories.c_id = $course_id
1118
                ORDER BY forum_categories.cat_order $sort_direction";
1119
    }
1120
    if ('forum' === $content) {
1121
        $sql = "SELECT *
1122
            FROM $table
1123
            WHERE
1124
                c_id = $course_id AND
1125
                forum_category = '".Database::escape_string($forum_category)."'
1126
            ORDER BY forum_order $sort_direction";
1127
    }
1128
    // Finding the items that need to be switched.
1129
    $result = Database::query($sql);
1130
    $found = false;
1131
    $next_sort = '';
1132
    $this_sort = '';
1133
    while ($row = Database::fetch_array($result, 'ASSOC')) {
1134
        if ($found) {
1135
            $next_id = $row[$id_column];
1136
            $next_sort = $row[$sort_column];
1137
            $found = false;
1138
        }
1139
        if ($id == $row[$id_column]) {
1140
            $this_id = $id;
1141
            $this_sort = $row[$sort_column];
1142
            $found = true;
1143
        }
1144
    }
1145
1146
    if ('forum' === $content && $next_sort) {
1147
        $repo = Container::getForumRepository();
1148
        /** @var CForum $forum */
1149
        $forum = $repo->find($id);
1150
        $forum->setForumOrder($next_sort);
1151
        $repo->update($forum);
1152
1153
        Display::addFlash(Display::return_message(get_lang('Updated')));
1154
    } else {
1155
        if ($next_sort) {
1156
            $repo = Container::getForumCategoryRepository();
1157
            /** @var CForumCategory $forum */
1158
            $category = $repo->find($id);
1159
            if ($category) {
1160
                $category->setCatOrder($next_sort);
1161
                $repo->update($category);
1162
1163
                Display::addFlash(Display::return_message(get_lang('Updated')));
1164
            }
1165
        }
1166
    }
1167
1168
    return '';
1169
}
1170
1171
/**
1172
 * Retrieve all the information off the forum categories (or one specific) for the current course.
1173
 * The categories are sorted according to their sorting order (cat_order.
1174
 *
1175
 * @param int $courseId  Optional. The course ID
1176
 * @param int $sessionId Optional. The session ID
1177
 *
1178
 * @return CForumCategory[]
1179
 */
1180
function get_forum_categories(int $courseId = 0, int $sessionId = 0): Array
1181
{
1182
    $repo = Container::getForumCategoryRepository();
1183
1184
    $course = api_get_course_entity($courseId);
1185
    $session = api_get_session_entity($sessionId);
1186
1187
    $qb = $repo->getResourcesByCourse($course, $session, null, $course->getResourceNode());
1188
1189
    return $qb->getQuery()->getResult();
1190
}
1191
1192
/**
1193
 * This function retrieves all the fora in a given forum category.
1194
 *
1195
 * @param int $categoryId the id of the forum category
1196
 * @param int $courseId   Optional. The course ID
1197
 *
1198
 * @return CForum[] containing all the information about the forums (regardless of their category)
1199
 *
1200
 * @author Patrick Cool <[email protected]>, Ghent University
1201
 *
1202
 * @version february 2006, dokeos 1.8
1203
 */
1204
function get_forums_in_category(int $categoryId, int $courseId = 0)
1205
{
1206
    $repo = Container::getForumRepository();
1207
    $course = api_get_course_entity($courseId);
1208
1209
    $qb = $repo->getResourcesByCourse($course, null);
1210
    $qb
1211
        ->andWhere('resource.forumCategory = :catId')
1212
        ->setParameter('catId', $categoryId)
1213
        ->orderBy('resource.forumOrder')
1214
    ;
1215
1216
    return $qb->getQuery()->getResult();
1217
}
1218
1219
/**
1220
 * Retrieve all the forums (regardless of their category) or of only one.
1221
 * The forums are sorted according to the forum_order.
1222
 * Since it does not take the forum category into account there probably
1223
 * will be two or more forums that have forum_order=1, ...
1224
 *
1225
 * @param bool $includeGroupsForum
1226
 * @param int  $sessionId
1227
 *
1228
 * @return CForum[]
1229
 */
1230
function get_forums(
1231
    int $courseId = null,
1232
    bool $includeGroupsForum = true,
1233
    int $sessionId = 0
1234
) {
1235
    $repo = Container::getForumRepository();
1236
    $courseId = empty($courseId) ? api_get_course_int_id() : $courseId;
1237
    $course = api_get_course_entity($courseId);
1238
    $session = api_get_session_entity($sessionId);
1239
1240
    $qb = $repo->getResourcesByCourse($course, $session);
1241
1242
    /*$qb->andWhere('resource.forumCategory = :catId')
1243
        ->setParameter('catId', $cat_id);
1244
    */
1245
    return $qb->getQuery()->getResult();
1246
}
1247
1248
/**
1249
 * Retrieve all the threads of a given forum.
1250
 *
1251
 * @param int|null $courseId  Optional If is null then it is considered the current course
1252
 * @param int|null $sessionId Optional. If is null then it is considered the current session
1253
 *
1254
 * @return CForumThread[]
1255
 */
1256
function get_threads(int $forumId, int $courseId = null, int $sessionId = null): Array
1257
{
1258
    $repo = Container::getForumThreadRepository();
1259
    $courseId = empty($courseId) ? api_get_course_int_id() : $courseId;
1260
    $course = api_get_course_entity($courseId);
1261
    $session = api_get_session_entity($sessionId);
1262
1263
    $qb = $repo->getResourcesByCourse($course, $session);
1264
    $qb->andWhere('resource.forum = :forum')->setParameter('forum', $forumId);
1265
1266
    return $qb->getQuery()->getResult();
1267
}
1268
1269
/**
1270
 * Get a thread by Id and course id.
1271
 *
1272
 * @param int $threadId the thread Id
1273
 *
1274
 * @return array containing all the information about the thread
1275
 */
1276
function getThreadInfo(int $threadId): Array
1277
{
1278
    $repo = Database::getManager()->getRepository(CForumThread::class);
1279
    /** @var CForumThread $forumThread */
1280
    $forumThread = $repo->findOneBy(['iid' => $threadId]);
1281
1282
    $thread = [];
1283
    if ($forumThread) {
1284
        $thread['iid'] = $forumThread->getIid();
1285
        $thread['threadId'] = $forumThread->getIid();
1286
        $thread['threadTitle'] = $forumThread->getThreadTitle();
1287
        $thread['forumId'] = $forumThread->getForum() ? $forumThread->getForum()->getIid() : 0;
1288
        //$thread['sessionId'] = $forumThread->getSessionId();
1289
        $thread['threadSticky'] = $forumThread->getThreadSticky();
1290
        $thread['locked'] = $forumThread->getLocked();
1291
        $thread['threadTitleQualify'] = $forumThread->getThreadTitleQualify();
1292
        $thread['threadQualifyMax'] = $forumThread->getThreadQualifyMax();
1293
        $thread['threadCloseDate'] = $forumThread->getThreadCloseDate();
1294
        $thread['threadWeight'] = $forumThread->getThreadWeight();
1295
        $thread['threadPeerQualify'] = $forumThread->isThreadPeerQualify();
1296
    }
1297
1298
    return $thread;
1299
}
1300
1301
/**
1302
 * Retrieve all posts of a given thread.
1303
 *
1304
 * @param int    $threadId       The thread ID
1305
 * @param string $orderDirection Optional. The direction for sort the posts
1306
 * @param bool   $recursive      Optional. If the list is recursive
1307
 * @param int    $postId         Optional. The post ID for recursive list
1308
 * @param int    $depth          Optional. The depth to indicate the indent
1309
 *
1310
 * @todo move to a repository
1311
 *
1312
 * @return array containing all the information about the posts of a given thread
1313
 */
1314
function getPosts(
1315
    CForum $forum,
1316
    int $threadId,
1317
    string $orderDirection = 'ASC',
1318
    bool $recursive = false,
1319
    int $postId = null,
1320
    int $depth = -1
1321
): Array
1322
{
1323
    $em = Database::getManager();
1324
1325
    if (api_is_allowed_to_edit(false, true)) {
1326
        $visibleCriteria = Criteria::expr()->neq('visible', 2);
1327
    } else {
1328
        $visibleCriteria = Criteria::expr()->eq('visible', 1);
1329
    }
1330
1331
    $criteria = Criteria::create();
1332
    $criteria
1333
        ->where(Criteria::expr()->eq('thread', $threadId))
1334
        //->andWhere(Criteria::expr()->eq('cId', $forum->getCId()))
1335
        ->andWhere($visibleCriteria)
1336
    ;
1337
1338
    $groupId = api_get_group_id();
1339
    $filterModerated = true;
1340
1341
    if (empty($groupId)) {
1342
        if (api_is_allowed_to_edit()) {
1343
            $filterModerated = false;
1344
        }
1345
    } else {
1346
        $groupEntity = api_get_group_entity($groupId);
1347
        if (GroupManager::isTutorOfGroup(api_get_user_id(), $groupEntity) ||
1348
            api_is_allowed_to_edit(false, true)
1349
        ) {
1350
            $filterModerated = false;
1351
        }
1352
    }
1353
1354
    if ($recursive) {
1355
        $criteria->andWhere(Criteria::expr()->eq('postParent', $postId));
1356
    }
1357
1358
    $qb = $em->getRepository(CForumPost::class)->createQueryBuilder('p');
1359
    $qb->select('p')
1360
        ->addCriteria($criteria)
1361
        ->addOrderBy('p.iid', $orderDirection);
1362
1363
    if ($filterModerated && 1 == $forum->isModerated()) {
1364
        if (!api_is_allowed_to_edit(false, true)) {
1365
            $userId = api_get_user_id();
1366
            $qb->andWhere(
1367
                'p.status = 1 OR
1368
                    (p.status = '.CForumPost::STATUS_WAITING_MODERATION." AND p.posterId = $userId) OR
1369
                    (p.status = ".CForumPost::STATUS_REJECTED." AND p.posterId = $userId) OR
1370
                    (p.status IS NULL AND p.posterId = $userId)
1371
                    "
1372
            );
1373
        }
1374
    }
1375
1376
    $posts = $qb->getQuery()->getResult();
1377
    $depth++;
1378
1379
    $list = [];
1380
    /** @var CForumPost $post */
1381
    foreach ($posts as $post) {
1382
        $postInfo = [
1383
            'iid' => $post->getIid(),
1384
            'post_id' => $post->getIid(),
1385
            'post_title' => $post->getPostTitle(),
1386
            'post_text' => $post->getPostText(),
1387
            'threadId' => $post->getThread() ? $post->getThread()->getIid() : 0,
1388
            'forum_id' => $post->getForum()->getIid(),
1389
            //'poster_id' => $post->getPosterId(),
1390
            //'poster_name' => $post->getPosterName(),
1391
            'post_date' => $post->getPostDate(),
1392
            'post_notification' => $post->getPostNotification(),
1393
            'post_parent_id' => $post->getPostParent() ? $post->getPostParent()->getIid() : 0,
1394
            'visible' => $post->getVisible(),
1395
            'status' => $post->getStatus(),
1396
            'indent_cnt' => $depth,
1397
            'entity' => $post,
1398
        ];
1399
1400
        $list[] = $postInfo;
1401
1402
        if (!$recursive) {
1403
            continue;
1404
        }
1405
        $list = array_merge(
1406
            $list,
1407
            getPosts(
1408
                $forum,
1409
                $threadId,
1410
                $orderDirection,
1411
                $recursive,
1412
                $post->getIid(),
1413
                $depth
1414
            )
1415
        );
1416
    }
1417
1418
    return $list;
1419
}
1420
1421
/**
1422
 * This function retrieves forum thread users details.
1423
 *
1424
 * @return Doctrine\DBAL\Driver\Statement|null array Array of type
1425
 *                                             ([user_id=>w,lastname=>x,firstname=>y,threadId=>z],[])
1426
 *
1427
 * @author Christian Fasanando <[email protected]>,
1428
 *
1429
 * @todo     this function needs to be improved
1430
 *
1431
 * @version October 2008, dokeos 1.8
1432
 */
1433
function get_thread_users_details(int $thread_id)
1434
{
1435
    $t_posts = Database::get_course_table(TABLE_FORUM_POST);
1436
    $t_users = Database::get_main_table(TABLE_MAIN_USER);
1437
    $t_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1438
    $t_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
1439
1440
    $course_id = api_get_course_int_id();
1441
1442
    $is_western_name_order = api_is_western_name_order();
1443
    if ($is_western_name_order) {
1444
        $orderby = 'ORDER BY user.firstname, user.lastname ';
1445
    } else {
1446
        $orderby = 'ORDER BY user.lastname, user.firstname';
1447
    }
1448
1449
    $session = api_get_session_entity();
1450
1451
    if ($session) {
1452
        $generalCoachesId = $session->getGeneralCoaches()->map(fn(User $coach) => $coach->getId())->getValues();
1453
        $sessionAdminsId = $session->getSessionAdmins()->map(fn(User $admin) => $admin->getId())->getValues();
1454
        $coachesId = array_merge($generalCoachesId, $sessionAdminsId);
1455
        $user_to_avoid = implode(', ', $coachesId);
1456
        //not showing coaches
1457
        $sql = "SELECT DISTINCT user.id, user.lastname, user.firstname, threadId
1458
                FROM $t_posts p, $t_users user, $t_session_rel_user session_rel_user_rel_course
1459
                WHERE
1460
                    p.poster_id = user.id AND
1461
                    user.id = session_rel_user_rel_course.user_id AND
1462
                    session_rel_user_rel_course.status = ".SessionEntity::STUDENT." AND
1463
                    session_rel_user_rel_course.user_id NOT IN ($user_to_avoid) AND
1464
                    p.threadId = ".(int) $thread_id.' AND
1465
                    session_id = '.api_get_session_id()." AND
1466
                    p.c_id = $course_id AND
1467
                    session_rel_user_rel_course.c_id = ".$course_id." $orderby ";
1468
    } else {
1469
        $sql = "SELECT DISTINCT user.id, user.lastname, user.firstname, threadId
1470
                FROM $t_posts p, $t_users user, $t_course_user course_user
1471
                WHERE
1472
                    p.poster_id = user.id
1473
                    AND user.id = course_user.user_id
1474
                    AND course_user.relation_type<>".COURSE_RELATION_TYPE_RRHH.'
1475
                    AND p.threadId = '.(int) $thread_id."
1476
                    AND course_user.status NOT IN('1') AND
1477
                    p.c_id = $course_id AND
1478
                    course_user.c_id = ".$course_id." $orderby";
1479
    }
1480
1481
    return Database::query($sql);
1482
}
1483
1484
/**
1485
 * This function retrieves forum thread users qualify.
1486
 *
1487
 * @return Doctrine\DBAL\Driver\Statement|null Array of type ([user_id=>w,lastname=>x,firstname=>y,threadId=>z],[])
1488
 * @author Jhon Hinojosa
1489
 * @todo     this function needs to be improved
1490
 */
1491
function get_thread_users_qualify(int $thread_id)
1492
{
1493
    $t_posts = Database::get_course_table(TABLE_FORUM_POST);
1494
    $t_qualify = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY);
1495
    $t_users = Database::get_main_table(TABLE_MAIN_USER);
1496
    $t_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1497
    $t_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
1498
1499
    $course_id = api_get_course_int_id();
1500
    $sessionId = api_get_session_id();
1501
1502
    $is_western_name_order = api_is_western_name_order();
1503
    if ($is_western_name_order) {
1504
        $orderby = 'ORDER BY user.firstname, user.lastname ';
1505
    } else {
1506
        $orderby = 'ORDER BY user.lastname, user.firstname';
1507
    }
1508
1509
    $session = api_get_session_entity();
1510
1511
    if ($session) {
1512
        $generalCoachesId = $session->getGeneralCoaches()->map(fn(User $coach) => $coach->getId())->getValues();
1513
        $sessionAdminsId = $session->getSessionAdmins()->map(fn(User $admin) => $admin->getId())->getValues();
1514
        $coachesId = array_merge($generalCoachesId, $sessionAdminsId);
1515
        $user_to_avoid = implode(', ', $coachesId);
1516
        //not showing coaches
1517
        $sql = "SELECT DISTINCT post.poster_id, user.lastname, user.firstname, post.threadId,user.id,qualify.qualify
1518
                FROM $t_posts post , $t_users user, $t_session_rel_user scu, $t_qualify qualify
1519
                WHERE poster_id = user.id
1520
                    AND post.poster_id = qualify.user_id
1521
                    AND user.id = scu.user_id
1522
                    AND scu.status = ".SessionEntity::STUDENT."
1523
                    AND scu.user_id NOT IN ($user_to_avoid)
1524
                    AND qualify.threadId = ".(int) $thread_id.'
1525
                    AND post.threadId = '.(int) $thread_id."
1526
                    AND scu.session_id = $sessionId
1527
                    AND scu.c_id = ".$course_id." AND
1528
                    qualify.c_id = $course_id AND
1529
                    post.c_id = $course_id
1530
                $orderby ";
1531
    } else {
1532
        $sql = "SELECT DISTINCT post.poster_id, user.lastname, user.firstname, post.threadId,user.id,qualify.qualify
1533
                FROM $t_posts post,
1534
                     $t_qualify qualify,
1535
                     $t_users user,
1536
                     $t_course_user course_user
1537
                WHERE
1538
                     post.poster_id = user.id
1539
                     AND post.poster_id = qualify.user_id
1540
                     AND user.id = course_user.user_id
1541
                     AND course_user.relation_type<>".COURSE_RELATION_TYPE_RRHH.'
1542
                     AND qualify.threadId = '.(int) $thread_id.'
1543
                     AND post.threadId = '.(int) $thread_id."
1544
                     AND course_user.status not in('1')
1545
                     AND course_user.c_id = $course_id
1546
                     AND qualify.c_id = $course_id
1547
                     AND post.c_id = $course_id
1548
                 $orderby ";
1549
    }
1550
1551
    return Database::query($sql);
1552
}
1553
1554
/**
1555
 * This function retrieves forum thread users not qualify.
1556
 *
1557
 * @param int $threadId Thread ID
1558
 * @param   string  Course DB name (optional)
1559
 *
1560
 * @return Doctrine\DBAL\Driver\Statement|null Array of type ([user_id=>w,lastname=>x,firstname=>y,threadId=>z],[])
1561
 *
1562
 * @author   Jhon Hinojosa<[email protected]>,
1563
 *
1564
 * @version oct 2008, dokeos 1.8
1565
 */
1566
function get_thread_users_not_qualify($thread_id)
1567
{
1568
    $t_posts = Database::get_course_table(TABLE_FORUM_POST);
1569
    $t_qualify = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY);
1570
    $t_users = Database::get_main_table(TABLE_MAIN_USER);
1571
    $t_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
1572
    $t_session_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
1573
1574
    $is_western_name_order = api_is_western_name_order();
1575
    if ($is_western_name_order) {
1576
        $orderby = 'ORDER BY user.firstname, user.lastname ';
1577
    } else {
1578
        $orderby = 'ORDER BY user.lastname, user.firstname';
1579
    }
1580
1581
    $course_id = api_get_course_int_id();
1582
1583
    $sql1 = "SELECT user_id FROM  $t_qualify
1584
             WHERE threadId = '".$thread_id."'";
1585
    $result1 = Database::query($sql1);
1586
    $cad = '';
1587
    while ($row = Database::fetch_array($result1)) {
1588
        $cad .= $row['user_id'].',';
1589
    }
1590
    if ('' == $cad) {
1591
        $cad = '0';
1592
    } else {
1593
        $cad = substr($cad, 0, strlen($cad) - 1);
1594
    }
1595
1596
    $session = api_get_session_entity();
1597
1598
    if ($session) {
1599
        $generalCoachesId = $session->getGeneralCoaches()->map(fn(User $coach) => $coach->getId())->getValues();
1600
        $sessionAdminsId = $session->getSessionAdmins()->map(fn(User $admin) => $admin->getId())->getValues();
1601
        $coachesId = array_merge($generalCoachesId, $sessionAdminsId);
1602
        $user_to_avoid = implode(', ', $coachesId);
1603
        //not showing coaches
1604
        $sql = "SELECT DISTINCT user.id, user.lastname, user.firstname, post.threadId
1605
                FROM $t_posts post , $t_users user, $t_session_rel_user session_rel_user_rel_course
1606
                WHERE poster_id = user.id
1607
                    AND user.id NOT IN (".$cad.")
1608
                    AND user.id = session_rel_user_rel_course.user_id
1609
                    AND session_rel_user_rel_course.status = ".SessionEntity::STUDENT."
1610
                    AND session_rel_user_rel_course.user_id NOT IN ($user_to_avoid)
1611
                    AND post.threadId = ".(int) $thread_id.'
1612
                    AND session_id = '.api_get_session_id()."
1613
                    AND session_rel_user_rel_course.c_id = $course_id AND post.c_id = $course_id $orderby ";
1614
    } else {
1615
        $sql = "SELECT DISTINCT user.id, user.lastname, user.firstname, post.threadId
1616
                FROM $t_posts post, $t_users user,$t_course_user course_user
1617
                WHERE post.poster_id = user.id
1618
                AND user.id NOT IN (".$cad.')
1619
                AND user.id = course_user.user_id
1620
                AND course_user.relation_type<>'.COURSE_RELATION_TYPE_RRHH.'
1621
                AND post.threadId = '.(int) $thread_id."
1622
                AND course_user.status not in('1')
1623
                AND course_user.c_id = $course_id AND post.c_id = $course_id  $orderby";
1624
    }
1625
1626
    return Database::query($sql);
1627
}
1628
1629
/**
1630
 * This function counts the number of forums inside a given category.
1631
 *
1632
 * @param int $cat_id the id of the forum category
1633
 *
1634
 * @todo an additional parameter that takes the visibility into account. For instance $countinvisible=0 would return
1635
 *       the number of visible forums, $countinvisible=1 would return the number of visible and invisible forums
1636
 *
1637
 * @return int the number of forums inside the given category
1638
 *
1639
 * @author Patrick Cool <[email protected]>, Ghent University
1640
 *
1641
 * @version february 2006, dokeos 1.8
1642
 */
1643
function count_number_of_forums_in_category($cat_id)
1644
{
1645
    $table_forums = Database::get_course_table(TABLE_FORUM);
1646
    $course_id = api_get_course_int_id();
1647
    $cat_id = (int) $cat_id;
1648
    $sql = "SELECT count(*) AS number_of_forums
1649
            FROM $table_forums
1650
            WHERE forum_category = $cat_id";
1651
    $result = Database::query($sql);
1652
    $row = Database::fetch_array($result);
1653
1654
    return $row['number_of_forums'];
1655
}
1656
1657
/**
1658
 * This function update a thread.
1659
 *
1660
 * @param array $values - The form Values
1661
 */
1662
function updateThread($values)
1663
{
1664
    if (!api_is_allowed_to_edit()) {
1665
        return '';
1666
    }
1667
1668
    $logInfo = [
1669
        'tool' => TOOL_FORUM,
1670
        'tool_id' => $values['forum_id'],
1671
        'tool_id_detail' => $values['threadId'],
1672
        'action' => 'edit-thread',
1673
        'action_details' => 'thread',
1674
        'info' => $values['thread_title'],
1675
    ];
1676
    Event::registerLog($logInfo);
1677
1678
    $threadTable = Database::get_course_table(TABLE_FORUM_THREAD);
1679
    $courseId = api_get_course_int_id();
1680
    $courseCode = api_get_course_id();
1681
    $sessionId = api_get_session_id();
1682
1683
    // Simple update + set gradebook values to null
1684
    $params = [
1685
        'thread_title' => $values['thread_title'],
1686
        'thread_sticky' => $values['thread_sticky'] ?? 0,
1687
    ];
1688
    $where = ['iid = ?' => [$values['threadId']]];
1689
    Database::update($threadTable, $params, $where);
1690
1691
    $id = $values['threadId'];
1692
    $linkInfo = GradebookUtils::isResourceInCourseGradebook(
1693
        $courseCode,
1694
        LINK_FORUM_THREAD,
1695
        $id,
1696
        $sessionId
1697
    );
1698
1699
    $gradebookLink = null;
1700
    $em = Database::getManager();
1701
    if (!empty($linkInfo) && isset($linkInfo['id'])) {
1702
        $gradebookLink = $em->getRepository(GradebookLink::class)->find($linkInfo['id']);
1703
    }
1704
1705
    // values 1 or 0
1706
    $check = isset($values['thread_qualify_gradebook']) ? $values['thread_qualify_gradebook'] : false;
1707
    if ($check) {
1708
        $title = Security::remove_XSS(stripslashes($values['calification_notebook_title']));
1709
        $value = isset($values['numeric_calification']) ? (int) ($values['numeric_calification']) : 0;
1710
        $weight = isset($values['weight_calification']) ? (float) ($values['weight_calification']) : 0;
1711
        $description = '';
1712
        // Update title
1713
        $params = [
1714
            'thread_title_qualify' => $values['calification_notebook_title'],
1715
            'thread_qualify_max' => api_float_val($values['numeric_calification']),
1716
            'thread_weight' => api_float_val($values['weight_calification']),
1717
            'thread_peer_qualify' => $values['thread_peer_qualify'],
1718
        ];
1719
        $where = ['iid = ?' => [$values['threadId']]];
1720
        Database::update($threadTable, $params, $where);
1721
1722
        if (!$linkInfo) {
1723
            GradebookUtils::add_resource_to_course_gradebook(
1724
                $values['category_id'],
1725
                $courseCode,
1726
                LINK_FORUM_THREAD,
1727
                $id,
1728
                $title,
1729
                $weight,
1730
                $value,
1731
                $description,
1732
                1,
1733
                $sessionId
1734
            );
1735
        } else {
1736
            if ($gradebookLink) {
1737
                $gradebookLink->setWeight($weight);
1738
                $em->persist($gradebookLink);
1739
                $em->flush();
1740
            }
1741
        }
1742
    } else {
1743
        $params = [
1744
            'thread_title_qualify' => '',
1745
            'thread_qualify_max' => 0,
1746
            'thread_weight' => 0,
1747
            'thread_peer_qualify' => 0,
1748
        ];
1749
        $where = ['iid = ?' => [$values['threadId']]];
1750
        Database::update($threadTable, $params, $where);
1751
1752
        if (!empty($linkInfo)) {
1753
            if ($gradebookLink) {
1754
                $em->remove($gradebookLink);
1755
                $em->flush();
1756
            }
1757
        }
1758
    }
1759
1760
    $message = get_lang('The post has been modified').'<br />';
1761
    Display::addFlash(Display::return_message($message, 'confirmation', false));
1762
}
1763
1764
function saveThread(
1765
    CForum $forum,
1766
    array $values,
1767
    array $courseInfo = [],
1768
    $showMessage = true,
1769
    $userId = 0,
1770
    $sessionId = 0
1771
): ?CForumThread {
1772
    $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo;
1773
    $userId = $userId ?: api_get_user_id();
1774
    $course_id = $courseInfo['real_id'];
1775
    $courseCode = $courseInfo['code'];
1776
    $sessionId = $sessionId ?: api_get_session_id();
1777
    $table_threads = Database::get_course_table(TABLE_FORUM_THREAD);
1778
1779
    $post_date = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
1780
    $visible = true;
1781
    if ('1' == $forum->getApprovalDirectPost() && !api_is_allowed_to_edit(null, true)) {
1782
        $visible = false; // The post has not been approved yet.
1783
    }
1784
    $clean_post_title = $values['post_title'];
1785
1786
    $user = api_get_user_entity(api_get_user_id());
1787
    $course = api_get_course_entity($course_id);
1788
    $session = api_get_session_entity($sessionId);
1789
1790
    // We first store an entry in the forum_thread table because the threadId is used in the forum_post table.
1791
    $thread = new CForumThread();
1792
    $thread
1793
        ->setThreadTitle($clean_post_title)
1794
        ->setForum($forum)
1795
        ->setUser($user)
1796
        ->setThreadDate($post_date)
1797
        ->setThreadSticky((bool) ($values['thread_sticky'] ?? false))
1798
        ->setThreadTitleQualify($values['calification_notebook_title'] ?? '')
1799
        ->setThreadQualifyMax(api_float_val($values['numeric_calification'] ?? 0))
1800
        ->setThreadWeight(api_float_val($values['weight_calification'] ?? 0))
1801
        ->setThreadPeerQualify(isset($values['thread_peer_qualify']) ? (bool) $values['thread_peer_qualify'] : false)
1802
        ->setParent($forum)
1803
        ->addCourseLink($course, $session)
1804
    ;
1805
    $em = Database::getManager();
1806
    $itemId = isset($values['lp_item_id']) ? (int) $values['lp_item_id'] : 0;
1807
    if (!empty($itemId)) {
1808
        $item = $em->getRepository(CLpItem::class)->find($itemId);
1809
        $thread->setItem($item);
1810
    }
1811
1812
    $repo = Container::getForumThreadRepository();
1813
    $repo->create($thread);
1814
1815
    if (!$thread->getIid()) {
1816
        return null;
1817
    }
1818
1819
    // Add option gradebook qualify.
1820
    if (isset($values['thread_qualify_gradebook']) &&
1821
        1 == $values['thread_qualify_gradebook']
1822
    ) {
1823
        // Add function gradebook.
1824
        $resourcename = stripslashes($values['calification_notebook_title']);
1825
        GradebookUtils::add_resource_to_course_gradebook(
1826
            $values['category_id'],
1827
            $courseCode,
1828
            5,
1829
            $thread->getIid(),
1830
            $resourcename,
1831
            $values['weight_calification'],
1832
            $values['numeric_calification'],
1833
            '',
1834
            0,
1835
            $sessionId
1836
        );
1837
    }
1838
1839
    $logInfo = [
1840
        'tool' => TOOL_FORUM,
1841
        'tool_id' => $values['forum_id'],
1842
        'tool_id_detail' => $thread->getIid(),
1843
        'action' => 'new-thread',
1844
        'info' => $clean_post_title,
1845
    ];
1846
    Event::registerLog($logInfo);
1847
1848
    // We now store the content in the table_post table.
1849
    $post = new CForumPost();
1850
    $post
1851
        ->setPostTitle($clean_post_title)
1852
        ->setPostText($values['post_text'])
1853
        ->setThread($thread)
1854
        ->setForum($forum)
1855
        ->setUser(api_get_user_entity($userId))
1856
        ->setPostDate($post_date)
1857
        ->setPostNotification(isset($values['post_notification']) ? (bool) $values['post_notification'] : false)
1858
        ->setVisible($visible)
1859
        ->setStatus(CForumPost::STATUS_VALIDATED)
1860
        ->setParent($thread)
1861
        ->addCourseLink($course, $session)
1862
    ;
1863
1864
    if ($forum->isModerated()) {
1865
        $post->setStatus(
1866
            api_is_course_admin() ? CForumPost::STATUS_VALIDATED : CForumPost::STATUS_WAITING_MODERATION
1867
        );
1868
    }
1869
1870
    $repo = Container::getForumPostRepository();
1871
    $repo->create($post);
1872
    $thread->setThreadLastPost($post);
1873
    $em = Database::getManager();
1874
    $em->persist($thread);
1875
    $em->flush();
1876
1877
    $postId = $post->getIid();
1878
1879
    $logInfo = [
1880
        'tool' => TOOL_FORUM,
1881
        'tool_id' => $values['forum_id'],
1882
        'tool_id_detail' => $thread->getIid(),
1883
        'action' => 'new-post',
1884
        'info' => $clean_post_title,
1885
    ];
1886
    Event::registerLog($logInfo);
1887
1888
    // Now we have to update the thread table to fill the thread_last_post
1889
    // field (so that we know when the thread has been updated for the last time).
1890
    $sql = "UPDATE $table_threads
1891
            SET thread_last_post = '".$postId."'
1892
            WHERE
1893
                iid = '".$thread->getIid()."'";
1894
    Database::query($sql);
1895
1896
    $message = '';
1897
    if ($showMessage) {
1898
        Display::addFlash(Display::return_message(get_lang('The new thread has been added'), 'success', false));
1899
    }
1900
1901
    // Overwrite default message.
1902
    if ($forum->isModerated() &&
1903
        !api_is_allowed_to_edit(null, true)
1904
    ) {
1905
        if ($showMessage) {
1906
            Display::addFlash(Display::return_message(get_lang('Your message has to be approved before people can view it.'), 'success', false));
1907
        }
1908
    }
1909
1910
    add_forum_attachment_file(
1911
        null,
1912
        $post
1913
    );
1914
1915
    if ('1' == $forum->getApprovalDirectPost() &&
1916
        !api_is_allowed_to_edit(null, true)
1917
    ) {
1918
        $message .= get_lang('Your message has to be approved before people can view it.').'<br />';
1919
        $message .= get_lang('You can now return to the').
1920
            ' <a href="viewforum.php?'.api_get_cidreq().'&forum='.$values['forum_id'].'">'.
1921
            get_lang('Forum').'</a><br />';
1922
    } else {
1923
        $message .= get_lang('You can now return to the').
1924
            ' <a href="viewforum.php?'.api_get_cidreq().'&forum='.$values['forum_id'].'">'.
1925
            get_lang('Forum').'</a><br />';
1926
        $message .= get_lang('You can now return to the').
1927
            ' <a href="viewthread.php?'.api_get_cidreq().'&forum='.$values['forum_id'].'&thread='.$thread->getIid().'">'.
1928
            get_lang('Message').'</a>';
1929
    }
1930
    $reply_info['new_post_id'] = $postId;
1931
    $my_post_notification = isset($values['post_notification']) ? $values['post_notification'] : null;
1932
1933
    if (1 == $my_post_notification) {
1934
        set_notification('thread', $thread->getIid(), true);
1935
    }
1936
1937
    send_notification_mails(
1938
        $forum,
1939
        $thread,
1940
        $reply_info
1941
    );
1942
1943
    Session::erase('formelements');
1944
    Session::erase('origin');
1945
    Session::erase('breadcrumbs');
1946
    Session::erase('addedresource');
1947
    Session::erase('addedresourceid');
1948
1949
    if ($showMessage) {
1950
        Display::addFlash(Display::return_message($message, 'success', false));
1951
    }
1952
1953
    return $thread;
1954
}
1955
1956
/**
1957
 * This function displays the form that is used to add a post. This can be a new thread or a reply.
1958
 *
1959
 * @param string $action
1960
 *                            is the parameter that determines if we are
1961
 *                            2. replythread: Replying to a thread ($action = replythread) => I-frame with the complete
1962
 *                            thread (if enabled)
1963
 *                            3. replymessage: Replying to a message ($action =replymessage) => I-frame with the
1964
 *                            complete thread (if enabled)
1965
 *                            (I first thought to put and I-frame with the message only)
1966
 *                            4. quote: Quoting a message ($action= quotemessage) => I-frame with the complete thread
1967
 *                            (if enabled). The message will be in the reply. (I first thought not to put an I-frame
1968
 *                            here)
1969
 * @param array  $form_values
1970
 * @param bool   $showPreview
1971
 *
1972
 * @return FormValidator
1973
 */
1974
function show_add_post_form(CForum $forum, CForumThread $thread, CForumPost $post = null, $action, $form_values, $showPreview = true)
1975
{
1976
    $_user = api_get_user_info();
1977
    $action = isset($action) ? Security::remove_XSS($action) : '';
1978
    $threadId = $thread->getIid();
1979
    $forumId = $forum->getIid();
1980
    $giveRevision = isset($_GET['give_revision']) && 1 == $_GET['give_revision'];
1981
    $postId = $post ? $post->getIid() : 0;
1982
1983
    $url = api_get_self().'?'.http_build_query(
1984
        [
1985
            'action' => $action,
1986
            'forum' => $forumId,
1987
            'thread' => $threadId,
1988
            'post' => $postId,
1989
        ]
1990
    ).'&'.api_get_cidreq();
1991
1992
    $form = new FormValidator(
1993
        'thread',
1994
        'post',
1995
        $url
1996
    );
1997
    $form->setConstants(['forum' => '5']);
1998
1999
    // Setting the form elements.
2000
    $form->addElement('hidden', 'forum_id', $forumId);
2001
    $form->addElement('hidden', 'threadId', $threadId);
2002
    $form->addElement('hidden', 'action', $action);
2003
2004
    // If anonymous posts are allowed we also display a form to allow the user to put his name or username in.
2005
    if (1 == $forum->getAllowAnonymous() && !isset($_user['user_id'])) {
2006
        $form->addElement('text', 'poster_name', get_lang('Name'));
2007
        $form->applyFilter('poster_name', 'html_filter');
2008
    }
2009
2010
    $form->addElement('text', 'post_title', get_lang('Title'));
2011
    $form->addHtmlEditor(
2012
        'post_text',
2013
        get_lang('Text'),
2014
        true,
2015
        false,
2016
        api_is_allowed_to_edit(null, true) ? [
2017
            'ToolbarSet' => 'Forum',
2018
            'Width' => '100%',
2019
            'Height' => '300',
2020
        ] : [
2021
            'ToolbarSet' => 'ForumStudent',
2022
            'Width' => '100%',
2023
            'Height' => '300',
2024
            'UserStatus' => 'student',
2025
        ]
2026
    );
2027
    $form->addRule('post_text', get_lang('Required field'), 'required');
2028
2029
    if (in_array($action, ['replythread', 'replymessage', 'quote'])) {
2030
        $extraFields = new ExtraField('forum_post');
2031
        $extraFields->addElements(
2032
            $form,
2033
            null,
2034
            [], //exclude
2035
            false, // filter
2036
            false, // tag as select
2037
            ['ask_for_revision'], //show only fields
2038
            [], // order fields
2039
            [] // extra data);
2040
        );
2041
    }
2042
2043
    $iframe = null;
2044
    if ($showPreview) {
2045
        if ('newthread' !== $action && !empty($threadId)) {
2046
            $iframe = '<iframe style="border: 1px solid black"
2047
            src="iframe_thread.php?'.api_get_cidreq().'&forum='.$forumId.'&thread='.$threadId.'#'.$postId.'" width="100%"></iframe>';
2048
        }
2049
        if (!empty($iframe)) {
2050
            $form->addElement('label', get_lang('Thread'), $iframe);
2051
        }
2052
    }
2053
2054
    if (in_array($action, ['quote', 'replymessage'])) {
2055
        $form->addFile('user_upload[]', get_lang('Attachment'));
2056
        $form->addButton(
2057
            'add_attachment',
2058
            get_lang('Add attachment'),
2059
            'paperclip',
2060
            'default',
2061
            'default',
2062
            null,
2063
            ['id' => 'reply-add-attachment']
2064
        );
2065
    } else {
2066
        $form->addFile('user_upload', get_lang('Attachment'));
2067
    }
2068
2069
    if ($giveRevision) {
2070
        $hide = api_get_configuration_value('hide_forum_post_revision_language');
2071
        $form->addHidden('give_revision', 1);
2072
        if (false === $hide) {
2073
            $extraField = new ExtraField('forum_post');
2074
            $extraField->addElements(
2075
                $form,
2076
                null,
2077
                [], //exclude
2078
                false, // filter
2079
                false, // tag as select
2080
                ['revision_language'], //show only fields
2081
                [], // order fields
2082
                [] // extra data
2083
            );
2084
        } else {
2085
            $form->addHidden('extra_revision_language', 1);
2086
        }
2087
    }
2088
2089
    // Setting the class and text of the form title and submit button.
2090
    if ('quote' === $action) {
2091
        $form->addButtonCreate(get_lang('Quote this message'), 'SubmitPost');
2092
    } elseif ('replythread' === $action) {
2093
        $form->addButtonCreate(get_lang('Reply to this thread'), 'SubmitPost');
2094
    } elseif ('replymessage' === $action) {
2095
        $form->addButtonCreate(get_lang('Reply to this message'), 'SubmitPost');
2096
    }
2097
2098
    $defaults['thread_peer_qualify'] = 0;
2099
    if (!empty($form_values)) {
2100
        $defaults['post_title'] = prepare4display($form_values['post_title']);
2101
        $defaults['post_text'] = prepare4display($form_values['post_text']);
2102
        $defaults['post_notification'] = (int) $form_values['post_notification'];
2103
        $defaults['thread_sticky'] = (int) $form_values['thread_sticky'];
2104
        $defaults['thread_peer_qualify'] = (int) $form_values['thread_peer_qualify'];
2105
    }
2106
2107
    // If we are quoting a message we have to retrieve the information of the post we are quoting so that
2108
    // we can add this as default to the textarea.
2109
    // We also need to put the parent_id of the post in a hidden form when
2110
    if (('quote' === $action || 'replymessage' === $action || $giveRevision) && !empty($postId)) {
2111
        // we are quoting or replying to a message (<> reply to a thread !!!)
2112
        $form->addHidden('post_parent_id', $post->getIid());
2113
        // If we are replying or are quoting then we display a default title.
2114
        $posterName = UserManager::formatUserFullName($post->getUser());
2115
        $defaults['post_title'] = get_lang('Re:').api_html_entity_decode($post->getPostTitle(), ENT_QUOTES);
2116
        // When we are quoting a message then we have to put that message into the wysiwyg editor.
2117
        // Note: The style has to be hardcoded here because using class="quote" didn't work.
2118
        if ('quote' === $action) {
2119
            $defaults['post_text'] = '<div>&nbsp;</div>
2120
                <div style="margin: 5px;">
2121
                    <div style="font-size: 90%; font-style: italic;">'.
2122
                get_lang('Quoting').' '.$posterName.':</div>
2123
                        <div style="color: #006600; font-size: 90%;  font-style: italic; background-color: #FAFAFA; border: #D1D7DC 1px solid; padding: 3px;">'.
2124
                prepare4display($post->getPostText()).'
2125
                        </div>
2126
                    </div>
2127
                <div>&nbsp;</div>
2128
                <div>&nbsp;</div>
2129
            ';
2130
        }
2131
        if ($giveRevision) {
2132
            $defaults['post_text'] = prepare4display($post->getPostText());
2133
        }
2134
    }
2135
2136
    $form->setDefaults(isset($defaults) ? $defaults : []);
2137
2138
    // The course admin can make a thread sticky (=appears with special icon and always on top).
2139
    $form->addRule('post_title', get_lang('Required field'), 'required');
2140
    if (1 == $forum->getAllowAnonymous() && !isset($_user['user_id'])) {
2141
        $form->addRule(
2142
            'poster_name',
2143
            get_lang('Required field'),
2144
            'required'
2145
        );
2146
    }
2147
2148
    // Validation or display
2149
    if ($form->validate()) {
2150
        $check = Security::check_token('post');
2151
        if ($check) {
2152
            $values = $form->getSubmitValues();
2153
            if (isset($values['thread_qualify_gradebook']) &&
2154
                '1' == $values['thread_qualify_gradebook'] &&
2155
                empty($values['weight_calification'])
2156
            ) {
2157
                Display::addFlash(
2158
                    Display::return_message(
2159
                        get_lang('You must assign a score to this activity').'&nbsp;<a href="javascript:window.history.go(-1);">'.get_lang('Back').'</a>',
2160
                        'error',
2161
                        false
2162
                    )
2163
                );
2164
2165
                return false;
2166
            }
2167
2168
            $postId = 0;
2169
            $threadId = 0;
2170
2171
            switch ($action) {
2172
                case 'quote':
2173
                case 'replythread':
2174
                case 'replymessage':
2175
                    $postId = store_reply($forum, $thread, $values);
2176
2177
                    break;
2178
            }
2179
2180
            if ($postId) {
2181
                if (isset($values['give_revision']) && 1 == $values['give_revision']) {
2182
                    $extraFieldValues = new ExtraFieldValue('forum_post');
2183
                    $revisionLanguage = isset($values['extra_revision_language']) ? $values['extra_revision_language'] : '';
2184
                    $params = [
2185
                        'item_id' => $postId,
2186
                        'extra_revision_language' => $revisionLanguage,
2187
                    ];
2188
2189
                    $extraFieldValues->saveFieldValues(
2190
                        $params,
2191
                        false,
2192
                        false,
2193
                        ['revision_language']
2194
                    );
2195
                }
2196
2197
                if (in_array($action, ['replythread', 'replymessage', 'quote'])) {
2198
                    $extraFieldValues = new ExtraFieldValue('forum_post');
2199
                    $params = [
2200
                        'item_id' => $postId,
2201
                        'extra_ask_for_revision' => isset($values['extra_ask_for_revision']) ? $values['extra_ask_for_revision'] : '',
2202
                    ];
2203
                    $extraFieldValues->saveFieldValues(
2204
                        $params,
2205
                        false,
2206
                        false,
2207
                        ['ask_for_revision']
2208
                    );
2209
                }
2210
            }
2211
2212
            $url = api_get_path(WEB_CODE_PATH).'forum/viewthread.php?'.api_get_cidreq().'&'.http_build_query(
2213
                [
2214
                    'forum' => $forumId,
2215
                    'thread' => $thread->getIid(),
2216
                ]
2217
            );
2218
2219
            Security::clear_token();
2220
            header('Location: '.$url);
2221
            exit;
2222
        }
2223
    } else {
2224
        $token = Security::get_token();
2225
        $form->addElement('hidden', 'sec_token');
2226
        $form->setConstants(['sec_token' => $token]);
2227
2228
        // Delete from $_SESSION forum attachment from other posts
2229
        // and keep only attachments for new post
2230
        //clearAttachedFiles(FORUM_NEW_POST);
2231
        // Get forum attachment ajax table to add it to form
2232
        $attachmentAjaxTable = getAttachmentsAjaxTable(0, $forum->getIid());
2233
        $ajaxHtml = $attachmentAjaxTable;
2234
        $form->addElement('html', $ajaxHtml);
2235
2236
        return $form;
2237
    }
2238
}
2239
2240
function newThread(CForum $forum, $form_values = '', $showPreview = true)
2241
{
2242
    $_user = api_get_user_info();
2243
    $forumId = $forum->getIid();
2244
    $my_post = isset($_GET['post']) ? (int) $_GET['post'] : '';
2245
    $giveRevision = isset($_GET['give_revision']) && 1 == $_GET['give_revision'];
2246
    $action = 'new_thread';
2247
2248
    $url = api_get_self().'?'.http_build_query(
2249
            [
2250
                'action' => $action,
2251
                'forum' => $forumId,
2252
                'post' => $my_post,
2253
            ]
2254
        ).'&'.api_get_cidreq();
2255
2256
    $form = new FormValidator(
2257
        'thread',
2258
        'post',
2259
        $url
2260
    );
2261
2262
    // Setting the form elements.
2263
    $form->addElement('hidden', 'forum_id', $forumId);
2264
    $form->addElement('hidden', 'threadId', 0);
2265
    $form->addElement('hidden', 'action', $action);
2266
2267
    // If anonymous posts are allowed we also display a form to allow the user to put his name or username in.
2268
    if (1 == $forum->getAllowAnonymous() && !isset($_user['user_id'])) {
2269
        $form->addElement('text', 'poster_name', get_lang('Name'));
2270
        $form->applyFilter('poster_name', 'html_filter');
2271
    }
2272
2273
    $form->addElement('text', 'post_title', get_lang('Title'));
2274
    $form->addHtmlEditor(
2275
        'post_text',
2276
        get_lang('Text'),
2277
        true,
2278
        false,
2279
        api_is_allowed_to_edit(null, true) ? [
2280
            'ToolbarSet' => 'Forum',
2281
            'Width' => '100%',
2282
            'Height' => '300',
2283
        ] : [
2284
            'ToolbarSet' => 'ForumStudent',
2285
            'Width' => '100%',
2286
            'Height' => '300',
2287
            'UserStatus' => 'student',
2288
        ]
2289
    );
2290
    $form->addRule('post_text', get_lang('Required field'), 'required');
2291
2292
    $extraFields = new ExtraField('forum_post');
2293
    $extraFields->addElements(
2294
        $form,
2295
        null,
2296
        [], //exclude
2297
        false, // filter
2298
        false, // tag as select
2299
        ['ask_for_revision'], //show only fields
2300
        [], // order fields
2301
        [] // extra data);
2302
    );
2303
2304
    if (Gradebook::is_active() &&
2305
        (api_is_course_admin() || api_is_session_general_coach() || api_is_course_tutor())
2306
    ) {
2307
        $form->addElement('advanced_settings', 'advanced_params', get_lang('Advanced settings'));
2308
        $form->addElement('html', '<div id="advanced_params_options" style="display:none">');
2309
2310
        // Thread qualify
2311
        if (Gradebook::is_active()) {
2312
            //Loading gradebook select
2313
            GradebookUtils::load_gradebook_select_in_tool($form);
2314
            $form->addCheckBox(
2315
                'thread_qualify_gradebook',
2316
                '',
2317
                get_lang('Grade this thread'),
2318
                ['onclick' => 'javascript:if(this.checked==true){document.getElementById(\'options_field\').style.display = \'block\';}else{document.getElementById(\'options_field\').style.display = \'none\';}']
2319
            );
2320
        } else {
2321
            $form->addElement('hidden', 'thread_qualify_gradebook', false);
2322
        }
2323
2324
        $form->addElement('html', '<div id="options_field" style="display:none">');
2325
        $form->addElement('text', 'numeric_calification', get_lang('Maximum score'));
2326
        $form->applyFilter('numeric_calification', 'html_filter');
2327
        $form->addElement('text', 'calification_notebook_title', get_lang('Column header in Competences Report'));
2328
        $form->applyFilter('calification_notebook_title', 'html_filter');
2329
2330
        $form->addElement(
2331
            'text',
2332
            'weight_calification',
2333
            get_lang('Weight in Report'),
2334
            ['value' => '0.00', 'onfocus' => 'javascript: this.select();']
2335
        );
2336
        $form->applyFilter('weight_calification', 'html_filter');
2337
2338
        $group = [];
2339
        $group[] = $form->createElement('radio', 'thread_peer_qualify', null, get_lang('Yes'), 1);
2340
        $group[] = $form->createElement('radio', 'thread_peer_qualify', null, get_lang('No'), 0);
2341
        $form->addGroup(
2342
            $group,
2343
            '',
2344
            [
2345
                get_lang('Thread scored by peers'),
2346
                get_lang('Thread scored by peersComment'),
2347
            ]
2348
        );
2349
        $form->addElement('html', '</div>');
2350
        $form->addElement('html', '</div>');
2351
    }
2352
2353
    SkillModel::addSkillsToForm($form, ITEM_TYPE_FORUM_THREAD, 0);
2354
    $form->addElement('checkbox', 'thread_sticky', '', get_lang('This is a sticky message (appears always on top and has a special sticky icon)'));
2355
2356
    $form->addFile('user_upload', get_lang('Attachment'));
2357
2358
    if ($giveRevision) {
2359
        $hide = api_get_configuration_value('hide_forum_post_revision_language');
2360
        $form->addHidden('give_revision', 1);
2361
        if (false === $hide) {
2362
            $extraField = new ExtraField('forum_post');
2363
            $extraField->addElements(
2364
                $form,
2365
                null,
2366
                [], //exclude
2367
                false, // filter
2368
                false, // tag as select
2369
                ['revision_language'], //show only fields
2370
                [], // order fields
2371
                [] // extra data
2372
            );
2373
        } else {
2374
            $form->addHidden('extra_revision_language', 1);
2375
        }
2376
    }
2377
    $form->addButtonCreate(get_lang('Create thread'), 'SubmitPost');
2378
2379
    $defaults['thread_peer_qualify'] = 0;
2380
    if (!empty($form_values)) {
2381
        $defaults['post_title'] = prepare4display($form_values['post_title']);
2382
        $defaults['post_text'] = prepare4display($form_values['post_text']);
2383
        $defaults['post_notification'] = (int) $form_values['post_notification'];
2384
        $defaults['thread_sticky'] = (int) $form_values['thread_sticky'];
2385
        $defaults['thread_peer_qualify'] = (int) $form_values['thread_peer_qualify'];
2386
    }
2387
2388
    $form->setDefaults(isset($defaults) ? $defaults : []);
2389
2390
    // The course admin can make a thread sticky (=appears with special icon and always on top).
2391
    $form->addRule('post_title', get_lang('Required field'), 'required');
2392
    if (1 == $forum->getAllowAnonymous() && !isset($_user['user_id'])) {
2393
        $form->addRule(
2394
            'poster_name',
2395
            get_lang('Required field'),
2396
            'required'
2397
        );
2398
    }
2399
2400
    // Validation or display
2401
    if ($form->validate()) {
2402
        $check = Security::check_token('post');
2403
        if ($check) {
2404
            $values = $form->getSubmitValues();
2405
            if (isset($values['thread_qualify_gradebook']) &&
2406
                '1' == $values['thread_qualify_gradebook'] &&
2407
                empty($values['weight_calification'])
2408
            ) {
2409
                Display::addFlash(
2410
                    Display::return_message(
2411
                        get_lang('You must assign a score to this activity').'&nbsp;<a href="javascript:window.history.go(-1);">'.get_lang('Back').'</a>',
2412
                        'error',
2413
                        false
2414
                    )
2415
                );
2416
2417
                return false;
2418
            }
2419
2420
            $newThread = saveThread($forum, $values);
2421
            if ($newThread) {
2422
                SkillModel::saveSkills($form, ITEM_TYPE_FORUM_THREAD, $newThread->getIid());
2423
                $post = $newThread->getThreadLastPost();
2424
2425
                if ($post) {
2426
                    $postId = $post->getIid();
2427
2428
                    if (isset($values['give_revision']) && 1 == $values['give_revision']) {
2429
                        $extraFieldValues = new ExtraFieldValue('forum_post');
2430
                        $revisionLanguage = isset($values['extra_revision_language']) ? $values['extra_revision_language'] : '';
2431
2432
                        $params = [
2433
                            'item_id' => $postId,
2434
                            'extra_revision_language' => $revisionLanguage,
2435
                        ];
2436
2437
                        $extraFieldValues->saveFieldValues(
2438
                            $params,
2439
                            false,
2440
                            false,
2441
                            ['revision_language']
2442
                        );
2443
                    }
2444
                    $extraFieldValues = new ExtraFieldValue('forum_post');
2445
                    $params = [
2446
                        'item_id' => $postId,
2447
                        'extra_ask_for_revision' => $values['extra_ask_for_revision'] ?? '',
2448
                    ];
2449
                    $extraFieldValues->saveFieldValues(
2450
                        $params,
2451
                        false,
2452
                        false,
2453
                        ['ask_for_revision']
2454
                    );
2455
                }
2456
            }
2457
2458
            $url = api_get_path(WEB_CODE_PATH).'forum/viewthread.php?'.api_get_cidreq().'&'.http_build_query(
2459
                [
2460
                    'forum' => $forumId,
2461
                    'thread' => $newThread->getIid(),
2462
                ]
2463
            );
2464
2465
            Security::clear_token();
2466
            header('Location: '.$url);
2467
            exit;
2468
        }
2469
    } else {
2470
        $token = Security::get_token();
2471
        $form->addElement('hidden', 'sec_token');
2472
        $form->setConstants(['sec_token' => $token]);
2473
2474
        // Delete from $_SESSION forum attachment from other posts
2475
        // and keep only attachments for new post
2476
        //clearAttachedFiles(FORUM_NEW_POST);
2477
        // Get forum attachment ajax table to add it to form
2478
        $attachmentAjaxTable = getAttachmentsAjaxTable(0, $forum->getIid());
2479
        $ajaxHtml = $attachmentAjaxTable;
2480
        $form->addElement('html', $ajaxHtml);
2481
2482
        return $form;
2483
    }
2484
}
2485
2486
/**
2487
 * @param CForumThread $threadInfo
2488
 * @param int          $user_id
2489
 * @param int          $thread_id
2490
 * @param int          $thread_qualify
2491
 * @param int          $qualify_time
2492
 * @param int          $session_id
2493
 *
2494
 * @return string
2495
 *
2496
 * @author Isaac Flores <[email protected]>, U.N.A.S University
2497
 *
2498
 * @version October 2008, dokeos  1.8.6
2499
 */
2500
function saveThreadScore(
2501
    CForumThread $threadEntity,
2502
    $user_id,
2503
    $thread_id,
2504
    $thread_qualify,
2505
    $qualify_time,
2506
    $session_id
2507
) {
2508
    $table_threads_qualify = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY);
2509
2510
    $course_id = api_get_course_int_id();
2511
    $session_id = (int) $session_id;
2512
    $thread_id = (int) $thread_id;
2513
    $user_id = (int) $user_id;
2514
    $thread_qualify = (float) $thread_qualify;
2515
    $currentUserId = api_get_user_id();
2516
    $qualify_time = Database::escape_string($qualify_time);
2517
2518
    $max = $threadEntity->getThreadQualifyMax();
2519
    if ($thread_qualify <= $max) {
2520
        if ($threadEntity->isThreadPeerQualify()) {
2521
            $sql = "SELECT COUNT(*) FROM $table_threads_qualify
2522
                    WHERE
2523
                        user_id = $user_id AND
2524
                        qualify_user_id = $currentUserId AND
2525
                        threadId = ".$thread_id;
2526
        } else {
2527
            $sql = "SELECT COUNT(*) FROM $table_threads_qualify
2528
                    WHERE
2529
                        user_id = $user_id AND
2530
                        threadId = ".$thread_id;
2531
        }
2532
2533
        $result = Database::query($sql);
2534
        $row = Database::fetch_array($result);
2535
2536
        if (0 == $row[0]) {
2537
            $sql = "INSERT INTO $table_threads_qualify (c_id, user_id, threadId,qualify,qualify_user_id,qualify_time,session_id)
2538
                    VALUES (".$course_id.", '".$user_id."','".$thread_id."',".$thread_qualify.", '".$currentUserId."','".$qualify_time."','".$session_id."')";
2539
            Database::query($sql);
2540
2541
            return 'insert';
2542
        } else {
2543
            saveThreadScoreHistory(
2544
                '1',
2545
                $course_id,
2546
                $user_id,
2547
                $thread_id
2548
            );
2549
2550
            // Update
2551
            $sql = "UPDATE $table_threads_qualify
2552
                    SET
2553
                        qualify = '".$thread_qualify."',
2554
                        qualify_time = '".$qualify_time."'
2555
                    WHERE
2556
                        user_id=".$user_id.' AND
2557
                        threadId='.$thread_id." AND
2558
                        qualify_user_id = $currentUserId
2559
                    ";
2560
            Database::query($sql);
2561
2562
            return 'update';
2563
        }
2564
    }
2565
2566
    return '';
2567
}
2568
2569
/**
2570
 * This function shows qualify.
2571
 *
2572
 * @param string $option    contains the information of option to run
2573
 * @param int    $user_id   contains the information the current user id
2574
 * @param int    $thread_id contains the information the current thread id
2575
 *
2576
 * @return int qualify
2577
 *             <code> $option=1 obtained the qualification of the current thread</code>
2578
 *
2579
 * @author Isaac Flores <[email protected]>, U.N.A.S University
2580
 *
2581
 * @version October 2008, dokeos  1.8.6
2582
 */
2583
function showQualify($option, $user_id, $thread_id)
2584
{
2585
    $table_threads_qualify = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY);
2586
    $table_threads = Database::get_course_table(TABLE_FORUM_THREAD);
2587
2588
    $course_id = api_get_course_int_id();
2589
    $user_id = (int) $user_id;
2590
    $thread_id = (int) $thread_id;
2591
2592
    if (empty($user_id) || empty($thread_id)) {
2593
        return 0;
2594
    }
2595
2596
    $sql = '';
2597
    switch ($option) {
2598
        case 1:
2599
            $sql = "SELECT qualify FROM $table_threads_qualify
2600
                    WHERE
2601
                        c_id = $course_id AND
2602
                        user_id=".$user_id.' AND
2603
                        threadId='.$thread_id;
2604
2605
            break;
2606
        case 2:
2607
            $sql = "SELECT thread_qualify_max FROM $table_threads
2608
                    WHERE c_id = $course_id AND iid=".$thread_id;
2609
2610
            break;
2611
    }
2612
2613
    if (!empty($sql)) {
2614
        $rs = Database::query($sql);
2615
        $row = Database::fetch_array($rs);
2616
        if ($row) {
2617
            return $row[0];
2618
        }
2619
    }
2620
2621
    return 0;
2622
}
2623
2624
/**
2625
 * This function gets qualify historical.
2626
 *
2627
 * @param int  $user_id   contains the information the current user id
2628
 * @param int  $thread_id contains the information the current thread id
2629
 * @param bool $opt       contains the information of option to run
2630
 *
2631
 * @return array
2632
 *
2633
 * @author Christian Fasanando <[email protected]>,
2634
 * @author Isaac Flores <[email protected]>,
2635
 *
2636
 * @version October 2008, dokeos  1.8.6
2637
 */
2638
function getThreadScoreHistory($user_id, $thread_id, $opt)
2639
{
2640
    $user_id = (int) $user_id;
2641
    $thread_id = (int) $thread_id;
2642
2643
    $table_threads_qualify_log = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY_LOG);
2644
    $course_id = api_get_course_int_id();
2645
2646
    if ('false' == $opt) {
2647
        $sql = "SELECT * FROM $table_threads_qualify_log
2648
                WHERE
2649
                    c_id = $course_id AND
2650
                    threadId='".$thread_id."' AND
2651
                    user_id='".$user_id."'
2652
                ORDER BY qualify_time";
2653
    } else {
2654
        $sql = "SELECT * FROM $table_threads_qualify_log
2655
                WHERE
2656
                    c_id = $course_id AND
2657
                    threadId='".$thread_id."' AND
2658
                    user_id='".$user_id."'
2659
                ORDER BY qualify_time DESC";
2660
    }
2661
    $rs = Database::query($sql);
2662
    $log = [];
2663
    while ($row = Database::fetch_array($rs, 'ASSOC')) {
2664
        $log[] = $row;
2665
    }
2666
2667
    return $log;
2668
}
2669
2670
/**
2671
 * This function stores qualify historical.
2672
 *
2673
 * @param bool contains the information of option to run
2674
 * @param string contains the information the current course id
2675
 * @param int contains the information the current forum id
2676
 * @param int contains the information the current user id
2677
 * @param int contains the information the current thread id
2678
 * @param int contains the information the current qualify
2679
 * @param string $option
2680
 * @param int    $course_id
2681
 * @param int    $user_id
2682
 * @param int    $thread_id
2683
 *
2684
 * @author Isaac Flores <[email protected]>, U.N.A.S University
2685
 *
2686
 * @version October 2008, dokeos  1.8.6
2687
 */
2688
function saveThreadScoreHistory(
2689
    $option,
2690
    $course_id,
2691
    $user_id,
2692
    $thread_id
2693
) {
2694
    $table_threads_qualify = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY);
2695
    $table_threads_qualify_log = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY_LOG);
2696
2697
    $thread_id = (int) $thread_id;
2698
    $course_id = (int) $course_id;
2699
    $user_id = (int) $user_id;
2700
    $qualify_user_id = api_get_user_id();
2701
2702
    if (1 == $option) {
2703
        // Extract information of thread_qualify.
2704
        $sql = "SELECT qualify, qualify_time
2705
                FROM $table_threads_qualify
2706
                WHERE
2707
                    c_id = $course_id AND
2708
                    user_id = ".$user_id.' AND
2709
                    threadId = '.$thread_id." AND
2710
                    qualify_user_id = $qualify_user_id
2711
                ";
2712
        $rs = Database::query($sql);
2713
        $row = Database::fetch_array($rs);
2714
2715
        // Insert thread_historical.
2716
        $sql = "INSERT INTO $table_threads_qualify_log (c_id, user_id, threadId, qualify, qualify_user_id,qualify_time,session_id)
2717
                VALUES(".$course_id.", '".$user_id."','".$thread_id."',".(float) $row[0].", '".$qualify_user_id."','".$row[1]."','')";
2718
        Database::query($sql);
2719
    }
2720
}
2721
2722
/**
2723
 * This function shows current thread qualify .
2724
 *
2725
 * @param int $threadId
2726
 * @param int $sessionId
2727
 * @param int $userId
2728
 *
2729
 * @return array or null if is empty
2730
 *
2731
 * @author Isaac Flores <[email protected]>, U.N.A.S University
2732
 *
2733
 * @version December 2008, dokeos  1.8.6
2734
 */
2735
function current_qualify_of_thread($threadId, $sessionId, $userId)
2736
{
2737
    $table_threads_qualify = Database::get_course_table(TABLE_FORUM_THREAD_QUALIFY);
2738
2739
    $course_id = api_get_course_int_id();
2740
    $currentUserId = api_get_user_id();
2741
    $sessionId = (int) $sessionId;
2742
    $threadId = (int) $threadId;
2743
2744
    $sql = "SELECT qualify FROM $table_threads_qualify
2745
            WHERE
2746
                c_id = $course_id AND
2747
                threadId = $threadId AND
2748
                session_id = $sessionId AND
2749
                qualify_user_id = $currentUserId AND
2750
                user_id = $userId
2751
            ";
2752
    $res = Database::query($sql);
2753
    $row = Database::fetch_array($res, 'ASSOC');
2754
2755
    if ($row) {
2756
        return $row['qualify'];
2757
    }
2758
2759
    return 0;
2760
}
2761
2762
/**
2763
 * This function stores a reply in the forum_post table.
2764
 * It also updates the forum_threads table (thread_replies +1 , thread_last_post, thread_date).
2765
 *
2766
 * @param array $values
2767
 * @param int   $courseId Optional
2768
 * @param int   $userId   Optional
2769
 *
2770
 * @return int post id
2771
 */
2772
function store_reply(CForum $forum, CForumThread $thread, $values, $courseId = 0, $userId = 0)
2773
{
2774
    $courseId = !empty($courseId) ? $courseId : api_get_course_int_id();
2775
    $post_date = api_get_utc_datetime();
2776
    $userId = $userId ?: api_get_user_id();
2777
2778
    if (1 == $forum->getAllowAnonymous()) {
2779
        if (api_is_anonymous() && empty($userId)) {
2780
            $userId = api_get_anonymous_id();
2781
        }
2782
    }
2783
2784
    if (empty($userId)) {
2785
        return false;
2786
    }
2787
2788
    $visible = 1;
2789
    if ('1' == $forum->getApprovalDirectPost() &&
2790
        !api_is_allowed_to_edit(null, true)
2791
    ) {
2792
        $visible = 0;
2793
    }
2794
2795
    $upload_ok = 1;
2796
    $new_post_id = 0;
2797
2798
    if ($upload_ok) {
2799
        $course = api_get_course_entity($courseId);
2800
        $session = api_get_session_entity();
2801
2802
        $repo = Container::getForumPostRepository();
2803
        $post = new CForumPost();
2804
        $post
2805
            ->setPostTitle($values['post_title'])
2806
            ->setPostText(isset($values['post_text']) ?: null)
2807
            ->setThread($thread)
2808
            ->setForum($forum)
2809
            ->setUser(api_get_user_entity($userId))
2810
            ->setPostNotification(isset($values['post_notification']) ? (bool) $values['post_notification'] : false)
2811
            ->setVisible($visible)
2812
            ->setPostDate(api_get_utc_datetime(null, false, true))
2813
            ->setParent($thread)
2814
            ->addCourseLink($course, $session)
2815
        ;
2816
2817
        if (isset($values['post_parent_id']) && !empty($values['post_parent_id'])) {
2818
            $parent = $repo->find($values['post_parent_id']);
2819
            $post->setPostParent($parent);
2820
        }
2821
        $repo->create($post);
2822
2823
        $new_post_id = $post->getIid();
2824
        if ($new_post_id) {
2825
            $values['new_post_id'] = $new_post_id;
2826
            $message = get_lang('The reply has been added');
2827
2828
            if (!empty($_POST['file_ids']) && is_array($_POST['file_ids'])) {
2829
                foreach ($_POST['file_ids'] as $key => $id) {
2830
                    editAttachedFile(
2831
                        [
2832
                            'comment' => $_POST['file_comments'][$key],
2833
                            'post_id' => $new_post_id,
2834
                        ],
2835
                        $id
2836
                    );
2837
                }
2838
            }
2839
2840
            // Update the thread.
2841
            updateThreadInfo($values['threadId'], $new_post_id, $post_date);
2842
2843
            if ('1' == $forum->getApprovalDirectPost() &&
2844
                !api_is_allowed_to_edit(null, true)
2845
            ) {
2846
                $message .= '<br />'.get_lang('Your message has to be approved before people can view it.').'<br />';
2847
            }
2848
2849
            if ($forum->isModerated() &&
2850
                !api_is_allowed_to_edit(null, true)
2851
            ) {
2852
                $message .= '<br />'.get_lang('Your message has to be approved before people can view it.').'<br />';
2853
            }
2854
2855
            // Setting the notification correctly.
2856
            $my_post_notification = isset($values['post_notification']) ? $values['post_notification'] : null;
2857
            if (1 == $my_post_notification) {
2858
                set_notification('thread', $values['threadId'], true);
2859
            }
2860
2861
            send_notification_mails(
2862
                $forum,
2863
                $thread,
2864
                $values
2865
            );
2866
            add_forum_attachment_file('', $post);
2867
2868
            $logInfo = [
2869
                'tool' => TOOL_FORUM,
2870
                'tool_id' => $values['forum_id'],
2871
                'tool_id_detail' => $values['threadId'],
2872
                'action' => 'new-post',
2873
                'action_details' => $values['action'],
2874
                'info' => $values['post_title'],
2875
            ];
2876
            Event::registerLog($logInfo);
2877
        }
2878
2879
        Session::erase('formelements');
2880
        Session::erase('origin');
2881
        Session::erase('breadcrumbs');
2882
        Session::erase('addedresource');
2883
        Session::erase('addedresourceid');
2884
2885
        Display::addFlash(Display::return_message($message, 'confirmation', false));
2886
    } else {
2887
        Display::addFlash(
2888
            Display::return_message(
2889
                get_lang('No file was uploaded.').' '.get_lang('Please select a file before pressing the upload button.'),
2890
                'error'
2891
            )
2892
        );
2893
    }
2894
2895
    return $new_post_id;
2896
}
2897
2898
/**
2899
 * This function displays the form that is used to edit a post. This can be a new thread or a reply.
2900
 *
2901
 * @param CForumPost   $post        contains all the information about the current post
2902
 * @param CForumThread $thread      contains all the information about the current thread
2903
 * @param CForum       $forum       contains all info about the current forum (to check if attachments are allowed)
2904
 * @param array        $form_values contains the default values to fill the form
2905
 *
2906
 * @author Patrick Cool <[email protected]>, Ghent University
2907
 *
2908
 * @version february 2006, dokeos 1.8
2909
 */
2910
function show_edit_post_form(
2911
    $post,
2912
    $thread,
2913
    $forum,
2914
    $form_values,
2915
    $id_attach = 0
2916
) {
2917
    // Initialize the object.
2918
    $form = new FormValidator(
2919
        'edit_post',
2920
        'post',
2921
        api_get_self().'?'.api_get_cidreq().'&forum='.(int) ($_GET['forum']).'&thread='.(int) ($_GET['thread']).'&post='.(int) ($_GET['post'])
2922
    );
2923
    $form->addElement('header', get_lang('Edit a post'));
2924
    // Setting the form elements.
2925
    $form->addElement('hidden', 'post_id', $post->getIid());
2926
    $form->addElement('hidden', 'threadId', $thread->getIid());
2927
    $form->addElement('hidden', 'id_attach', $id_attach);
2928
2929
    if (null === $post->getPostParent()) {
2930
        $form->addElement('hidden', 'is_first_post_of_thread', '1');
2931
    }
2932
2933
    $form->addElement('text', 'post_title', get_lang('Title'));
2934
    $form->applyFilter('post_title', 'html_filter');
2935
    $form->addElement(
2936
        'html_editor',
2937
        'post_text',
2938
        get_lang('Text'),
2939
        null,
2940
        api_is_allowed_to_edit(null, true) ? [
2941
            'ToolbarSet' => 'Forum',
2942
            'Width' => '100%',
2943
            'Height' => '400',
2944
        ] : [
2945
            'ToolbarSet' => 'ForumStudent',
2946
            'Width' => '100%',
2947
            'Height' => '400',
2948
            'UserStatus' => 'student',
2949
        ]
2950
    );
2951
    $form->addRule('post_text', get_lang('Required field'), 'required');
2952
2953
    $extraFields = new ExtraField('forum_post');
2954
    $extraFields->addElements($form, $post->getIid());
2955
2956
    $form->addButtonAdvancedSettings('advanced_params');
2957
    $form->addElement('html', '<div id="advanced_params_options" style="display:none">');
2958
2959
    if ($forum->isModerated() && api_is_allowed_to_edit(null, true)) {
2960
        $group = [];
2961
        $group[] = $form->createElement(
2962
            'radio',
2963
            'status',
2964
            null,
2965
            get_lang('Validated'),
2966
            1
2967
        );
2968
        $group[] = $form->createElement(
2969
            'radio',
2970
            'status',
2971
            null,
2972
            get_lang('Waiting for moderation'),
2973
            2
2974
        );
2975
        $group[] = $form->createElement(
2976
            'radio',
2977
            'status',
2978
            null,
2979
            get_lang('Rejected'),
2980
            3
2981
        );
2982
        $form->addGroup($group, 'status', get_lang('Status'));
2983
    }
2984
2985
    $defaults['status']['status'] = $post->getStatus();
2986
2987
    $form->addElement(
2988
        'checkbox',
2989
        'post_notification',
2990
        '',
2991
        get_lang('Notify me by e-mail when somebody replies')
2992
    );
2993
2994
    if (api_is_allowed_to_edit(null, true) && null === $post->getPostParent()) {
2995
        // The sticky checkbox only appears when it is the first post of a thread.
2996
        $form->addElement(
2997
            'checkbox',
2998
            'thread_sticky',
2999
            '',
3000
            get_lang('This is a sticky message (appears always on top and has a special sticky icon)')
3001
        );
3002
        if (1 == $thread->getThreadSticky()) {
3003
            $defaults['thread_sticky'] = true;
3004
        }
3005
    }
3006
3007
    $form->addElement('html', '</div>');
3008
3009
    $form->addFile('user_upload[]', get_lang('Attachment'));
3010
    $form->addButton(
3011
        'add_attachment',
3012
        get_lang('Add attachment'),
3013
        'paperclip',
3014
        'default',
3015
        'default',
3016
        null,
3017
        ['id' => 'reply-add-attachment']
3018
    );
3019
3020
    $form->addButtonUpdate(get_lang('Edit'), 'SubmitPost');
3021
3022
    // Setting the default values for the form elements.
3023
    $defaults['post_title'] = $post->getPostTitle();
3024
    $defaults['post_text'] = $post->getPostText();
3025
3026
    if (1 == $post->getPostNotification()) {
3027
        $defaults['post_notification'] = true;
3028
    }
3029
3030
    if (!empty($form_values)) {
3031
        $defaults['post_notification'] = Security::remove_XSS($form_values['post_notification']);
3032
        $defaults['thread_sticky'] = Security::remove_XSS($form_values['thread_sticky']);
3033
    }
3034
3035
    $form->setDefaults($defaults);
3036
3037
    // The course admin can make a thread sticky (=appears with special icon and always on top).
3038
    $form->addRule('post_title', get_lang('Required field'), 'required');
3039
3040
    // Validation or display
3041
    if ($form->validate()) {
3042
        $values = $form->exportValues();
3043
        $values['item_id'] = $post->getIid();
3044
        $extraFieldValues = new ExtraFieldValue('forum_post');
3045
        $extraFieldValues->saveFieldValues($values);
3046
3047
        store_edit_post($forum, $values);
3048
    } else {
3049
        // Delete from $_SESSION forum attachment from other posts
3050
        clearAttachedFiles($post->getIid());
3051
        // Get forum attachment ajax table to add it to form
3052
        $fileData = getAttachmentsAjaxTable($post->getIid(), $forum->getIid());
3053
        $form->addElement('html', $fileData);
3054
        $form->display();
3055
    }
3056
}
3057
3058
/**
3059
 * This function stores the edit of a post in the forum_post table.
3060
 *
3061
 * @author Patrick Cool <[email protected]>, Ghent University
3062
 *
3063
 * @version february 2006, dokeos 1.8
3064
 */
3065
function store_edit_post(CForum $forum, $values)
3066
{
3067
    $logInfo = [
3068
        'tool' => TOOL_FORUM,
3069
        'tool_id' => $_GET['forum'],
3070
        'tool_id_detail' => $values['threadId'],
3071
        'action' => 'edit-post',
3072
        'action_details' => 'post',
3073
        'info' => $values['post_title'],
3074
    ];
3075
    Event::registerLog($logInfo);
3076
3077
    $threadTable = Database::get_course_table(TABLE_FORUM_THREAD);
3078
3079
    //check if this post is the first of the thread
3080
    // First we check if the change affects the thread and if so we commit
3081
    // the changes (sticky and post_title=thread_title are relevant).
3082
    $posts = getPosts($forum, $values['threadId']);
3083
    $first_post = null;
3084
    if (!empty($posts) && count($posts) > 0 && isset($posts[0])) {
3085
        $first_post = $posts[0];
3086
    }
3087
3088
    if (!empty($first_post) && $first_post['post_id'] == $values['post_id']) {
3089
        // Simple edit
3090
        $params = [
3091
            'thread_title' => $values['post_title'],
3092
            'thread_sticky' => isset($values['thread_sticky']) ? $values['thread_sticky'] : 0,
3093
        ];
3094
        $where = ['iid = ?' => [$values['threadId']]];
3095
        Database::update($threadTable, $params, $where);
3096
    }
3097
3098
    $status = '';
3099
    $updateStatus = false;
3100
    if ($forum->isModerated()) {
3101
        if (api_is_allowed_to_edit(null, true)) {
3102
            $status = $values['status']['status'];
3103
            $updateStatus = true;
3104
        } else {
3105
            $status = CForumPost::STATUS_WAITING_MODERATION;
3106
            $updateStatus = true;
3107
        }
3108
    }
3109
3110
    $postId = $values['post_id'];
3111
    $repo = Container::getForumPostRepository();
3112
    /** @var CForumPost $post */
3113
    $post = $repo->find($postId);
3114
    if ($post) {
3115
        $post
3116
            ->setPostTitle($values['post_title'])
3117
            ->setPostText($values['post_text'])
3118
            ->setPostNotification(isset($values['post_notification']))
3119
        ;
3120
3121
        if ($updateStatus) {
3122
            $post->setStatus($status);
3123
        }
3124
        $repo->update($post);
3125
    }
3126
3127
    // Update attached files
3128
    if (!empty($_POST['file_ids']) && is_array($_POST['file_ids'])) {
3129
        foreach ($_POST['file_ids'] as $key => $id) {
3130
            editAttachedFile(
3131
                [
3132
                    'comment' => $_POST['file_comments'][$key],
3133
                    'post_id' => $values['post_id'],
3134
                ],
3135
                $id
3136
            );
3137
        }
3138
    }
3139
3140
    if (!empty($values['remove_attach'])) {
3141
        throw new Exception('remove_attach');
3142
    }
3143
3144
    if (empty($values['id_attach'])) {
3145
        add_forum_attachment_file(
3146
            isset($values['file_comment']) ? $values['file_comment'] : null,
3147
            $post
3148
        );
3149
    }
3150
3151
    $message = get_lang('The post has been modified').'<br />';
3152
    $message .= get_lang('You can now return to the').
3153
        ' <a href="viewforum.php?'.api_get_cidreq().'&forum='.(int) ($_GET['forum']).'&">'.
3154
        get_lang('Forum').'</a><br />';
3155
    $message .= get_lang('You can now return to the').
3156
        ' <a
3157
            href="viewthread.php?'.api_get_cidreq().'&forum='.(int) ($_GET['forum']).'&thread='.$values['threadId'].'&post='.Security::remove_XSS($_GET['post']).'">'.
3158
        get_lang('Message').'</a>';
3159
3160
    Session::erase('formelements');
3161
    Session::erase('origin');
3162
    Session::erase('breadcrumbs');
3163
    Session::erase('addedresource');
3164
    Session::erase('addedresourceid');
3165
3166
    echo Display::return_message($message, 'confirmation', false);
3167
}
3168
3169
function displayUserLink(User $user)
3170
{
3171
    return '<a href="'.$user->getProfileUrl().'">'.
3172
        Security::remove_XSS(UserManager::formatUserFullName($user)).'</a>';
3173
}
3174
3175
function displayUserImage(User $user)
3176
{
3177
    $url = Container::getIllustrationRepository()->getIllustrationUrl($user, '', ICON_SIZE_BIG);
3178
3179
    return '<div class="thumbnail"><img src="'.$url.'?w=100"/></div>';
3180
}
3181
3182
/**
3183
 * The relies counter gets increased every time somebody replies to the thread.
3184
 *
3185
 * @author Patrick Cool <[email protected]>, Ghent University
3186
 *
3187
 * @version february 2006, dokeos 1.8
3188
 *
3189
 * @param int    $threadId
3190
 * @param string $lastPostId
3191
 * @param string $post_date
3192
 */
3193
function updateThreadInfo($threadId, $lastPostId, $post_date)
3194
{
3195
    $table_threads = Database::get_course_table(TABLE_FORUM_THREAD);
3196
    $threadId = (int) $threadId;
3197
    $lastPostId = (int) $lastPostId;
3198
3199
    $sql = "UPDATE $table_threads SET
3200
            thread_replies = thread_replies+1,
3201
            thread_last_post = '".$lastPostId."',
3202
            thread_date = '".Database::escape_string($post_date)."'
3203
            WHERE
3204
                iid ='".$threadId."'"; // this needs to be cleaned first
3205
    Database::query($sql);
3206
}
3207
3208
function approvePost(CForumPost $post, $action)
3209
{
3210
    if ('invisible' === $action) {
3211
        $visibility = 0;
3212
    }
3213
3214
    if ('visible' === $action) {
3215
        $visibility = 1;
3216
        handle_mail_cue('post', $post->getIid());
3217
    }
3218
3219
    $post->setVisible($visibility);
3220
    Database::getManager()->persist($post);
3221
    Database::getManager()->flush();
3222
3223
    Display::addFlash(Display::return_message(get_lang('The visibility has been changed')));
3224
}
3225
3226
/**
3227
 * This function retrieves all the unapproved messages for a given forum
3228
 * This is needed to display the icon that there are unapproved messages in that thread (only the courseadmin can see
3229
 * this).
3230
 *
3231
 * @param int $forum_id forum where we want to know the unapproved messages of
3232
 *
3233
 * @return array returns
3234
 *
3235
 * @author Patrick Cool <[email protected]>, Ghent University
3236
 *
3237
 * @version february 2006, dokeos 1.8
3238
 */
3239
function get_unaproved_messages($forum_id)
3240
{
3241
    $table_posts = Database::get_course_table(TABLE_FORUM_POST);
3242
    $course_id = api_get_course_int_id();
3243
3244
    $return_array = [];
3245
    $sql = "SELECT DISTINCT threadId FROM $table_posts
3246
            WHERE
3247
                c_id = $course_id AND
3248
                forum_id='".Database::escape_string($forum_id)."' AND
3249
                visible='0' ";
3250
    $result = Database::query($sql);
3251
    while ($row = Database::fetch_array($result)) {
3252
        $return_array[] = $row['threadId'];
3253
    }
3254
3255
    return $return_array;
3256
}
3257
3258
/**
3259
 * This function sends the notification mails to everybody who stated that they wanted to be informed when a new post
3260
 * was added to a given thread.
3261
 */
3262
function send_notification_mails(CForum $forum, CForumThread $thread, $reply_info)
3263
{
3264
    $courseEntity = api_get_course_entity();
3265
    $courseId = $courseEntity->getId();
3266
3267
    $sessionId = api_get_session_id();
3268
    $sessionEntity = api_get_session_entity($sessionId);
3269
3270
    $table = Database::get_course_table(TABLE_FORUM_MAIL_QUEUE);
3271
3272
    // First we need to check if
3273
    // 1. the forum category is visible
3274
    // 2. the forum is visible
3275
    // 3. the thread is visible
3276
    // 4. the reply is visible (=when there is)
3277
3278
    $current_forum_category = null;
3279
    if ($forum->getForumCategory()) {
3280
        $current_forum_category = $forum->getForumCategory();
3281
    }
3282
3283
    $send_mails = false;
3284
    if ($thread->isVisible($courseEntity, $sessionEntity) &&
3285
        $forum->isVisible($courseEntity, $sessionEntity) &&
3286
        ($current_forum_category && $forum->getForumCategory()->isVisible($courseEntity, $sessionEntity)) &&
3287
        '1' != $forum->getApprovalDirectPost()
3288
    ) {
3289
        $send_mails = true;
3290
    }
3291
3292
    // The forum category, the forum, the thread and the reply are visible to the user
3293
    if ($send_mails && !empty($forum)) {
3294
        $postId = isset($reply_info['new_post_id']) ? $reply_info['new_post_id'] : 0;
3295
        send_notifications($forum, $thread, $postId);
3296
    } else {
3297
        $table_notification = Database::get_course_table(TABLE_FORUM_NOTIFICATION);
3298
        if ($forum) {
3299
            $sql = "SELECT * FROM $table_notification
3300
                    WHERE
3301
                        c_id = ".$courseId." AND
3302
                        (
3303
                            forum_id = '".$forum->getIid()."' OR
3304
                            threadId = '".$thread->getIid()."'
3305
                        ) ";
3306
            $result = Database::query($sql);
3307
            $user_id = api_get_user_id();
3308
            while ($row = Database::fetch_array($result)) {
3309
                $sql = "INSERT INTO $table (c_id, threadId, post_id, user_id)
3310
                        VALUES (".$courseId.", '".$thread->getIid()."', '".(int) ($reply_info['new_post_id'])."', '$user_id' )";
3311
                Database::query($sql);
3312
            }
3313
        }
3314
    }
3315
}
3316
3317
/**
3318
 * This function is called whenever something is made visible because there might
3319
 * be new posts and the user might have indicated that (s)he wanted to be
3320
 * informed about the new posts by mail.
3321
 *
3322
 * @param string $content Content type (post, thread, forum, forum_category)
3323
 * @param int    $id      Item DB ID of the corresponding content type
3324
 *
3325
 * @return string language variable
3326
 *
3327
 * @author Patrick Cool <[email protected]>, Ghent University
3328
 *
3329
 * @version february 2006, dokeos 1.8
3330
 */
3331
function handle_mail_cue($content, $id)
3332
{
3333
    $table_mailcue = Database::get_course_table(TABLE_FORUM_MAIL_QUEUE);
3334
    $table_forums = Database::get_course_table(TABLE_FORUM);
3335
    $table_threads = Database::get_course_table(TABLE_FORUM_THREAD);
3336
    $table_posts = Database::get_course_table(TABLE_FORUM_POST);
3337
    $table_users = Database::get_main_table(TABLE_MAIN_USER);
3338
3339
    $course_id = api_get_course_int_id();
3340
    $id = (int) $id;
3341
3342
    /* If the post is made visible we only have to send mails to the people
3343
     who indicated that they wanted to be informed for that thread.*/
3344
    if ('post' === $content) {
3345
        // Getting the information about the post (need the threadId).
3346
        /** @var CForumPost $post */
3347
        $post = Container::getForumPostRepository()->find($id);
3348
        $thread_id = $post->getThread()->getIid();
3349
3350
        // Sending the mail to all the users that wanted to be informed for replies on this thread.
3351
        $sql = "SELECT users.firstname, users.lastname, users.id as user_id, users.email
3352
                FROM $table_mailcue mailcue, $table_posts posts, $table_users users
3353
                WHERE
3354
                    posts.c_id = $course_id AND
3355
                    mailcue.c_id = $course_id AND
3356
                    posts.threadId = $thread_id AND
3357
                    posts.post_notification = '1' AND
3358
                    mailcue.threadId = $thread_id AND
3359
                    users.id = posts.poster_id AND
3360
                    users.active = 1
3361
                GROUP BY users.email";
3362
3363
        $result = Database::query($sql);
3364
        $forum = Container::getForumRepository()->find($post->getForum()->getIid());
3365
3366
        while ($row = Database::fetch_array($result)) {
3367
            send_mail($row, $forum, $post->getThread(), $post);
3368
        }
3369
    } elseif ('thread' === $content) {
3370
        // Sending the mail to all the users that wanted to be informed for replies on this thread.
3371
        $sql = "SELECT users.firstname, users.lastname, users.id as user_id, users.email, posts.forum_id
3372
                FROM $table_mailcue mailcue, $table_posts posts, $table_users users
3373
                WHERE
3374
                    posts.c_id = $course_id AND
3375
                    mailcue.c_id = $course_id AND
3376
                    posts.threadId = $id AND
3377
                    posts.post_notification = '1' AND
3378
                    mailcue.threadId = $id AND
3379
                    users.id = posts.poster_id AND
3380
                    users.active = 1
3381
                GROUP BY users.email";
3382
        $result = Database::query($sql);
3383
        while ($row = Database::fetch_array($result)) {
3384
            $forum = Container::getForumRepository()->find($row['forum_id']);
3385
            $thread = Container::getForumThreadRepository()->find($id);
3386
            send_mail($row, $forum, $thread);
3387
        }
3388
3389
        // Deleting the relevant entries from the mailcue.
3390
        $sql = "DELETE FROM $table_mailcue
3391
                WHERE c_id = $course_id AND threadId = $id";
3392
        Database::query($sql);
3393
    } elseif ('forum' === $content) {
3394
        $sql = "SELECT iid FROM $table_threads
3395
                WHERE c_id = $course_id AND forum_id = $id";
3396
        $result = Database::query($sql);
3397
        while ($row = Database::fetch_array($result)) {
3398
            handle_mail_cue('thread', $row['iid']);
3399
        }
3400
    } elseif ('forum_category' === $content) {
3401
        $sql = "SELECT iid FROM $table_forums
3402
                WHERE c_id = $course_id AND forum_category = $id";
3403
        $result = Database::query($sql);
3404
        while ($row = Database::fetch_array($result)) {
3405
            handle_mail_cue('forum', $row['iid']);
3406
        }
3407
    } else {
3408
        return get_lang('Error');
3409
    }
3410
}
3411
3412
/**
3413
 * This function sends the mails for the mail notification.
3414
 */
3415
function send_mail($userInfo, CForum $forum, CForumThread $thread, CForumPost $postInfo = null)
3416
{
3417
    if (empty($userInfo) || empty($forum) || empty($thread)) {
3418
        return false;
3419
    }
3420
3421
    $_course = api_get_course_info();
3422
    $user_id = api_get_user_id();
3423
    $forumId = $forum->getIid();
3424
    $threadId = $thread->getIid();
3425
3426
    $thread_link = api_get_path(WEB_CODE_PATH).
3427
        'forum/viewthread.php?'.api_get_cidreq().'&forum='.$forumId.'&thread='.$threadId;
3428
3429
    $email_body = get_lang('Dear').' '.
3430
        api_get_person_name($userInfo['firstname'], $userInfo['lastname'], null, PERSON_NAME_EMAIL_ADDRESS).", <br />\n\r";
3431
    $email_body .= get_lang('New Post in the forum').
3432
        ': '.$forum->getForumTitle().' - '.$thread->getThreadTitle()." <br />\n";
3433
3434
    $courseId = api_get_configuration_value('global_forums_course_id');
3435
    $subject = get_lang('New Post in the forum').' - '.
3436
        $_course['official_code'].': '.$forum->getForumTitle().' - '.$thread->getThreadTitle()." <br />\n";
3437
3438
    $courseInfoTitle = get_lang('Course').': '.$_course['name'].' - ['.$_course['official_code']."] - <br />\n";
3439
    if (!empty($courseId) && $_course['real_id'] == $courseId) {
3440
        $subject = get_lang('New Post in the forum').': '.
3441
            $forum->getForumTitle().' - '.$thread->getThreadTitle()." <br />\n";
3442
        $courseInfoTitle = " <br />\n";
3443
    }
3444
    $email_body .= $courseInfoTitle;
3445
3446
    if (!empty($postInfo)) {
3447
        $text = cut(strip_tags($postInfo->getPostText()), 100);
3448
        if (!empty($text)) {
3449
            $email_body .= get_lang('Message').": <br />\n ";
3450
            $email_body .= $text;
3451
            $email_body .= "<br /><br />\n";
3452
        }
3453
    }
3454
3455
    $email_body .= get_lang('You stated that you wanted to be informed by e-mail whenever somebody replies on the thread')."<br />\n";
3456
3457
    if (!empty($thread_link)) {
3458
        $email_body .= get_lang('The thread can be found here').' : <br /><a href="'.$thread_link.'">'.$thread_link."</a>\n";
3459
    }
3460
3461
    if ($userInfo['user_id'] != $user_id) {
3462
        MessageManager::send_message(
3463
            $userInfo['user_id'],
3464
            $subject,
3465
            $email_body,
3466
            [],
3467
            [],
3468
            null,
3469
            null,
3470
            null,
3471
            null,
3472
            $user_id
3473
        );
3474
    }
3475
}
3476
3477
/**
3478
 * This function displays the form for moving a thread to a different (already existing) forum.
3479
 *
3480
 * @author Patrick Cool <[email protected]>, Ghent University
3481
 *
3482
 * @version february 2006, dokeos 1.8
3483
 */
3484
function move_thread_form()
3485
{
3486
    $form = new FormValidator(
3487
        'movepost',
3488
        'post',
3489
        api_get_self().'?forum='.(int) ($_GET['forum']).'&thread='.(int) ($_GET['thread']).'&action='.Security::remove_XSS($_GET['action']).'&'.api_get_cidreq()
3490
    );
3491
    $form->addHeader(get_lang('Move Thread'));
3492
    // Invisible form: the threadId
3493
    $form->addHidden('threadId', (int) ($_GET['thread']));
3494
    $forum_categories = get_forum_categories();
3495
3496
    $htmlcontent = '<div class="row">
3497
        <div class="label">
3498
            <span class="form_required">*</span>'.get_lang('Move to').'
3499
        </div>
3500
        <div class="formw">';
3501
    $htmlcontent .= '<select name="forum">';
3502
    foreach ($forum_categories as $category) {
3503
        $htmlcontent .= '<optgroup label="'.$category->getCatTitle().'">';
3504
        $forums = $category->getForums();
3505
        foreach ($forums as $forum) {
3506
            $htmlcontent .= '<option value="'.$forum->getIid().'">'.$forum->getForumTitle().'</option>';
3507
        }
3508
        $htmlcontent .= '</optgroup>';
3509
    }
3510
    $htmlcontent .= '</select>';
3511
    $htmlcontent .= '   </div>
3512
                    </div>';
3513
3514
    $form->addElement('html', $htmlcontent);
3515
3516
    // The OK button
3517
    $form->addButtonSave(get_lang('Move Thread'), 'SubmitForum');
3518
3519
    // Validation or display
3520
    if ($form->validate()) {
3521
        $values = $form->exportValues();
3522
        if (isset($_POST['forum'])) {
3523
            store_move_thread($values);
3524
            Display::addFlash(Display::return_message(get_lang('Moved')));
3525
        }
3526
    } else {
3527
        return $form->returnForm();
3528
    }
3529
}
3530
3531
/**
3532
 * This function displays the form for moving a post message to a different (already existing) or a new thread.
3533
 *
3534
 * @author Patrick Cool <[email protected]>, Ghent University
3535
 *
3536
 * @version february 2006, dokeos 1.8
3537
 */
3538
function move_post_form()
3539
{
3540
    $form = new FormValidator(
3541
        'movepost',
3542
        'post',
3543
        api_get_self().'?'.api_get_cidreq().'&forum='.(int) ($_GET['forum']).'&thread='.(int) ($_GET['thread']).'&post='.Security::remove_XSS($_GET['post']).'&action='.Security::remove_XSS($_GET['action']).'&post='.Security::remove_XSS($_GET['post'])
3544
    );
3545
    // The header for the form
3546
    $form->addElement('header', '', get_lang('Move post'));
3547
3548
    // Invisible form: the post_id
3549
    $form->addElement('hidden', 'post_id', (int) ($_GET['post']));
3550
3551
    // Dropdown list: Threads of this forum
3552
    $threads = get_threads($_GET['forum']);
3553
    $threads_list[0] = get_lang('A new thread');
3554
    foreach ($threads as $thread) {
3555
        $threads_list[$thread->getIid()] = $thread->getThreadTitle();
3556
    }
3557
    $form->addSelect('thread', get_lang('Move toThread'), $threads_list);
3558
    $form->applyFilter('thread', 'html_filter');
3559
3560
    // The OK button
3561
    $form->addButtonSave(get_lang('Move post'), 'submit');
3562
3563
    // Setting the rules
3564
    $form->addRule('thread', get_lang('Required field'), 'required');
3565
3566
    return $form;
3567
}
3568
3569
/**
3570
 * @param array $values
3571
 *
3572
 * @return string HTML language variable
3573
 *
3574
 * @author Patrick Cool <[email protected]>, Ghent University
3575
 *
3576
 * @version february 2006, dokeos 1.8
3577
 */
3578
function store_move_post($values)
3579
{
3580
    $_course = api_get_course_info();
3581
    $course_id = api_get_course_int_id();
3582
3583
    $table_forums = Database::get_course_table(TABLE_FORUM);
3584
    $table_threads = Database::get_course_table(TABLE_FORUM_THREAD);
3585
    $table_posts = Database::get_course_table(TABLE_FORUM_POST);
3586
3587
    $user = api_get_user_entity(api_get_user_id());
3588
    $course = api_get_course_entity($course_id);
3589
    $session = api_get_session_entity();
3590
3591
    if ('0' == $values['thread']) {
3592
        $repoPost = Container::getForumPostRepository();
3593
3594
        /** @var CForumPost $post */
3595
        $post = $repoPost->find($values['post_id']);
3596
        $forumId = $post->getForum()->getIid();
3597
        $threadId = $post->getThread()->getIid();
3598
3599
        $thread = new CForumThread();
3600
        $thread
3601
            ->setThreadTitle($post->getPostTitle())
3602
            ->setForum($post->getForum())
3603
            ->setUser($post->getUser())
3604
            ->setThreadLastPost($post)
3605
            ->setThreadDate($post->getPostDate())
3606
            ->setParent($post->getForum())
3607
            ->addCourseLink(
3608
                $user,
3609
                $course,
3610
                $session
3611
            );
3612
3613
        $repo = Container::getForumThreadRepository();
3614
        $repo->create($thread);
3615
        $new_thread_id = $thread->getIid();
3616
3617
        // Moving the post to the newly created thread.
3618
        $sql = "UPDATE $table_posts SET threadId='".$new_thread_id."', post_parent_id = NULL
3619
                WHERE c_id = $course_id AND iid ='".(int) ($values['post_id'])."'";
3620
        Database::query($sql);
3621
3622
        // Resetting the parent_id of the thread to 0 for all those who had this moved post as parent.
3623
        $sql = "UPDATE $table_posts SET post_parent_id = NULL
3624
                WHERE c_id = $course_id AND post_parent_id='".(int) ($values['post_id'])."'";
3625
        Database::query($sql);
3626
3627
        // Updating updating the number of threads in the forum.
3628
        $sql = "UPDATE $table_forums SET forum_threads=forum_threads+1
3629
                WHERE c_id = $course_id AND iid ='".$forumId."'";
3630
        Database::query($sql);
3631
3632
        // Resetting the last post of the old thread and decreasing the number of replies and the thread.
3633
        $sql = "SELECT * FROM $table_posts
3634
                WHERE c_id = $course_id AND threadId='".$threadId."'
3635
                ORDER BY iid DESC";
3636
        $result = Database::query($sql);
3637
        $row = Database::fetch_array($result);
3638
        $sql = "UPDATE $table_threads SET
3639
                    thread_last_post='".$row['iid']."',
3640
                    thread_replies=thread_replies-1
3641
                WHERE
3642
                    c_id = $course_id AND
3643
                    iid ='".$threadId."'";
3644
        Database::query($sql);
3645
    } else {
3646
        // Moving to the chosen thread.
3647
        $sql = 'SELECT threadId FROM '.$table_posts."
3648
                WHERE c_id = $course_id AND iid = '".$values['post_id']."' ";
3649
        $result = Database::query($sql);
3650
        $row = Database::fetch_array($result);
3651
3652
        $original_thread_id = $row['threadId'];
3653
        $sql = 'SELECT thread_last_post FROM '.$table_threads."
3654
                WHERE c_id = $course_id AND iid = '".$original_thread_id."' ";
3655
3656
        $result = Database::query($sql);
3657
        $row = Database::fetch_array($result);
3658
        $thread_is_last_post = $row['thread_last_post'];
3659
        // If is this thread, update the thread_last_post with the last one.
3660
3661
        if ($thread_is_last_post == $values['post_id']) {
3662
            $sql = 'SELECT iid as post_id FROM '.$table_posts."
3663
                    WHERE
3664
                        c_id = $course_id AND
3665
                        threadId = '".$original_thread_id."' AND
3666
                        iid <> '".$values['post_id']."'
3667
                    ORDER BY post_date DESC LIMIT 1";
3668
            $result = Database::query($sql);
3669
3670
            $row = Database::fetch_array($result);
3671
            $thread_new_last_post = $row['post_id'];
3672
3673
            $sql = 'UPDATE '.$table_threads."
3674
                    SET thread_last_post = '".$thread_new_last_post."'
3675
                    WHERE c_id = $course_id AND iid = '".$original_thread_id."' ";
3676
            Database::query($sql);
3677
        }
3678
3679
        $sql = "UPDATE $table_threads SET thread_replies=thread_replies-1
3680
                WHERE c_id = $course_id AND iid ='".$original_thread_id."'";
3681
        Database::query($sql);
3682
3683
        // moving to the chosen thread
3684
        $sql = "UPDATE $table_posts SET threadId='".(int) ($_POST['thread'])."', post_parent_id = NULL
3685
                WHERE c_id = $course_id AND iid ='".(int) ($values['post_id'])."'";
3686
        Database::query($sql);
3687
3688
        // resetting the parent_id of the thread to 0 for all those who had this moved post as parent
3689
        $sql = "UPDATE $table_posts SET post_parent_id = NULL
3690
                WHERE c_id = $course_id AND post_parent_id='".(int) ($values['post_id'])."'";
3691
        Database::query($sql);
3692
3693
        $sql = "UPDATE $table_threads SET thread_replies=thread_replies+1
3694
                WHERE c_id = $course_id AND iid ='".(int) ($_POST['thread'])."'";
3695
        Database::query($sql);
3696
    }
3697
3698
    return get_lang('Thread moved');
3699
}
3700
3701
/**
3702
 * @param array $values
3703
 *
3704
 * @return string HTML language variable
3705
 *
3706
 * @author Patrick Cool <[email protected]>, Ghent University
3707
 *
3708
 * @version february 2006, dokeos 1.8
3709
 */
3710
function store_move_thread($values)
3711
{
3712
    $table_threads = Database::get_course_table(TABLE_FORUM_THREAD);
3713
    $table_posts = Database::get_course_table(TABLE_FORUM_POST);
3714
3715
    $courseId = api_get_course_int_id();
3716
    $sessionId = api_get_session_id();
3717
3718
    $forumId = (int) ($_POST['forum']);
3719
    $threadId = (int) ($_POST['threadId']);
3720
    //$forumInfo = get_forums($forumId);
3721
3722
    // Change the thread table: Setting the forum_id to the new forum.
3723
    $sql = "UPDATE $table_threads SET forum_id = $forumId
3724
            WHERE c_id = $courseId AND iid = $threadId";
3725
    Database::query($sql);
3726
3727
    // Changing all the posts of the thread: setting the forum_id to the new forum.
3728
    $sql = "UPDATE $table_posts SET forum_id = $forumId
3729
            WHERE c_id = $courseId AND threadId= $threadId";
3730
    Database::query($sql);
3731
3732
    // Fix group id, if forum is moved to a different group
3733
    /*if (!empty($forumInfo['to_group_id'])) {
3734
        $groupId = $forumInfo['to_group_id'];
3735
        $item = api_get_item_property_info(
3736
            $courseId,
3737
            TABLE_FORUM_THREAD,
3738
            $threadId,
3739
            $sessionId,
3740
            $groupId
3741
        );
3742
        $table = Database::get_course_table(TABLE_ITEM_PROPERTY);
3743
        $sessionCondition = api_get_session_condition($sessionId);
3744
3745
        if (!empty($item)) {
3746
            if ($item['to_group_id'] != $groupId) {
3747
                $sql = "UPDATE $table
3748
                    SET to_group_id = $groupId
3749
                    WHERE
3750
                      tool = '".TABLE_FORUM_THREAD."' AND
3751
                      c_id = $courseId AND
3752
                      ref = ".$item['ref']."
3753
                      $sessionCondition
3754
                ";
3755
                Database::query($sql);
3756
            }
3757
        } else {
3758
            $sql = "UPDATE $table
3759
                    SET to_group_id = $groupId
3760
                    WHERE
3761
                      tool = '".TABLE_FORUM_THREAD."' AND
3762
                      c_id = $courseId AND
3763
                      ref = ".$threadId."
3764
                      $sessionCondition
3765
            ";
3766
            Database::query($sql);
3767
        }
3768
    }*/
3769
3770
    return get_lang('Thread moved');
3771
}
3772
3773
/**
3774
 * Prepares a string for displaying by highlighting the search results inside, if any.
3775
 *
3776
 * @param string $input the input string
3777
 *
3778
 * @return string the same string with highlighted hits inside
3779
 *
3780
 * @author Patrick Cool <[email protected]>, Ghent University, February 2006 - the initial version.
3781
 * @author Ivan Tcholakov, March 2011 - adaptation for Chamilo LMS.
3782
 */
3783
function prepare4display($input)
3784
{
3785
    static $highlightcolors = ['yellow', '#33CC33', '#3399CC', '#9999FF', '#33CC33'];
3786
    static $search;
3787
3788
    if (!isset($search)) {
3789
        if (isset($_POST['search_term'])) {
3790
            $search = $_POST['search_term']; // No html at all.
3791
        } elseif (isset($_GET['search'])) {
3792
            $search = $_GET['search'];
3793
        } else {
3794
            $search = '';
3795
        }
3796
    }
3797
3798
    if (!empty($search)) {
3799
        if (strstr($search, '+')) {
3800
            $search_terms = explode('+', $search);
3801
        } else {
3802
            $search_terms[] = trim($search);
3803
        }
3804
        $counter = 0;
3805
        foreach ($search_terms as $key => $search_term) {
3806
            $input = api_preg_replace(
3807
                '/'.preg_quote(trim($search_term), '/').'/i',
3808
                '<span style="background-color: '.$highlightcolors[$counter].'">$0</span>',
3809
                $input
3810
            );
3811
            $counter++;
3812
        }
3813
    }
3814
3815
    // TODO: Security should be implemented outside this function.
3816
    // Change this to COURSEMANAGERLOWSECURITY or COURSEMANAGER to lower filtering and allow more styles
3817
    // (see comments of Security::remove_XSS() method to learn about other levels).
3818
3819
    return Security::remove_XSS($input, STUDENT, true);
3820
}
3821
3822
/**
3823
 * Display the search form for the forum and display the search results.
3824
 *
3825
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
3826
 *
3827
 * @version march 2008, dokeos 1.8.5
3828
 */
3829
function forum_search()
3830
{
3831
    $form = new FormValidator(
3832
        'forumsearch',
3833
        'post',
3834
        'forumsearch.php?'.api_get_cidreq()
3835
    );
3836
3837
    // Setting the form elements.
3838
    $form->addElement('header', '', get_lang('Search in the Forum'));
3839
    $form->addElement('text', 'search_term', get_lang('Search term'), ['autofocus']);
3840
    $form->applyFilter('search_term', 'html_filter');
3841
    $form->addElement('static', 'search_information', '', get_lang('Search in the ForumInformation'));
3842
    $form->addButtonSearch(get_lang('Search'));
3843
3844
    // Setting the rules.
3845
    $form->addRule('search_term', get_lang('Required field'), 'required');
3846
    $form->addRule('search_term', get_lang('Too short'), 'minlength', 3);
3847
3848
    // Validation or display.
3849
    if ($form->validate()) {
3850
        $values = $form->exportValues();
3851
        $form->setDefaults($values);
3852
        $form->display();
3853
        // Display the search results.
3854
        display_forum_search_results($values['search_term']);
3855
    } else {
3856
        $form->display();
3857
    }
3858
}
3859
3860
/**
3861
 * Display the search results.
3862
 *
3863
 * @param string $search_term
3864
 *
3865
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
3866
 *
3867
 * @version march 2008, dokeos 1.8.5
3868
 */
3869
function display_forum_search_results($search_term)
3870
{
3871
    /*$table_threads = Database::get_course_table(TABLE_FORUM_THREAD);
3872
    $table_posts = Database::get_course_table(TABLE_FORUM_POST);
3873
    $table_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
3874
    $session_id = api_get_session_id();
3875
    $course_id = api_get_course_int_id();
3876
3877
    // Defining the search strings as an array.
3878
    if (strstr($search_term, '+')) {
3879
        $search_terms = explode('+', $search_term);
3880
    } else {
3881
        $search_terms[] = $search_term;
3882
    }
3883
3884
    // Search restriction.
3885
    foreach ($search_terms as $value) {
3886
        $search_restriction[] = "
3887
        (
3888
            posts.post_title LIKE '%".Database::escape_string(trim($value))."%' OR
3889
            posts.post_text LIKE '%".Database::escape_string(trim($value))."%'
3890
        )";
3891
    }
3892
3893
    $sessionCondition = api_get_session_condition(
3894
        $session_id,
3895
        true,
3896
        false,
3897
        'item_property.session_id'
3898
    );
3899
3900
    $sql = "SELECT posts.*
3901
            FROM $table_posts posts
3902
            INNER JOIN $table_threads threads
3903
            ON (posts.threadId = threads.threadId AND posts.c_id = threads.c_id)
3904
            INNER JOIN $table_item_property item_property
3905
            ON (item_property.ref = threads.threadId AND item_property.c_id = threads.c_id)
3906
            WHERE
3907
                posts.c_id = $course_id AND
3908
                item_property.c_id = $course_id AND
3909
                item_property.visibility = 1
3910
                $sessionCondition AND
3911
                posts.visible = 1 AND
3912
                item_property.tool = '".TOOL_FORUM_THREAD."' AND
3913
                ".implode(' AND ', $search_restriction).'
3914
            GROUP BY posts.post_id';
3915
3916
    // Getting all the information of the forum categories.
3917
    $forum_categories_list = get_forum_categories();
3918
3919
    // Getting all the information of the forums.
3920
    $forum_list = get_forums();
3921
3922
    $result = Database::query($sql);
3923
    $search_results = [];
3924
    while ($row = Database::fetch_array($result, 'ASSOC')) {
3925
        $forumId = $row['forum_id'];
3926
        $forumData = get_forums($forumId);
3927
        $category = isset($forum_categories_list[$forumData['forum_category']]) ? $forum_categories_list[$forumData['forum_category']] : null;
3928
        $display_result = false;
3929
3930
//          We only show it when
3931
//          1. forum category is visible
3932
//          2. forum is visible
3933
//          3. thread is visible (to do)
3934
//          4. post is visible
3935
3936
        if (!api_is_allowed_to_edit(null, true)) {
3937
            if (!empty($category)) {
3938
                if ('1' == $category['visibility'] && '1' == $forumData['visibility']) {
3939
                    $display_result = true;
3940
                }
3941
            } else {
3942
                if ('1' == $forumData['visible']) {
3943
                    $display_result = true;
3944
                }
3945
            }
3946
        } else {
3947
            $display_result = true;
3948
        }
3949
3950
        if ($display_result) {
3951
            $categoryName = !empty($category) ? $category['cat_title'] : '';
3952
            $search_results_item = '<li><a href="viewforumcategory.php?'.api_get_cidreq().'&forumcategory='.$forumData['forum_category'].'&search='.urlencode($search_term).'">'.
3953
                prepare4display($categoryName).'</a> &gt; ';
3954
            $search_results_item .= '<a href="viewforum.php?'.api_get_cidreq().'&forum='.$forumId.'&search='.urlencode($search_term).'">'.
3955
                prepare4display($forum_list[$row['forum_id']]['forum_title']).'</a> &gt; ';
3956
            $search_results_item .= '<a href="viewthread.php?'.api_get_cidreq().'&forum='.$forumId.'&thread='.$row['threadId'].'&search='.urlencode($search_term).'">'.
3957
                prepare4display($row['post_title']).'</a>';
3958
            $search_results_item .= '<br />';
3959
            if (api_strlen($row['post_title']) > 200) {
3960
                $search_results_item .= prepare4display(api_substr(strip_tags($row['post_title']), 0, 200)).'...';
3961
            } else {
3962
                $search_results_item .= prepare4display($row['post_title']);
3963
            }
3964
            $search_results_item .= '</li>';
3965
            $search_results[] = $search_results_item;
3966
        }
3967
    }
3968
    echo '<legend>'.count($search_results).' '.get_lang('Search in the ForumResults').'</legend>';
3969
    echo '<ol>';
3970
    if ($search_results) {
3971
        echo implode($search_results);
3972
    }
3973
    echo '</ol>';*/
3974
}
3975
3976
/**
3977
 * Return the link to the forum search page.
3978
 *
3979
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
3980
 *
3981
 * @version April 2008, dokeos 1.8.5
3982
 */
3983
function search_link()
3984
{
3985
    // @todo implement search
3986
3987
    return '';
3988
3989
    $return = '';
0 ignored issues
show
Unused Code introduced by
$return = '' is not reachable.

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

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

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

    return false;
}

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

Loading history...
3990
    $origin = api_get_origin();
3991
    if ('learnpath' != $origin) {
3992
        $return = '<a href="forumsearch.php?'.api_get_cidreq().'&action=search"> ';
3993
        $return .= Display::return_icon('search.png', get_lang('Search'), '', ICON_SIZE_MEDIUM).'</a>';
3994
3995
        if (!empty($_GET['search'])) {
3996
            $return .= ': '.Security::remove_XSS($_GET['search']).' ';
3997
            $url = api_get_self().'?';
3998
            $url_parameter = [];
3999
            foreach ($_GET as $key => $value) {
4000
                if ('search' != $key) {
4001
                    $url_parameter[] = Security::remove_XSS($key).'='.Security::remove_XSS($value);
4002
                }
4003
            }
4004
            $url .= implode('&', $url_parameter);
4005
            $return .= '<a href="'.$url.'">'.Display::return_icon('delete.gif', get_lang('Clean search results')).'</a>';
4006
        }
4007
    }
4008
4009
    return $return;
4010
}
4011
4012
/**
4013
 * This function adds an attachment file into a forum.
4014
 *
4015
 * @param string     $file_comment a comment about file
4016
 * @param CForumPost $post         from forum_post table
4017
 *
4018
 * @return false|null
4019
 */
4020
function add_forum_attachment_file($file_comment, CForumPost $post)
4021
{
4022
    $request = Container::getRequest();
4023
    if (false === $request->files->has('user_upload')) {
4024
        return false;
4025
    }
4026
4027
    $file = $request->files->get('user_upload');
4028
    if (empty($file)) {
4029
        return false;
4030
    }
4031
4032
    $files = [];
4033
    if ($file instanceof UploadedFile) {
4034
        $files[] = $file;
4035
    } else {
4036
        $files = $file;
4037
    }
4038
4039
    /** @var UploadedFile $attachment */
4040
    foreach ($files as $file) {
4041
        $valid = process_uploaded_file($file);
4042
4043
        if (!$valid) {
4044
            continue;
4045
        }
4046
4047
        // Try to add an extension to the file if it hasn't one.
4048
        $new_file_name = add_ext_on_mime(
4049
            stripslashes($file->getPathname()),
4050
            $file->getType()
4051
        );
4052
4053
        // User's file name
4054
        $file_name = $file->getClientOriginalName();
4055
4056
        if (!filter_extension($new_file_name)) {
4057
            Display::addFlash(
4058
                Display::return_message(
4059
                    get_lang('File upload failed: this file extension or file type is prohibited'),
4060
                    'error'
4061
                )
4062
            );
4063
4064
            return;
4065
        }
4066
4067
        $safe_file_comment = Database::escape_string($file_comment);
4068
4069
        $user = api_get_user_entity(api_get_user_id());
4070
        $course = api_get_course_entity(api_get_course_int_id());
4071
        $session = api_get_session_entity(api_get_session_id());
4072
4073
        $attachment = (new CForumAttachment())
4074
            ->setCId(api_get_course_int_id())
4075
            ->setComment($safe_file_comment)
4076
            ->setFilename($file_name)
4077
            ->setPath($file_name)
4078
            ->setPost($post)
4079
            ->setSize($file->getSize())
4080
            ->setParent($post)
4081
            ->addCourseLink(
4082
                $course,
4083
                $session
4084
            );
4085
4086
        $repo = Container::getForumAttachmentRepository();
4087
        $repo->create($attachment);
4088
        $repo->addFile($attachment, $file);
4089
        $repo->update($attachment);
4090
    }
4091
}
4092
4093
/**
4094
 * This function edits an attachment file into a forum.
4095
 *
4096
 * @param string $file_comment a comment about file
4097
 * @param int    $post_id
4098
 * @param int    $id_attach    attachment file Id
4099
 */
4100
function edit_forum_attachment_file($file_comment, $post_id, $id_attach)
4101
{
4102
    throw new Exception('edit_forum_attachment_file');
4103
    /*
4104
    $_course = api_get_course_info();
4105
    $table_forum_attachment = Database::get_course_table(TABLE_FORUM_ATTACHMENT);
4106
    $course_id = api_get_course_int_id();
4107
4108
    $filesData = [];
4109
4110
    if (!is_array($_FILES['user_upload']['name'])) {
4111
        $filesData[] = $_FILES['user_upload'];
4112
    } else {
4113
        $fileCount = count($_FILES['user_upload']['name']);
4114
        $fileKeys = array_keys($_FILES['user_upload']);
4115
4116
        for ($i = 0; $i < $fileCount; $i++) {
4117
            foreach ($fileKeys as $key) {
4118
                $filesData[$i][$key] = $_FILES['user_upload'][$key][$i];
4119
            }
4120
        }
4121
    }
4122
4123
    foreach ($filesData as $attachment) {
4124
        if (empty($attachment['name'])) {
4125
            continue;
4126
        }
4127
4128
        $upload_ok = process_uploaded_file($attachment);
4129
4130
        if (!$upload_ok) {
4131
            continue;
4132
        }
4133
4134
        $course_dir = $_course['path'].'/upload/forum';
4135
        $sys_course_path = api_get_path(SYS_COURSE_PATH);
4136
        $updir = $sys_course_path.$course_dir;
4137
4138
        // Try to add an extension to the file if it hasn't one.
4139
        $new_file_name = add_ext_on_mime(stripslashes($attachment['name']), $attachment['type']);
4140
        // User's file name
4141
        $file_name = $attachment['name'];
4142
4143
        if (!filter_extension($new_file_name)) {
4144
            Display::addFlash(
4145
                Display::return_message(
4146
                    get_lang('File upload failed: this file extension or file type is prohibited'),
4147
                    'error'
4148
                )
4149
            );
4150
        } else {
4151
            $new_file_name = uniqid('');
4152
            $new_path = $updir.'/'.$new_file_name;
4153
            $result = @move_uploaded_file($attachment['tmp_name'], $new_path);
4154
            $safe_file_comment = Database::escape_string($file_comment);
4155
            $safe_file_name = Database::escape_string($file_name);
4156
            $safe_new_file_name = Database::escape_string($new_file_name);
4157
            $safe_post_id = (int) $post_id;
4158
            $safe_id_attach = (int) $id_attach;
4159
            // Storing the attachments if any.
4160
            if ($result) {
4161
                $sql = "UPDATE $table_forum_attachment
4162
                        SET
4163
                            filename = '$safe_file_name',
4164
                            comment = '$safe_file_comment',
4165
                            path = '$safe_new_file_name',
4166
                            post_id = '$safe_post_id',
4167
                            size ='".$attachment['size']."'
4168
                        WHERE c_id = $course_id AND id = '$safe_id_attach'";
4169
                Database::query($sql);
4170
                api_item_property_update(
4171
                    $_course,
4172
                    TOOL_FORUM_ATTACH,
4173
                    $safe_id_attach,
4174
                    'ForumAttachmentUpdated',
4175
                    api_get_user_id()
4176
                );
4177
            }
4178
        }
4179
    }*/
4180
}
4181
4182
/**
4183
 * Show a list with all the attachments according to the post's id.
4184
 *
4185
 * @param int $postId
4186
 *
4187
 * @return array with the post info
4188
 *
4189
 * @author Julio Montoya
4190
 *
4191
 * @version avril 2008, dokeos 1.8.5
4192
 */
4193
function get_attachment($postId)
4194
{
4195
    $table = Database::get_course_table(TABLE_FORUM_ATTACHMENT);
4196
    $course_id = api_get_course_int_id();
4197
    $row = [];
4198
    $postId = (int) $postId;
4199
4200
    if (empty($postId)) {
4201
        return [];
4202
    }
4203
4204
    $sql = "SELECT iid, path, filename, comment
4205
            FROM $table
4206
            WHERE c_id = $course_id AND post_id = $postId";
4207
    $result = Database::query($sql);
4208
    if (0 != Database::num_rows($result)) {
4209
        $row = Database::fetch_array($result);
4210
    }
4211
4212
    return $row;
4213
}
4214
4215
/**
4216
 * Delete the all the attachments from the DB and the file according to the post's id or attach id(optional).
4217
 *
4218
 * @param int $postId
4219
 * @param int $attachmentId
4220
 *
4221
 * @return bool
4222
 */
4223
function delete_attachment($postId, $attachmentId)
4224
{
4225
    $repo = Container::getForumPostRepository();
4226
    $em = Database::getManager();
4227
    /** @var CForumPost $post */
4228
    $post = $repo->find($postId);
4229
    if ($post) {
4230
        $repoAttachment = Container::getForumAttachmentRepository();
4231
        $attachment = $repoAttachment->find($attachmentId);
4232
        if ($attachment) {
4233
            $post->removeAttachment($attachment);
4234
            $em->remove($attachment);
4235
        }
4236
        $repo->update($post);
4237
4238
        Display::addFlash(Display::return_message(get_lang('The attached file has been deleted'), 'confirmation'));
4239
    }
4240
4241
    return true;
4242
}
4243
4244
/**
4245
 * This function gets all the forum information of the all the forum of the group.
4246
 *
4247
 * @param array $groupInfo the id of the group we need the fora of (see forum.forum_of_group)
4248
 *
4249
 * @return CForum[]
4250
 *
4251
 * @todo this is basically the same code as the get_forums function. Consider merging the two.
4252
 */
4253
function get_forums_of_group(CGroup $group)
4254
{
4255
    $course = api_get_course_entity();
4256
    $session = api_get_session_entity();
4257
    $repo = Container::getForumRepository();
4258
    $qb = $repo->getResourcesByCourse($course, $session, $group);
4259
4260
    return $qb->getQuery()->getResult();
4261
}
4262
4263
/**
4264
 * This function stores which users have to be notified of which forums or threads.
4265
 *
4266
 * @param string $content    does the user want to be notified about a forum or about a thread
4267
 * @param int    $id         the id of the forum or thread
4268
 * @param bool   $addOnly
4269
 * @param array  $userInfo
4270
 * @param array  $courseInfo
4271
 *
4272
 * @return string language variable
4273
 *
4274
 * @author  Patrick Cool <[email protected]>, Ghent University, Belgium
4275
 * @author  Julio Montoya
4276
 *
4277
 * @since   May 2008 v1.8.5
4278
 */
4279
function set_notification($content, $id, $addOnly = false, $userInfo = [], $courseInfo = [])
4280
{
4281
    $userInfo = empty($userInfo) ? api_get_user_info() : $userInfo;
4282
    $courseInfo = empty($courseInfo) ? api_get_course_info() : $courseInfo;
4283
    $id = (int) $id;
4284
4285
    if (empty($userInfo) || empty($courseInfo) || empty($id) || empty($content)) {
4286
        return false;
4287
    }
4288
4289
    // Database table definition
4290
    $table_notification = Database::get_course_table(TABLE_FORUM_NOTIFICATION);
4291
    $course_id = $courseInfo['real_id'];
4292
4293
    // Which database field do we have to store the id in?
4294
    $field = 'threadId';
4295
    if ('forum' === $content) {
4296
        $field = 'forum_id';
4297
    }
4298
4299
    $userId = $userInfo['user_id'];
4300
4301
    // First we check if the notification is already set for this.
4302
    $sql = "SELECT * FROM $table_notification
4303
            WHERE
4304
                c_id = $course_id AND
4305
                $field = $id AND
4306
                user_id = $userId ";
4307
    $result = Database::query($sql);
4308
    $total = Database::num_rows($result);
4309
4310
    // If the user did not indicate that (s)he wanted to be notified already
4311
    // then we store the notification request (to prevent double notification requests).
4312
    if ($total <= 0) {
4313
        $notification = new CForumNotification();
4314
        $notification
4315
            ->setCId($course_id)
4316
            ->setUserId($userId)
4317
        ;
4318
4319
        if ('forum' === $content) {
4320
            $notification->setForumId($id);
4321
        } else {
4322
            $notification->setThreadId($id);
4323
        }
4324
4325
        $em = Database::getManager();
4326
        $em->persist($notification);
4327
        $em->flush();
4328
4329
        Session::erase('forum_notification');
4330
        getNotificationsPerUser(0, true);
4331
4332
        return get_lang('You will be notified of new posts by e-mail.');
4333
    } else {
4334
        if (!$addOnly) {
4335
            $sql = "DELETE FROM $table_notification
4336
                    WHERE
4337
                        c_id = $course_id AND
4338
                        $field = $id AND
4339
                        user_id = $userId ";
4340
            Database::query($sql);
4341
            Session::erase('forum_notification');
4342
            getNotificationsPerUser(0, true);
4343
4344
            return get_lang('You will no longer be notified of new posts by email');
4345
        }
4346
    }
4347
}
4348
4349
/**
4350
 * This function retrieves all the email adresses of the users who wanted to be notified
4351
 * about a new post in a certain forum or thread.
4352
 *
4353
 * @param string $content does the user want to be notified about a forum or about a thread
4354
 * @param int    $id      the id of the forum or thread
4355
 *
4356
 * @return array returns
4357
 *
4358
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
4359
 * @author Julio Montoya
4360
 *
4361
 * @version May 2008, dokeos 1.8.5
4362
 *
4363
 * @since May 2008, dokeos 1.8.5
4364
 */
4365
function get_notifications($content, $id)
4366
{
4367
    // Database table definition
4368
    $table_users = Database::get_main_table(TABLE_MAIN_USER);
4369
    $table_notification = Database::get_course_table(TABLE_FORUM_NOTIFICATION);
4370
    $course_id = api_get_course_int_id();
4371
4372
    // Which database field contains the notification?
4373
    $field = 'threadId';
4374
    if ('forum' === $content) {
4375
        $field = 'forum_id';
4376
    }
4377
4378
    $id = (int) $id;
4379
4380
    $sql = "SELECT user.id as user_id, user.firstname, user.lastname, user.email, user.id user
4381
            FROM $table_users user, $table_notification notification
4382
            WHERE
4383
                notification.c_id = $course_id AND user.active = 1 AND
4384
                user.id = notification.user_id AND
4385
                notification.$field = $id ";
4386
4387
    $result = Database::query($sql);
4388
    $return = [];
4389
4390
    while ($row = Database::fetch_array($result)) {
4391
        $return['user'.$row['user_id']] = ['email' => $row['email'], 'user_id' => $row['user_id']];
4392
    }
4393
4394
    return $return;
4395
}
4396
4397
/**
4398
 * Get all the users who need to receive a notification of a new post (those subscribed to
4399
 * the forum or the thread).
4400
 *
4401
 * @param int $post_id the id of the post
4402
 *
4403
 * @return false|null
4404
 *
4405
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
4406
 *
4407
 * @version May 2008, dokeos 1.8.5
4408
 *
4409
 * @since May 2008, dokeos 1.8.5
4410
 */
4411
function send_notifications(CForum $forum, CForumThread $thread, $post_id = 0)
4412
{
4413
    if (!$forum) {
4414
        return false;
4415
    }
4416
4417
    // Users who subscribed to the forum
4418
    $users_to_be_notified_by_forum = get_notifications('forum', $forum->getIid());
4419
4420
    // User who subscribed to the thread
4421
    $users_to_be_notified_by_thread = [];
4422
    if (!$thread) {
4423
        $users_to_be_notified_by_thread = get_notifications('thread', $thread->getIid());
4424
    }
4425
4426
    $postInfo = null;
4427
    if (!empty($post_id)) {
4428
        $postInfo = Container::getForumPostRepository()->find($post_id);
4429
    }
4430
4431
    // Merging the two
4432
    $users_to_be_notified = array_merge($users_to_be_notified_by_forum, $users_to_be_notified_by_thread);
4433
4434
    if (is_array($users_to_be_notified)) {
4435
        foreach ($users_to_be_notified as $value) {
4436
            $userInfo = api_get_user_info($value['user_id']);
4437
            send_mail($userInfo, $forum, $thread, $postInfo);
4438
        }
4439
    }
4440
}
4441
4442
/**
4443
 * Get all the notification subscriptions of the user
4444
 * = which forums and which threads does the user wants to be informed of when a new
4445
 * post is added to this thread.
4446
 *
4447
 * @param int  $user_id the user_id of a user (default = 0 => the current user)
4448
 * @param bool $force   force get the notification subscriptions (even if the information is already in the session
4449
 *
4450
 * @return array
4451
 *
4452
 * @author Patrick Cool <[email protected]>, Ghent University, Belgium
4453
 *
4454
 * @version May 2008, dokeos 1.8.5
4455
 *
4456
 * @since May 2008, dokeos 1.8.5
4457
 */
4458
function getNotificationsPerUser($user_id = 0, $force = false, $course_id = 0)
4459
{
4460
    // Database table definition
4461
    $table_notification = Database::get_course_table(TABLE_FORUM_NOTIFICATION);
4462
    $course_id = empty($course_id) ? api_get_course_int_id() : (int) $course_id;
4463
    if (empty($course_id) || -1 == $course_id) {
4464
        return null;
4465
    }
4466
4467
    $user_id = empty($user_id) ? api_get_user_id() : (int) $user_id;
4468
4469
    if (!isset($_SESSION['forum_notification']) ||
4470
        $_SESSION['forum_notification']['course'] != $course_id ||
4471
        true == $force
4472
    ) {
4473
        $_SESSION['forum_notification']['course'] = $course_id;
4474
        $sql = "SELECT * FROM $table_notification
4475
                WHERE c_id = $course_id AND user_id='".$user_id."'";
4476
4477
        $result = Database::query($sql);
4478
        while ($row = Database::fetch_array($result)) {
4479
            if (null !== $row['forum_id']) {
4480
                $_SESSION['forum_notification']['forum'][] = $row['forum_id'];
4481
            }
4482
            if (null !== $row['threadId']) {
4483
                $_SESSION['forum_notification']['thread'][] = $row['threadId'];
4484
            }
4485
        }
4486
    }
4487
}
4488
4489
/**
4490
 * This function counts the number of post inside a thread.
4491
 *
4492
 * @param int $thread_id
4493
 *
4494
 * @return int the number of post inside a thread
4495
 *
4496
 * @author Jhon Hinojosa <[email protected]>,
4497
 *
4498
 * @version octubre 2008, dokeos 1.8
4499
 */
4500
function count_number_of_post_in_thread($thread_id)
4501
{
4502
    $table_posts = Database::get_course_table(TABLE_FORUM_POST);
4503
    $course_id = api_get_course_int_id();
4504
    if (empty($course_id)) {
4505
        return 0;
4506
    }
4507
    $sql = "SELECT count(*) count FROM $table_posts
4508
            WHERE
4509
                c_id = $course_id AND
4510
                threadId='".(int) $thread_id."' ";
4511
    $result = Database::query($sql);
4512
4513
    $count = 0;
4514
    if (Database::num_rows($result) > 0) {
4515
        $row = Database::fetch_array($result);
4516
        $count = $row['count'];
4517
    }
4518
4519
    return $count;
4520
}
4521
4522
/**
4523
 * This function counts the number of post inside a thread user.
4524
 *
4525
 * @param int $thread_id
4526
 * @param int $user_id
4527
 *
4528
 * @return int the number of post inside a thread user
4529
 */
4530
function count_number_of_post_for_user_thread($thread_id, $user_id)
4531
{
4532
    $table_posts = Database::get_course_table(TABLE_FORUM_POST);
4533
    $sql = "SELECT count(iid) as count
4534
            FROM $table_posts
4535
            WHERE
4536
                  threadId=".(int) $thread_id.' AND
4537
                  poster_id = '.(int) $user_id.' AND
4538
                  visible = 1 ';
4539
    $result = Database::query($sql);
4540
    $count = 0;
4541
    if (Database::num_rows($result) > 0) {
4542
        $count = Database::fetch_array($result);
4543
        $count = $count['count'];
4544
    }
4545
4546
    return $count;
4547
}
4548
4549
/**
4550
 * This function retrieves information of statistical.
4551
 *
4552
 * @param int $thread_id
4553
 * @param int $user_id
4554
 * @param int $course_id
4555
 *
4556
 * @return array the information of statistical
4557
 *
4558
 * @author Jhon Hinojosa <[email protected]>,
4559
 *
4560
 * @version oct 2008, dokeos 1.8
4561
 */
4562
function get_statistical_information($thread_id, $user_id, $course_id)
4563
{
4564
    $result = [];
4565
    $courseInfo = api_get_course_info_by_id($course_id);
4566
    $result['user_course'] = CourseManager::get_users_count_in_course($courseInfo['code']);
4567
    $result['post'] = count_number_of_post_in_thread($thread_id);
4568
    $result['user_post'] = count_number_of_post_for_user_thread($thread_id, $user_id);
4569
4570
    return $result;
4571
}
4572
4573
/**
4574
 * This function return the posts inside a thread from a given user.
4575
 *
4576
 * @param int $thread_id
4577
 * @param int $user_id
4578
 *
4579
 * @return array posts inside a thread
4580
 *
4581
 * @author  Jhon Hinojosa <[email protected]>,
4582
 *
4583
 * @version oct 2008, dokeos 1.8
4584
 */
4585
function get_thread_user_post(Course $course, $thread_id, $user_id)
4586
{
4587
    $table_posts = Database::get_course_table(TABLE_FORUM_POST);
4588
    $table_users = Database::get_main_table(TABLE_MAIN_USER);
4589
    $thread_id = (int) $thread_id;
4590
    $user_id = (int) $user_id;
4591
    $course_id = $course->getId();
4592
4593
    if (empty($course_id)) {
4594
        $course_id = api_get_course_int_id();
4595
    }
4596
    $sql = "SELECT *, user.id as user_id FROM $table_posts posts
4597
            LEFT JOIN $table_users user
4598
            ON posts.poster_id = user.id
4599
            WHERE
4600
                posts.c_id = $course_id AND
4601
                posts.threadId='$thread_id' AND
4602
                posts.poster_id='$user_id'
4603
            ORDER BY posts.iid ASC";
4604
4605
    $result = Database::query($sql);
4606
    $post_list = [];
4607
    while ($row = Database::fetch_array($result)) {
4608
        $row['status'] = '1';
4609
        $post_list[] = $row;
4610
        $sql = "SELECT * FROM $table_posts posts
4611
                LEFT JOIN $table_users users
4612
                ON (posts.poster_id=users.id)
4613
                WHERE
4614
                    posts.c_id = $course_id AND
4615
                    posts.threadId='$thread_id'
4616
                    AND posts.post_parent_id='".$row['iid']."'
4617
                ORDER BY posts.iid ASC";
4618
        $result2 = Database::query($sql);
4619
        while ($row2 = Database::fetch_array($result2)) {
4620
            $row2['status'] = '0';
4621
            $post_list[] = $row2;
4622
        }
4623
    }
4624
4625
    return $post_list;
4626
}
4627
4628
/**
4629
 * This function get the name of an thread by id.
4630
 *
4631
 * @param int $thread_id
4632
 *
4633
 * @return string
4634
 *
4635
 * @author Christian Fasanando
4636
 * @author Julio Montoya <[email protected]> Adding security
4637
 */
4638
function get_name_thread_by_id($thread_id)
4639
{
4640
    $t_forum_thread = Database::get_course_table(TABLE_FORUM_THREAD);
4641
    $course_id = api_get_course_int_id();
4642
    $sql = "SELECT thread_title
4643
            FROM $t_forum_thread
4644
            WHERE c_id = $course_id AND iid = '".(int) $thread_id."' ";
4645
    $result = Database::query($sql);
4646
    $row = Database::fetch_array($result);
4647
4648
    return $row[0];
4649
}
4650
4651
/**
4652
 * This function gets all the post written by an user.
4653
 *
4654
 * @param int $user_id
4655
 *
4656
 * @return string
4657
 */
4658
function get_all_post_from_user($user_id, $courseId)
4659
{
4660
    $j = 0;
4661
    $forums = get_forums($courseId);
4662
    krsort($forums);
4663
    $forum_results = '';
4664
4665
    foreach ($forums as $forum) {
4666
        $forumId = $forum->getIid();
4667
4668
        /*if (0 == $forum['visibility']) {
4669
            continue;
4670
        }*/
4671
        if ($j <= 4) {
4672
            $threads = get_threads($forumId);
4673
4674
            if ($threads) {
4675
                $i = 0;
4676
                $hand_forums = '';
4677
                $post_counter = 0;
4678
                foreach ($threads as $thread) {
4679
                    /*if (0 == $thread['visibility']) {
4680
                        continue;
4681
                    }*/
4682
                    if ($i <= 4) {
4683
                        $post_list = get_thread_user_post_limit(
4684
                            $courseId,
4685
                            $thread->getIid(),
4686
                            $user_id,
4687
                            1
4688
                        );
4689
                        $post_counter = count($post_list);
4690
                        if (is_array($post_list) && count($post_list) > 0) {
4691
                            $hand_forums .= '<div id="social-thread">';
4692
                            $hand_forums .= Display::return_icon(
4693
                                'thread.png',
4694
                                get_lang('Thread'),
4695
                                '',
4696
                                ICON_SIZE_MEDIUM
4697
                            );
4698
                            $hand_forums .= '&nbsp;'.Security::remove_XSS($thread->getThreadTitle(), STUDENT);
4699
                            $hand_forums .= '</div>';
4700
4701
                            foreach ($post_list as $posts) {
4702
                                $hand_forums .= '<div id="social-post">';
4703
                                $hand_forums .= '<strong>'.Security::remove_XSS($posts['post_title'], STUDENT).'</strong>';
4704
                                $hand_forums .= '<br / >';
4705
                                $hand_forums .= Security::remove_XSS($posts['post_text'], STUDENT);
4706
                                $hand_forums .= '</div>';
4707
                                $hand_forums .= '<br / >';
4708
                            }
4709
                        }
4710
                    }
4711
                    $i++;
4712
                }
4713
                $forum_results .= '<div id="social-forum">';
4714
                $forum_results .= '<div class="clear"></div><br />';
4715
                $forum_results .= '<div id="social-forum-title">'.
4716
                    Display::return_icon('forum.gif', get_lang('Forum')).'&nbsp;'.Security::remove_XSS($forum->getForumTitle(), STUDENT).
4717
                    '<div style="float:right;margin-top:-35px">
4718
                        <a href="../forum/viewforum.php?'.api_get_cidreq_params($courseId).'&forum='.$forum->getIid().' " >'.
4719
                    get_lang('See forum').'
4720
                        </a>
4721
                     </div></div>';
4722
                $forum_results .= '<br / >';
4723
                if ($post_counter > 0) {
4724
                    $forum_results .= $hand_forums;
4725
                }
4726
                $forum_results .= '</div>';
4727
            }
4728
            $j++;
4729
        }
4730
    }
4731
4732
    return $forum_results;
4733
}
4734
4735
/**
4736
 * @param int $thread_id
4737
 * @param int $user_id
4738
 * @param int $limit
4739
 *
4740
 * @return array
4741
 */
4742
function get_thread_user_post_limit($courseId, $thread_id, $user_id, $limit = 10)
4743
{
4744
    $table_posts = Database::get_course_table(TABLE_FORUM_POST);
4745
    $table_users = Database::get_main_table(TABLE_MAIN_USER);
4746
4747
    $courseId = (int) $courseId;
4748
    $limit = (int) $limit;
4749
4750
    $sql = "SELECT * FROM $table_posts posts
4751
            LEFT JOIN $table_users users
4752
                ON posts.poster_id=users.id
4753
            WHERE
4754
                posts.c_id = $courseId AND
4755
                posts.threadId='".Database::escape_string($thread_id)."' AND
4756
                posts.poster_id='".Database::escape_string($user_id)."'
4757
            ORDER BY posts.post_id DESC LIMIT $limit ";
4758
    $result = Database::query($sql);
4759
    $post_list = [];
4760
    while ($row = Database::fetch_array($result)) {
4761
        $row['status'] = '1';
4762
        $post_list[] = $row;
4763
    }
4764
4765
    return $post_list;
4766
}
4767
4768
/**
4769
 * @param string $userId
4770
 * @param array  $courseInfo
4771
 * @param int    $sessionId
4772
 *
4773
 * @return array
4774
 */
4775
function getForumCreatedByUser($userId, $courseInfo, $sessionId)
4776
{
4777
    if (empty($userId) || empty($courseInfo)) {
4778
        return [];
4779
    }
4780
4781
    $courseId = $courseInfo['real_id'];
4782
4783
    $repo = Container::getForumRepository();
4784
4785
    $courseEntity = api_get_course_entity($courseId);
4786
    $sessionEntity = api_get_session_entity($sessionId);
4787
4788
    $qb = $repo->getResourcesByCourse($courseEntity, $sessionEntity);
4789
4790
    $qb->andWhere('node.creator = :creator');
4791
    $qb->setParameter('creator', $userId);
4792
    $items = $qb->getQuery()->getResult();
4793
4794
    $forumList = [];
4795
    if (!empty($items)) {
4796
        /** @var CForum $forum */
4797
        foreach ($items as $forum) {
4798
            $forumList[] = [
4799
                $forum->getForumTitle(),
4800
                api_get_local_time($forum->getResourceNode()->getCreatedAt()),
4801
                api_get_local_time($forum->getResourceNode()->getUpdatedAt()),
4802
            ];
4803
        }
4804
    }
4805
4806
    return $forumList;
4807
}
4808
4809
/**
4810
 * This function builds an array of all the posts in a given thread
4811
 * where the key of the array is the post_id
4812
 * It also adds an element children to the array which itself is an array
4813
 * that contains all the id's of the first-level children.
4814
 *
4815
 * @return array containing all the information on the posts of a thread
4816
 *
4817
 * @author Patrick Cool <[email protected]>, Ghent University
4818
 */
4819
function calculate_children($rows)
4820
{
4821
    $sorted_rows = [0 => []];
4822
    if (!empty($rows)) {
4823
        foreach ($rows as $row) {
4824
            $rows_with_children[$row['post_id']] = $row;
4825
            $rows_with_children[$row['post_parent_id']]['children'][] = $row['post_id'];
4826
        }
4827
4828
        $rows = $rows_with_children;
4829
        forumRecursiveSort($rows, $sorted_rows);
4830
        unset($sorted_rows[0]);
4831
    }
4832
4833
    return $sorted_rows;
4834
}
4835
4836
/**
4837
 * @param $rows
4838
 * @param $threads
4839
 * @param int $seed
4840
 * @param int $indent
4841
 */
4842
function forumRecursiveSort($rows, &$threads, $seed = 0, $indent = 0)
4843
{
4844
    if ($seed > 0) {
4845
        $threads[$rows[$seed]['post_id']] = $rows[$seed];
4846
        $threads[$rows[$seed]['post_id']]['indent_cnt'] = $indent;
4847
        $indent++;
4848
    }
4849
4850
    if (isset($rows[$seed]['children'])) {
4851
        foreach ($rows[$seed]['children'] as $child) {
4852
            forumRecursiveSort($rows, $threads, $child, $indent);
4853
        }
4854
    }
4855
}
4856
4857
/**
4858
 * Update forum attachment data, used to update comment and post ID.
4859
 *
4860
 * @param array $array    (field => value) to update forum attachment row
4861
 * @param int   $id       ID to find row to update
4862
 * @param int   $courseId course ID to find row to update
4863
 *
4864
 * @return int number of affected rows
4865
 */
4866
function editAttachedFile($array, $id, $courseId = null)
4867
{
4868
    // Init variables
4869
    $setString = '';
4870
    $id = (int) $id;
4871
    $courseId = (int) $courseId;
4872
    if (empty($courseId)) {
4873
        // $courseId can be null, use api method
4874
        $courseId = api_get_course_int_id();
4875
    }
4876
    /*
4877
     * Check if Attachment ID and Course ID are greater than zero
4878
     * and array of field values is not empty
4879
     */
4880
    if ($id > 0 && $courseId > 0 && !empty($array) && is_array($array)) {
4881
        foreach ($array as $key => &$item) {
4882
            $item = Database::escape_string($item);
4883
            $setString .= $key.' = "'.$item.'", ';
4884
        }
4885
        // Delete last comma
4886
        $setString = substr($setString, 0, strlen($setString) - 2);
4887
        $forumAttachmentTable = Database::get_course_table(TABLE_FORUM_ATTACHMENT);
4888
        $sql = "UPDATE $forumAttachmentTable
4889
                SET $setString WHERE c_id = $courseId AND id = $id";
4890
        $result = Database::query($sql);
4891
        if (false !== $result) {
4892
            $affectedRows = Database::affected_rows($result);
4893
            if ($affectedRows > 0) {
4894
                /*
4895
                 * If exist in $_SESSION variable, then delete them from it
4896
                 * because they would be deprecated
4897
                 */
4898
                if (!empty($_SESSION['forum']['upload_file'][$courseId][$id])) {
4899
                    unset($_SESSION['forum']['upload_file'][$courseId][$id]);
4900
                }
4901
            }
4902
4903
            return $affectedRows;
4904
        }
4905
    }
4906
4907
    return 0;
4908
}
4909
4910
/**
4911
 * Return a table where the attachments will be set.
4912
 *
4913
 * @param int $postId Forum Post ID
4914
 *
4915
 * @return string The Forum Attachments Ajax Table
4916
 */
4917
function getAttachmentsAjaxTable($postId = 0)
4918
{
4919
    $postId = (int) $postId;
4920
    $courseId = api_get_course_int_id();
4921
    $attachIds = getAttachmentIdsByPostId($postId, $courseId);
4922
    $fileDataContent = '';
4923
    // Update comment to show if form did not pass validation
4924
    if (!empty($_REQUEST['file_ids']) && is_array($_REQUEST['file_ids'])) {
4925
        // 'file_ids is the name from forum attachment ajax form
4926
        foreach ($_REQUEST['file_ids'] as $key => $attachId) {
4927
            if (!empty($_SESSION['forum']['upload_file'][$courseId][$attachId]) &&
4928
                is_array($_SESSION['forum']['upload_file'][$courseId][$attachId])
4929
            ) {
4930
                // If exist forum attachment then update into $_SESSION data
4931
                $_SESSION['forum']['upload_file'][$courseId][$attachId]['comment'] = $_POST['file_comments'][$key];
4932
            }
4933
        }
4934
    }
4935
4936
    // Get data to fill into attachment files table
4937
    if (!empty($_SESSION['forum']['upload_file'][$courseId]) &&
4938
        is_array($_SESSION['forum']['upload_file'][$courseId])
4939
    ) {
4940
        $uploadedFiles = $_SESSION['forum']['upload_file'][$courseId];
4941
        foreach ($uploadedFiles as $k => $uploadedFile) {
4942
            if (!empty($uploadedFile) && in_array($uploadedFile['id'], $attachIds)) {
4943
                // Buil html table including an input with attachmentID
4944
                $fileDataContent .= '<tr id="'.$uploadedFile['id'].'" ><td>'.$uploadedFile['name'].'</td><td>'.$uploadedFile['size'].'</td><td>&nbsp;'.$uploadedFile['result'].
4945
                    ' </td><td> <input style="width:90%;" type="text" value="'.$uploadedFile['comment'].'" name="file_comments[]"> </td><td>'.
4946
                    $uploadedFile['delete'].'</td>'.
4947
                    '<input type="hidden" value="'.$uploadedFile['id'].'" name="file_ids[]">'.'</tr>';
4948
            } else {
4949
                /*
4950
                 * If attachment data is empty, then delete it from $_SESSION
4951
                 * because could generate and empty row into html table
4952
                 */
4953
                unset($_SESSION['forum']['upload_file'][$courseId][$k]);
4954
            }
4955
        }
4956
    }
4957
    $style = empty($fileDataContent) ? 'display: none;' : '';
4958
    // Forum attachment Ajax table
4959
    return '
4960
    <div class="control-group " style="'.$style.'">
4961
        <label class="control-label">'.get_lang('Attachments list').'</label>
4962
        <div class="controls">
4963
            <table id="attachmentFileList" class="files data_table span10">
4964
                <tr>
4965
                    <th>'.get_lang('Filename').'</th>
4966
                    <th>'.get_lang('Size').'</th>
4967
                    <th>'.get_lang('Status').'</th>
4968
                    <th>'.get_lang('Comment').'</th>
4969
                    <th>'.get_lang('Delete').'</th>
4970
                </tr>
4971
                '.$fileDataContent.'
4972
            </table>
4973
        </div>
4974
    </div>';
4975
}
4976
4977
/**
4978
 * Return an array of prepared attachment data to build forum attachment table
4979
 * Also, save this array into $_SESSION to do available the attachment data.
4980
 *
4981
 * @param int $forumId
4982
 * @param int $threadId
4983
 * @param int $postId
4984
 * @param int $attachId
4985
 * @param int $courseId
4986
 *
4987
 * @return array
4988
 */
4989
function getAttachedFiles(
4990
    $forumId,
4991
    $threadId,
4992
    $postId = 0,
4993
    $attachId = 0,
4994
    $courseId = 0
4995
) {
4996
    $forumId = (int) $forumId;
4997
    $courseId = (int) $courseId;
4998
    $attachId = (int) $attachId;
4999
    $postId = (int) $postId;
5000
    $threadId = (int) $threadId;
5001
5002
    if (empty($courseId)) {
5003
        // $courseId can be null, use api method
5004
        $courseId = api_get_course_int_id();
5005
    }
5006
    if (empty($forumId)) {
5007
        if (!empty($_REQUEST['forum'])) {
5008
            $forumId = (int) $_REQUEST['forum'];
5009
        } else {
5010
            // if forum ID is empty, cannot generate delete url
5011
5012
            return [];
5013
        }
5014
    }
5015
    // Check if exist at least one of them to filter forum attachment select query
5016
    if (empty($postId) && empty($attachId)) {
5017
        return [];
5018
    }
5019
5020
    if (empty($postId)) {
5021
        $filter = "AND iid = $attachId";
5022
    } elseif (empty($attachId)) {
5023
        $filter = "AND post_id = $postId";
5024
    } else {
5025
        $filter = "AND post_id = $postId AND iid = $attachId";
5026
    }
5027
    $forumAttachmentTable = Database::get_course_table(TABLE_FORUM_ATTACHMENT);
5028
    $sql = "SELECT iid
5029
            FROM $forumAttachmentTable
5030
            WHERE c_id = $courseId $filter";
5031
    $result = Database::query($sql);
5032
    $json = [];
5033
    if (Database::num_rows($result) > 0) {
5034
        $repo = Container::getForumAttachmentRepository();
5035
        while ($row = Database::fetch_array($result, 'ASSOC')) {
5036
            /** @var CForumAttachment $attachment */
5037
            $attachment = $repo->find($row['iid']);
5038
            $downloadUrl = $repo->getResourceFileDownloadUrl($attachment);
5039
5040
            // name contains an URL to download attachment file and its filename
5041
            $json['name'] = Display::url(
5042
                api_htmlentities($attachment->getFilename()),
5043
                $downloadUrl,
5044
                ['target' => '_blank', 'class' => 'attachFilename']
5045
            );
5046
            $json['id'] = $row['iid'];
5047
            $json['comment'] = $attachment->getComment();
5048
            // Format file size
5049
            $json['size'] = format_file_size($attachment->getSize());
5050
            // Check if $row is consistent
5051
            if ($attachment) {
5052
                // Set result as success and bring delete URL
5053
                $json['result'] = Display::return_icon('accept.png', get_lang('Uploaded.'));
5054
                $url = api_get_path(WEB_CODE_PATH).'forum/viewthread.php?'.api_get_cidreq().'&action=delete_attach&forum='.$forumId.'&thread='.$threadId.'&id_attach='.$row['iid'];
5055
                $json['delete'] = Display::url(
5056
                    Display::return_icon('delete.png', get_lang('Delete'), [], ICON_SIZE_SMALL),
5057
                    $url,
5058
                    ['class' => 'deleteLink']
5059
                );
5060
            } else {
5061
                // If not, set an exclamation result
5062
                $json['result'] = Display::return_icon('exclamation.png', get_lang('Error'));
5063
            }
5064
            // Store array data into $_SESSION
5065
            $_SESSION['forum']['upload_file'][$courseId][$json['id']] = $json;
5066
        }
5067
    }
5068
5069
    return $json;
5070
}
5071
5072
/**
5073
 * Clear forum attachment data stored in $_SESSION,
5074
 * If is not defined post, it will clear all forum attachment data from course.
5075
 *
5076
 * @param int $postId   -1 : Clear all attachments from course stored in $_SESSION
5077
 *                      0 : Clear attachments from course, except from temporal post "0"
5078
 *                      but without delete them from file system and database
5079
 *                      Other values : Clear attachments from course except specified post
5080
 *                      and delete them from file system and database
5081
 * @param int $courseId : Course ID, if it is null, will use api_get_course_int_id()
5082
 *
5083
 * @return array
5084
 */
5085
function clearAttachedFiles($postId = 0, $courseId = 0)
5086
{
5087
    // Init variables
5088
    $courseId = (int) $courseId;
5089
    $postId = (int) $postId;
5090
    $array = [];
5091
    if (empty($courseId)) {
5092
        // $courseId can be null, use api method
5093
        $courseId = api_get_course_int_id();
5094
    }
5095
    if (-1 === $postId) {
5096
        // If post ID is -1 then delete course's attachment data from $_SESSION
5097
        if (!empty($_SESSION['forum']['upload_file'][$courseId])) {
5098
            $array = array_keys($_SESSION['forum']['upload_file'][$courseId]);
5099
            unset($_SESSION['forum']['upload_file'][$courseId]);
5100
        }
5101
    } else {
5102
        $attachIds = getAttachmentIdsByPostId($postId, $courseId);
5103
        if (!empty($_SESSION['forum']['upload_file'][$courseId]) &&
5104
            is_array($_SESSION['forum']['upload_file'][$courseId])) {
5105
            foreach ($_SESSION['forum']['upload_file'][$courseId] as $attachId => $attach) {
5106
                if (!in_array($attachId, $attachIds)) {
5107
                    // If attach ID is not into specified post, delete attachment
5108
                    // Save deleted attachment ID
5109
                    $array[] = $attachId;
5110
                    if (0 !== $postId) {
5111
                        // Post 0 is temporal, delete them from file system and DB
5112
                        delete_attachment(0, $attachId);
5113
                    }
5114
                    // Delete attachment data from $_SESSION
5115
                    unset($_SESSION['forum']['upload_file'][$courseId][$attachId]);
5116
                }
5117
            }
5118
        }
5119
    }
5120
5121
    return $array;
5122
}
5123
5124
/**
5125
 * Returns an array of forum attachment ids into a course and forum post.
5126
 *
5127
 * @param int $postId
5128
 * @param int $courseId
5129
 *
5130
 * @return array
5131
 */
5132
function getAttachmentIdsByPostId($postId, $courseId = 0)
5133
{
5134
    $array = [];
5135
    $courseId = (int) $courseId;
5136
    $postId = (int) $postId;
5137
    if (empty($courseId)) {
5138
        // $courseId can be null, use api method
5139
        $courseId = api_get_course_int_id();
5140
    }
5141
    if ($courseId > 0) {
5142
        $forumAttachmentTable = Database::get_course_table(TABLE_FORUM_ATTACHMENT);
5143
        $sql = "SELECT iid FROM $forumAttachmentTable
5144
                WHERE c_id = $courseId AND post_id = $postId";
5145
        $result = Database::query($sql);
5146
        if (false !== $result && Database::num_rows($result) > 0) {
5147
            while ($row = Database::fetch_array($result, 'ASSOC')) {
5148
                $array[] = $row['iid'];
5149
            }
5150
        }
5151
    }
5152
5153
    return $array;
5154
}
5155
5156
function getPostStatus(CForum $forum, array $row, bool $addWrapper = true): string
5157
{
5158
    $statusIcon = '';
5159
    if ($forum->isModerated()) {
5160
        if ($addWrapper) {
5161
            $statusIcon = '<br /><br /><span id="status_post_'.$row['iid'].'">';
5162
        }
5163
        $row['status'] = empty($row['status']) ? 2 : $row['status'];
5164
5165
        $addUrl = false;
5166
        $showStatus = false;
5167
        if (api_is_allowed_to_edit(false, true)) {
5168
            $addUrl = true;
5169
        } else {
5170
            if ($row['user_id'] == api_get_user_id()) {
5171
                $showStatus = true;
5172
            }
5173
        }
5174
5175
        $label = '';
5176
        $icon = '';
5177
        $buttonType = '';
5178
        switch ($row['status']) {
5179
            case CForumPost::STATUS_VALIDATED:
5180
                $label = get_lang('Validated');
5181
                $icon = 'check-circle';
5182
                $buttonType = 'success';
5183
5184
                break;
5185
            case CForumPost::STATUS_WAITING_MODERATION:
5186
                $label = get_lang('Waiting for moderation');
5187
                $icon = 'alert';
5188
                $buttonType = 'warning';
5189
5190
                break;
5191
            case CForumPost::STATUS_REJECTED:
5192
                $label = get_lang('Rejected');
5193
                $icon = 'minus-circle';
5194
                $buttonType = 'danger';
5195
5196
                break;
5197
        }
5198
5199
        if ($addUrl) {
5200
            $statusIcon .= Display::toolbarButton(
5201
                $label.'&nbsp;',
5202
                'javascript:void(0)',
5203
                $icon,
5204
                $buttonType,
5205
                ['class' => 'change_post_status']
5206
            );
5207
        } else {
5208
            if ($showStatus) {
5209
                $statusIcon .= Display::label(
5210
                    Display::returnFontAwesomeIcon($icon).$label,
5211
                    $buttonType
5212
                );
5213
            }
5214
        }
5215
5216
        if ($addWrapper) {
5217
            $statusIcon .= '</span>';
5218
        }
5219
    }
5220
5221
    return $statusIcon;
5222
}
5223
5224
/**
5225
 * @param CForum $forum
5226
 * @param int    $threadId
5227
 * @param int    $status
5228
 */
5229
function getCountPostsWithStatus($status, $forum, $threadId = null)
5230
{
5231
    $em = Database::getManager();
5232
    $criteria = Criteria::create();
5233
    $criteria
5234
        ->where(Criteria::expr()->eq('status', $status))
5235
        //->andWhere(Criteria::expr()->eq('cId', $forum->getCId()))
5236
        ->andWhere(Criteria::expr()->eq('visible', 1))
5237
    ;
5238
5239
    if (!empty($threadId)) {
5240
        $criteria->andWhere(Criteria::expr()->eq('thread', $threadId));
5241
    }
5242
5243
    $qb = $em->getRepository(CForumPost::class)->createQueryBuilder('p');
5244
    $qb->select('count(p.iid)')
5245
        ->addCriteria($criteria);
5246
5247
    return $qb->getQuery()->getSingleScalarResult();
5248
}
5249
5250
/**
5251
 * @param CForum     $forum
5252
 * @param CForumPost $post
5253
 *
5254
 * @return bool
5255
 */
5256
function postIsEditableByStudent($forum, $post)
5257
{
5258
    if (api_is_platform_admin() || api_is_allowed_to_edit()) {
5259
        return true;
5260
    }
5261
5262
    if (1 == $forum->isModerated()) {
5263
        if (null === $post->getStatus()) {
5264
            return true;
5265
        } else {
5266
            return in_array(
5267
                $post->getStatus(),
5268
                [
5269
                    CForumPost::STATUS_WAITING_MODERATION,
5270
                    CForumPost::STATUS_REJECTED,
5271
                ]
5272
            );
5273
        }
5274
    }
5275
5276
    return true;
5277
}
5278
5279
/**
5280
 * @return bool
5281
 */
5282
function savePostRevision(CForumPost $post)
5283
{
5284
    $userId = api_get_user_id();
5285
5286
    if ($post->getUser()->getId() != $userId) {
5287
        return false;
5288
    }
5289
5290
    $status = (int) !postNeedsRevision($post);
5291
    $extraFieldValue = new ExtraFieldValue('forum_post');
5292
    $params = [
5293
        'item_id' => $post->getIid(),
5294
        'extra_ask_for_revision' => ['extra_ask_for_revision' => $status],
5295
    ];
5296
    if (empty($status)) {
5297
        unset($params['extra_ask_for_revision']);
5298
    }
5299
    $extraFieldValue->saveFieldValues(
5300
        $params,
5301
        true,
5302
        false,
5303
        ['ask_for_revision']
5304
    );
5305
}
5306
5307
/**
5308
 * @param int $postId
5309
 *
5310
 * @return string
5311
 */
5312
function getPostRevision($postId)
5313
{
5314
    $extraFieldValue = new ExtraFieldValue('forum_post');
5315
    $value = $extraFieldValue->get_values_by_handler_and_field_variable(
5316
        $postId,
5317
        'revision_language'
5318
    );
5319
    $revision = '';
5320
    if ($value && isset($value['value'])) {
5321
        $revision = $value['value'];
5322
    }
5323
5324
    return $revision;
5325
}
5326
5327
function postNeedsRevision(CForumPost $post): bool
5328
{
5329
    $postId = $post->getIid();
5330
    $extraFieldValue = new ExtraFieldValue('forum_post');
5331
    $value = $extraFieldValue->get_values_by_handler_and_field_variable(
5332
        $postId,
5333
        'ask_for_revision'
5334
    );
5335
    $hasRevision = false;
5336
    if ($value && isset($value['value'])) {
5337
        return 1 == $value['value'];
5338
    }
5339
5340
    return $hasRevision;
5341
}
5342
5343
/**
5344
 * Generates an HTML button to ask for a review
5345
 */
5346
function getAskRevisionButton(CForumPost $post, CForumThread $threadInfo): string
5347
{
5348
    if (false === api_get_configuration_value('allow_forum_post_revisions')) {
5349
        return '';
5350
    }
5351
5352
    $postId = $post->getIid();
5353
5354
    $status = 'btn-default';
5355
    if (postNeedsRevision($post)) {
5356
        $status = 'btn-success';
5357
    }
5358
5359
    return Display::url(
5360
        get_lang('Ask for a revision'),
5361
        api_get_path(WEB_CODE_PATH).'forum/viewthread.php?'.
5362
        api_get_cidreq().'&action=ask_revision&post_id='.$postId.'&forum='.$threadInfo->getForum()->getIid().'&thread='.$threadInfo->getIid(),
5363
        ['class' => "btn $status", 'title' => get_lang('Ask for a revision')]
5364
    );
5365
}
5366
5367
/**
5368
 * Generates an HTML button to give a review
5369
 */
5370
function getGiveRevisionButton(int $postId, CForumThread $threadInfo): string
5371
{
5372
    return Display::toolbarButton(
5373
        get_lang('Give revision'),
5374
        api_get_path(WEB_CODE_PATH).'forum/reply.php?'.api_get_cidreq().'&'.http_build_query(
5375
            [
5376
                'forum' => $threadInfo->getForum()->getIid(),
5377
                'thread' => $threadInfo->getIid(),
5378
                'post' => $postId,
5379
                'action' => 'replymessage',
5380
                'give_revision' => 1,
5381
            ]
5382
        ),
5383
        'reply',
5384
        'primary',
5385
        ['id' => "reply-to-post-{$postId}"]
5386
    );
5387
}
5388
5389
/**
5390
 * Generates an HTML button to report a post as offensive
5391
 */
5392
function getReportButton(int $postId, CForumThread $threadInfo): string
5393
{
5394
    return Display::url(
5395
        Display::returnFontAwesomeIcon('flag'),
5396
        api_get_path(WEB_CODE_PATH).'forum/viewthread.php?'.
5397
        api_get_cidreq().'&action=report&post_id='.$postId.
5398
        '&forum='.$threadInfo->getForum()->getIid().'&thread='.$threadInfo->getIid(),
5399
        ['class' => 'btn btn-danger', 'title' => get_lang('Report')]
5400
    );
5401
}
5402
5403
/**
5404
 * @return bool
5405
 */
5406
function reportAvailable()
5407
{
5408
    $extraFieldValue = new ExtraFieldValue('course');
5409
    $value = $extraFieldValue->get_values_by_handler_and_field_variable(
5410
        api_get_course_int_id(),
5411
        'allow_forum_report_button'
5412
    );
5413
    $allowReport = false;
5414
    if ($value && isset($value['value']) && 1 == $value['value']) {
5415
        $allowReport = true;
5416
    }
5417
5418
    return $allowReport;
5419
}
5420
5421
/**
5422
 * @return array
5423
 */
5424
function getReportRecipients()
5425
{
5426
    $extraFieldValue = new ExtraFieldValue('course');
5427
    $value = $extraFieldValue->get_values_by_handler_and_field_variable(
5428
        api_get_course_int_id(),
5429
        'forum_report_recipients'
5430
    );
5431
    $users = [];
5432
    if ($value && isset($value['value'])) {
5433
        $usersType = explode(';', $value['value']);
5434
5435
        foreach ($usersType as $type) {
5436
            switch ($type) {
5437
                case 'teachers':
5438
                    $teachers = CourseManager::get_teacher_list_from_course_code(api_get_course_id());
5439
                    if (!empty($teachers)) {
5440
                        $users = array_merge($users, array_column($teachers, 'user_id'));
5441
                    }
5442
5443
                break;
5444
                case 'admins':
5445
                    $admins = UserManager::get_all_administrators();
5446
                    if (!empty($admins)) {
5447
                        $users = array_merge($users, array_column($admins, 'user_id'));
5448
                    }
5449
5450
                    break;
5451
                case 'community_managers':
5452
                    $managers = api_get_configuration_value('community_managers_user_list');
5453
                    if (!empty($managers) && isset($managers['users'])) {
5454
                        $users = array_merge($users, $managers['users']);
5455
                    }
5456
5457
                    break;
5458
            }
5459
        }
5460
5461
        $users = array_unique(array_filter($users));
5462
    }
5463
5464
    return $users;
5465
}
5466
5467
/**
5468
 * Sends an e-mail to all users from getReportRecipients() (users with extra field 'forum_report_recipients')
5469
 */
5470
function reportPost(CForumPost $post, CForum $forumInfo, CForumThread $threadInfo): bool
5471
{
5472
    if (!reportAvailable()) {
5473
        return false;
5474
    }
5475
5476
    if (empty($forumInfo) || empty($threadInfo)) {
5477
        return false;
5478
    }
5479
5480
    $postId = $post->getIid();
5481
5482
    $currentUser = api_get_user_info();
5483
    $users = getReportRecipients();
5484
    if (!empty($users)) {
5485
        $url = api_get_path(WEB_CODE_PATH).
5486
            'forum/viewthread.php?forum='.$forumInfo->getIid().'&thread='.$threadInfo->getIid().'&'.api_get_cidreq().'&post_id='.$postId.'#post_id_'.$postId;
5487
        $postLink = Display::url(
5488
            $post->getPostTitle(),
5489
            $url
5490
        );
5491
        $subject = get_lang('Post reported');
5492
        $content = sprintf(
5493
            get_lang('User %s has reported the message %s in the forum %s'),
5494
            $currentUser['complete_name'],
5495
            $postLink,
5496
            $forumInfo->getForumTitle()
5497
        );
5498
        foreach ($users as $userId) {
5499
            MessageManager::send_message_simple($userId, $subject, $content);
5500
        }
5501
    }
5502
5503
    return true;
5504
}
5505