Passed
Push — master ( 306e3e...2f14dc )
by Julito
09:59
created

increase_thread_view()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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