view.php 20.6 KB
Newer Older
1
2
3
4
5
<?php
/**
 *
 * @package    mahara
 * @subpackage core
6
 * @author     Catalyst IT Ltd
7
8
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
 * @copyright  For copyright information on Mahara, please see the README file distributed with this software.
9
10
11
12
 *
 */

define('INTERNAL', 1);
13
define('PUBLIC', 1);
14
15
16
17
define('SECTION_PLUGINTYPE', 'core');
define('SECTION_PLUGINNAME', 'view');
define('SECTION_PAGE', 'view');

18
require(dirname(dirname(__FILE__)) . '/init.php');
19

20
require_once(get_config('libroot') . 'view.php');
21
require_once(get_config('libroot') . 'collection.php');
22
require_once(get_config('libroot') . 'objectionable.php');
23
require_once('institution.php');
24
require_once('group.php');
25
safe_require('artefact', 'comment');
26
safe_require('artefact', 'file');
27

28
29
30
31
32
33
34
35
36
37
38
39
40
// Used by the Mahara assignment submission plugin for Moodle, to indicate that a user
// coming over from mnet should be able to view a certain page (i.e. a teacher viewing
// an assignmnet submission)
$mnetviewid = param_integer('mnetviewid', false);
$mnetcollid = param_integer('mnetcollid', false);
if (
        ($mnetviewid || $mnetcollid)
        && $SESSION->get('mnetuser')
        && safe_require_plugin('auth', 'xmlrpc')
) {
    auth_xmlrpc_mnet_view_access($mnetviewid, $mnetcollid);
}

41
// access key for roaming teachers
42
43
// TODO: The mt token is used by the old token-based Mahara assignment submission
// access system, which is now deprecated. Remove eventually.
44
45
$mnettoken = param_alphanum('mt', null);
$mnettokenaccess = $SESSION->get('mnetuser') ? $mnettoken : null;
46
47

// access key for logged out users
48
$usertoken = (is_null($mnettokenaccess) && get_config('allowpublicviews')) ? param_alphanum('t', null) : null;
49
$viewtoken = null;
50
if ($mnettoken) {
51
52
    $viewtoken = get_view_from_token($mnettoken, false);
    if (!$viewtoken->viewid) {
53
54
        throw new AccessDeniedException(get_string('accessdenied', 'error'));
    }
55
    $viewid = $viewtoken->viewid;
56
57
}
else if ($usertoken) {
58
59
    $viewtoken = get_view_from_token($usertoken, true);
    if (!$viewtoken->viewid) {
60
        throw new AccessDeniedException(get_string('accessdenied', 'error'));
Richard Mansfield's avatar
Richard Mansfield committed
61
    }
62
    $viewid = $viewtoken->viewid;
Richard Mansfield's avatar
Richard Mansfield committed
63
}
64
65
66
67
68
69
70
71
72
73
74
75
else if ($pageurl = param_alphanumext('page', null)) {
    if ($profile = param_alphanumext('profile', null)) {
        $view = new View(array('urlid' => $pageurl, 'ownerurlid' => $profile));
    }
    else if ($homepage = param_alphanumext('homepage', null)) {
        $view = new View(array('urlid' => $pageurl, 'groupurlid' => $homepage));
    }
    else {
        throw new ViewNotFoundException(get_string('viewnotfoundexceptiontitle', 'error'));
    }
    $viewid = $view->get('id');
}
Richard Mansfield's avatar
Richard Mansfield committed
76
77
78
else {
    $viewid = param_integer('id');
}
79

80
81
82
83
$showmore = param_boolean('showmore');
if (!$showmore) {
    $showmore = 0;
}
84

85
86
87
88
if (!isset($view)) {
    $view = new View($viewid);
}

89
90
$is_admin = $USER->get('admin') || $USER->is_institutional_admin();
$is_owner = $view->get('owner') == $USER->get('id');
91
92
93
94
95
96
97

// check if this is a group page and the user is group admin
if ($groupid = $view->get('group')) {
    $is_group_admin = (group_user_access($groupid) == 'admin');
}

if (is_view_suspended($view) && !$is_admin && !$is_owner && !($groupid && $is_group_admin)) {
98
99
100
101
    $errorstr = get_string('accessdeniedsuspension', 'error');
    throw new AccessDeniedException($errorstr);
}

102
if (!can_view_view($view)) {
103
104
    $errorstr = (param_integer('objection', null)) ? get_string('accessdeniedobjection', 'error') : get_string('accessdenied', 'error');
    throw new AccessDeniedException($errorstr);
105
}
106
$institution = $view->get('institution');
107
View::set_nav($groupid, $institution, false, false, false);
108
// Comment list pagination requires limit/offset params
109
110
111
$limit       = param_integer('limit', 10);
$offset      = param_integer('offset', 0);
$showcomment = param_integer('showcomment', null);
112

113
// Create the "make comment private form" now if it's been submitted
114
if (param_exists('make_public_submit')) {
115
    pieform(ArtefactTypeComment::make_public_form(param_integer('comment')));
116
}
117
else if (param_exists('delete_comment_submit')) {
Richard Mansfield's avatar
Richard Mansfield committed
118
    pieform(ArtefactTypeComment::delete_comment_form(param_integer('comment')));
119
120
}

121
122
$owner    = $view->get('owner');
$viewtype = $view->get('type');
123

124
125
if ($viewtype == 'profile' || $viewtype == 'dashboard' || $viewtype == 'grouphomepage') {
    redirect($view->get_url());
126
}
127

128
129
define('TITLE', $view->get('title'));

130
$collection = $view->get('collection');
131
132
133
134
// Do we need to redirect to the matrix page on first visit via token access?
if ($viewtoken && $viewtoken->gotomatrix && $collection && $collection->has_framework()) {
    redirect($collection->get_framework_url($collection, true));
}
135
$submittedgroup = (int)$view->get('submittedgroup');
136
if ($USER->is_logged_in() && $submittedgroup && group_user_can_assess_submitted_views($submittedgroup, $USER->get('id'))) {
137
    // The user is a tutor of the group that this view has
138
    // been submitted to, and is entitled to release the view
139
    $submittedgroup = get_group_by_id($submittedgroup, true);
140
141

    // Form for LTI grading
142
143
144
145
146
147
148
    if (is_plugin_active('lti', 'module')) {
        if ($collection) {
            $ltigradeform = PluginModuleLti::get_grade_dialogue($collection->get('id'), null);
        }
        else {
            $ltigradeform = PluginModuleLti::get_grade_dialogue(null, $view->get('id'));
        }
149
150
    }

151
152
    // If the view is part of a submitted collection, the whole
    // collection must be released at once.
153
154
    $releasecollection = !empty($collection) && $collection->get('submittedgroup') == $submittedgroup->id && empty($ltigradeform);

155
    if ($releasecollection) {
156
        if (isset($ltigradeform) && $ltigradeform && $ctime = $collection->get('submittedtime')) {
157
158
159
160
161
162
            preg_match("/^.*?\"(.*?)\" - \"(.*?)\"/", $submittedgroup->name, $matches);
            $lticoursename = hsc($matches[1]);
            $ltiassignmentname = hsc($matches[2]);
            $text = get_string('collectionsubmittedtogroupgrade', 'view', group_homepage_url($submittedgroup), $ltiassignmentname, $lticoursename, format_date(strtotime($ctime)));
        }
        else if ($ctime = $collection->get('submittedtime')) {
163
164
165
166
167
168
169
170
171
            $text = get_string(
                'collectionsubmittedtogroupon', 'view', group_homepage_url($submittedgroup), hsc($submittedgroup->name),
                format_date(strtotime($ctime))
            );
        }
        else {
            $text = get_string('collectionsubmittedtogroup', 'view', group_homepage_url($submittedgroup), hsc($submittedgroup->name));
        }
    }
172
173
174
175
176
177
    else if ($ltigradeform && $view->get('submittedtime')) {
        preg_match("/^.*?\"(.*?)\" - \"(.*?)\"/", $submittedgroup->name, $matches);
        $lticoursename = hsc($matches[1]);
        $ltiassignmentname = hsc($matches[2]);
        $text = get_string('viewsubmittedtogroupgrade', 'view', group_homepage_url($submittedgroup), $ltiassignmentname, $lticoursename, format_date(strtotime($view->get('submittedtime'))));
    }
178
    else if ($view->get('submittedtime')) {
179
        $text = get_string('viewsubmittedtogroupon1', 'view', group_homepage_url($submittedgroup), hsc($submittedgroup->name), format_date(strtotime($view->get('submittedtime'))));
180
181
    }
    else {
182
        $text = get_string('viewsubmittedtogroup1', 'view', group_homepage_url($submittedgroup), hsc($submittedgroup->name));
183
    }
184
    if (($releasecollection && $collection->get('submittedstatus') == Collection::SUBMITTED) || $view->get('submittedstatus') == View::SUBMITTED && empty($ltigradeform)) {
185
186
187
        $releaseform = pieform(array(
            'name'     => 'releaseview',
            'method'   => 'post',
Pat Kira's avatar
Pat Kira committed
188
            'class' => 'form-inline',
189
190
191
192
193
194
195
196
197
            'plugintype' => 'core',
            'pluginname' => 'view',
            'autofocus' => false,
            'elements' => array(
                'submittedview' => array(
                    'type'  => 'html',
                    'value' => $text,
                ),
                'submit' => array(
Pat Kira's avatar
Pat Kira committed
198
199
                    'type'  => 'button',
                    'usebuttontag' => true,
200
                    'class' => 'btn-secondary float-right',
201
                    'value' => $releasecollection ? '<span class="icon icon-unlock left" role="presentation" aria-hidden="true"></span>' . get_string('releasecollection', 'group') : '<span class="icon icon-unlock left" role="presentation" aria-hidden="true"></span>' . get_string('releaseview', 'group'),
202
                ),
203
            ),
204
205
        ));
    }
206
207
208
    else if ($ltigradeform) {
        $releaseform = $text;
    }
209
210
211
    else {
        $releaseform = $text . ' ' . get_string('submittedpendingrelease', 'view');
    }
212
213
214
215
216

    if (!empty($ltigradeform)) {
        $releaseform .= $ltigradeform;
    }

217
}
218
219
220
221
222
else {
    $releaseform = '';
}

function releaseview_submit() {
223
    global $USER, $SESSION, $view, $collection, $submittedgroup, $releasecollection;
224

225
    if ($releasecollection) {
226
227
228
229
230
231
232
233
        if (is_object($submittedgroup) && $submittedgroup->allowarchives) {
            $collection->pendingrelease($USER);
            $SESSION->add_ok_msg(get_string('collectionreleasedpending', 'group'));
        }
        else {
            $collection->release($USER);
            $SESSION->add_ok_msg(get_string('collectionreleasedsuccess', 'group'));
        }
234
235
    }
    else {
236
237
238
239
240
241
242
243
        if (is_object($submittedgroup) && $submittedgroup->allowarchives) {
            $view->pendingrelease($USER);
            $SESSION->add_ok_msg(get_string('viewreleasedpending', 'group'));
        }
        else {
            $view->release($USER);
            $SESSION->add_ok_msg(get_string('viewreleasedsuccess', 'group'));
        }
244
    }
245
    if ($submittedgroup) {
246
247
        // The tutor might not have access to the view any more; send
        // them back to the group page.
248
        redirect(group_homepage_url($submittedgroup));
249
    }
250
    redirect($view->get_url());
251
}
252

253
$javascript = array('paginator', 'viewmenu', 'js/collection-navigation.js', 'js/jquery/jquery-mobile/jquery.mobile.custom.min.js');
254
255
$blocktype_js = $view->get_all_blocktype_javascript();
$javascript = array_merge($javascript, $blocktype_js['jsfiles']);
256
if (is_plugin_active('externalvideo', 'blocktype')) {
257
258
    $javascript = array_merge($javascript, array((is_https() ? 'https:' : 'http:') . '//cdn.embedly.com/widgets/platform.js'));
}
259
$inlinejs = "jQuery( function() {\n" . join("\n", $blocktype_js['initjs']) . "\n});";
260

261
262
// If the view has comments turned off, tutors can still leave
// comments if the view is submitted to their group.
263
if (!empty($releaseform) || ($commenttype = $view->user_comments_allowed($USER))) {
264
    $defaultprivate = !empty($releaseform);
265
    $moderate = !$USER->is_logged_in() || (isset($commenttype) && $commenttype === 'private');
266
    $addfeedbackform = pieform(ArtefactTypeComment::add_comment_form($defaultprivate, $moderate));
267
}
268
$objectionform = false;
269
270
if ($USER->is_logged_in()) {
    $objectionform = pieform(objection_form());
271
    $reviewform = pieform(review_form($view->get('id')));
272
    if ($notrudeform = notrude_form()) {
273
274
        $notrudeform = pieform($notrudeform);
    }
275
276
277
278
279
    // For for admin to review objection claim, add comment
    // about objectionable content and possibly remove access
    if ($stillrudeform = stillrude_form()) {
        $stillrudeform = pieform($stillrudeform);
    }
280
281
}

282
$viewbeingwatched = (int)record_exists('usr_watchlist_view', 'usr', $USER->get('id'), 'view', $viewid);
283
284
285
286
287
288
$commentoptions = ArtefactTypeComment::get_comment_options();
$commentoptions->limit = $limit;
$commentoptions->offset = $offset;
$commentoptions->showcomment = $showcomment;
$commentoptions->view = $view;
$feedback = ArtefactTypeComment::get_comments($commentoptions);
289

290
// Set up theme
291
// if the view theme is set in view table
292
293
$viewtheme = $view->get('theme');
if ($viewtheme && $THEME->basename != $viewtheme) {
294
    $THEME = new Theme($view);
295
}
296
297
// if it's another users view, it should be displayed with the other users institution theme
else if ($owner && $owner != $USER->get('id')) {
298
    $THEME = new Theme((int)$owner);
299
300
}

Naomi Guyer's avatar
Naomi Guyer committed
301
$headers = array();
302
$headers[] = '<link rel="stylesheet" type="text/css" href="' . append_version_number(get_config('wwwroot') . 'js/jquery/jquery-ui/css/smoothness/jquery-ui.min.css') . '">';
303
$headers = array_merge($headers, $view->get_all_blocktype_css());
304
305
// Set up skin, if the page has one
$viewskin = $view->get('skin');
306
307
$issiteview = $view->get('institution') == 'mahara';
if ($viewskin && get_config('skins') && can_use_skins($owner, false, $issiteview) && (!isset($THEME->skins) || $THEME->skins !== false)) {
308
    $skin = array('skinid' => $viewskin, 'viewid' => $view->get('id'));
309
310
311
312
313
}
else {
    $skin = false;
}

314
315
316
if (!$view->is_public()) {
    $headers[] = '<meta name="robots" content="noindex">';  // Tell search engines not to index non-public views
}
317

318
$can_edit = $USER->can_edit_view($view) && !$submittedgroup && !$view->is_submitted();
319
$can_copy = $view->is_copyable();
320

321
322
323
324
325
326
327
$viewgroupform = false;
if ($owner && $owner == $USER->get('id')) {
    if ($tutorgroupdata = group_get_user_course_groups()) {
        if (!$view->is_submitted()) {
            $viewgroupform = view_group_submission_form($view, $tutorgroupdata, 'view');
        }
    }
328
    if (is_plugin_active('lti', 'module') && PluginModuleLti::can_submit_for_grading()) {
329
330
        $ltisubmissionform = PluginModuleLti::submit_from_view_or_collection_form($view);
    }
331
332
}

333

334
335
// Don't show page content to a user with peer role
// if the view doesn't have a peer assessment block
336
337
if (!$USER->has_peer_role_only($view) || $view->has_peer_assessement_block()
    || ($USER->is_admin_for_user($view->get('owner')) && $view->is_objectionable())) {
338
339
    $viewcontent = $view->build_rows(); // Build content before initialising smarty in case pieform elements define headers.
}
340

341
$smarty = smarty(
342
    $javascript,
343
    $headers,
344
345
346
347
    array('confirmcopytitle' => 'view',
          'confirmcopydesc' => 'view',
          'View' => 'view',
          'Collection' => 'collection'),
348
349
    array(
        'sidebars' => false,
350
        'skin' => $skin
351
    )
352
);
353

354
355
$javascript = <<<EOF
var viewid = {$viewid};
356
var showmore = {$showmore};
357
jQuery(function () {
358
359
    paginator = {$feedback->pagination_js}
});
360

361
362
363
jQuery(function($) {
    $('#column-container .blockinstance-content .commentlink').each(function() {
        var blockid = $(this).attr('id').match(/\d+/);
364
        // only use comments expander if there are comments on the artefact
365
366
        $(this).on('click', function(e) {
            var commentlink = $(this);
367
368
            var chtml = commentlink.parent().parent().find('#feedbacktable_' + blockid).parent();
            // add a 'close' link at the bottom of the list for convenience
369
            if ($('#closer_' + blockid).length == 0) {
370
                var closer = $('<a id="closer_' + blockid + '" href="#" class="close-link">Close</a>').on("click", function(e) {
371
                    $(this).parent().toggle(400, function() {
372
                        commentlink.trigger("focus");
373
374
375
376
377
378
379
                    });
                    e.preventDefault();
                });
                chtml.append(closer);
            }
            chtml.toggle(400, function() {
                if (chtml.is(':visible')) {
380
                    chtml.find('a').first().trigger("focus");
381
382
                }
                else {
383
                    commentlink.trigger("focus");
384
385
386
387
388
                }
            });
            e.preventDefault();
        });
    });
389
390
391
392
393
394
395
396
397
398
399
400

    $('.moretags').on('click', function(e) {
        e.preventDefault();
        var params = {
            'viewid': viewid
        }
        sendjsonrequest(config['wwwroot'] + 'view/viewtags.json.php',  params, 'POST', function(data) {
            if (data.count) {
                $('.tags').html(data.html);
            }
        });
    });
401
402
});

403
404
405
jQuery(window).on('pageupdated', {}, function() {
    dock.init(jQuery(document));
});
406
407
EOF;

408
// collection top navigation
409
if ($collection) {
410
411
    $shownav = $collection->get('navigation');
    if ($shownav) {
412
        if ($views = $collection->get('views')) {
413
414
415
416
417
            $viewnav = $views['views'];
            if ($collection->has_framework()) {
                array_unshift($viewnav, $collection->collection_nav_framework_option());
            }
            $smarty->assign('collection', $viewnav);
418
        }
419
    }
420
421
}

422
423
424
425
$blocktype_toolbar = $view->get_all_blocktype_toolbar();
if (!empty($blocktype_toolbar['toolbarhtml'])) {
    $smarty->assign('toolbarhtml', join("\n", $blocktype_toolbar['toolbarhtml']));
}
426
$smarty->assign('canremove', $can_edit);
427
$smarty->assign('INLINEJAVASCRIPT', $javascript . $inlinejs);
428
$smarty->assign('viewid', $viewid);
429
$smarty->assign('viewtype', $viewtype);
430
$smarty->assign('feedback', $feedback);
Richard Mansfield's avatar
Richard Mansfield committed
431
$smarty->assign('owner', $owner);
432
433
434
list($tagcount, $alltags) = $view->get_all_tags_for_view(10);
$smarty->assign('alltags', $alltags);
$smarty->assign('moretags', ($tagcount > sizeof($alltags) ? true : false));
435
$smarty->assign('PAGEHEADING', null);
436
437
438
439
440
441
442
443
444
445
446
447
448
if ($view->is_anonymous()) {
  $smarty->assign('PAGEAUTHOR', get_string('anonymoususer'));
  $smarty->assign('author', get_string('anonymoususer'));
  if ($view->is_staff_or_admin_for_page()) {
    $smarty->assign('realauthor', $view->display_author());
  }
  $smarty->assign('anonymous', TRUE);
} else {
  $smarty->assign('PAGEAUTHOR', $view->formatted_owner());
  $smarty->assign('author', $view->display_author());
  $smarty->assign('anonymous', FALSE);
}

449

450
$titletext = ($collection && $shownav) ? hsc($collection->get('name')) : $view->display_title(true, false, false);
451
$smarty->assign('lastupdatedstr', $view->lastchanged_message());
452
$smarty->assign('visitstring', $view->visit_message());
453
if ($can_edit) {
454
    $smarty->assign('editurl', get_config('wwwroot') . 'view/blocks.php?id=' . $viewid);
455
}
456
457
if ($can_copy) {
    $smarty->assign('copyurl', get_config('wwwroot') . 'view/copy.php?id=' . $viewid . (!empty($collection) ? '&collection=' . $collection->get('id') : ''));
458
459
460
461
    if (!$USER->is_logged_in() && $view->get('owner')) {
        // if no user is loggedin and the personal profile is public, the Copy button should download the portfolio
        $smarty->assign('downloadurl', get_config('wwwroot') . 'view/download.php?id=' . $viewid . (!empty($collection) ? '&collection=' . $collection->get('id') : ''));
    }
462
}
463
464
465
466
$versions = View::get_versions($view->get('id'));
if ($versions->count > 0) {
    $smarty->assign('versionurl', get_config('wwwroot') . 'view/versioning.php?view=' . $viewid);
}
467
468
$smarty->assign('createversionurl', get_config('wwwroot') . 'view/createversion.php?view=' . $viewid);

469
470
$title = hsc(TITLE);

471
$smarty->assign('maintitle', $titletext);
472

473
// Provide a link for roaming teachers to return
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
$showmnetlink = false;
// Old token-based access list
if (
    $mnetviewlist = $SESSION->get('mnetviewaccess')
    && isset($mnetviewlist[$view->get('id')])
) {
    $showmnetlink = true;
}

// New mnet-based access list
if (
    $SESSION->get('mnetviews')
    && in_array($view->get('id'), $SESSION->get('mnetviews'))
) {
    $showmnetlink = true;
}


if ($showmnetlink) {
    $returnurl = $SESSION->get('mnetuserfrom');
    require_once(get_config('docroot') . 'api/xmlrpc/lib.php');
    if ($peer = get_peer_from_instanceid($SESSION->get('authinstance'))) {
        $smarty->assign('mnethost', array(
            'name'      => $peer->name,
            'url'       => $returnurl ? $returnurl : $peer->wwwroot,
        ));
500
501
    }
}
502

503
$smarty->assign('viewdescription', ArtefactTypeFolder::append_view_url($view->get('description'), $view->get('id')));
504
$smarty->assign('viewinstructions', ArtefactTypeFolder::append_view_url($view->get('instructions'), $view->get('id')));
505
$smarty->assign('viewcontent', (isset($viewcontent) ? $viewcontent : null));
506
$smarty->assign('releaseform', $releaseform);
507
508
509
510
if (isset($ltisubmissionform)) {
    $smarty->assign('ltisubmissionform', $ltisubmissionform);
}

511
if (isset($addfeedbackform)) {
512
    $smarty->assign('enablecomments', 1);
513
    $smarty->assign('addfeedbackform', $addfeedbackform);
514
}
515
516
if (isset($objectionform)) {
    $smarty->assign('objectionform', $objectionform);
517
518
519
520
    if ($USER->is_logged_in()) {
        $smarty->assign('notrudeform', $notrudeform);
        $smarty->assign('stillrudeform', $stillrudeform);
    }
521
522
523
524
525
526
    $smarty->assign('objectedpage', $view->is_objectionable());
    $smarty->assign('objector', $view->is_objectionable($USER->get('id')));
    $smarty->assign('objectionreplied', $view->is_objectionable(null, true));
}
if (isset($reviewform)) {
    $smarty->assign('reviewform', $reviewform);
527
}
528
$smarty->assign('viewbeingwatched', $viewbeingwatched);
529

530
531
if ($viewgroupform) {
    $smarty->assign('view_group_submission_form', $viewgroupform);
532
533
}

534
535
536
537
if ($titletext !== $title) {
    $smarty->assign('title', $title);
}

538
539
$smarty->assign('userisowner', ($owner && $owner == $USER->get('id')));

540
541
$smarty->display('view/view.tpl');

542
mahara_touch_record('view', $viewid); // Update record 'atime'
543
mahara_log('views', "$viewid"); // Log view visits