Commit d1d6880e authored by Robert Lyon's avatar Robert Lyon Committed by Cecilia Vela Gurovic
Browse files

Bug 1784778: Peer assessment block



A new block is created that can only be filled with content from a
person who has the “Peer assessment” role for a portfolio. The block's
content is not visible to the page owner until the peer published it.

The block can be selected by the portfolio author for inclusion on any
page, but it can only be used in conjunction with the peer assessor role.
If no peer assessor is selected for a portfolio, the block stays empty.
It is also available on the site and institution level for inclusion into
portfolio pages there (for the creation of templates).

behatnotneeded: will be added in patch 9063

Change-Id: I6f9945f184b4512c345402e1a73f6fc6f21572a7
Signed-off-by: Robert Lyon's avatarRobert Lyon <robertl@catalyst.net.nz>
parent 012786f4
...@@ -185,7 +185,7 @@ function check_is_embedded_image_visible($fileid, $includeresourcetypes = null, ...@@ -185,7 +185,7 @@ function check_is_embedded_image_visible($fileid, $includeresourcetypes = null,
$isvisible = false; $isvisible = false;
// Check for resource types a file may be embeded in. // Check for resource types a file may be embeded in.
$resourcetypes = array( $resourcetypes = array(
'comment', 'annotation', 'annotationfeedback', 'blog', 'textbox', 'editnote', 'text', 'introtext', 'wallpost', 'staticpages' 'comment', 'annotation', 'annotationfeedback', 'assessment', 'peerinstruction', 'blog', 'textbox', 'editnote', 'text', 'introtext', 'wallpost', 'staticpages'
); );
if (!empty($includeresourcetypes)) { if (!empty($includeresourcetypes)) {
if (!is_array($includeresourcetypes)) { if (!is_array($includeresourcetypes)) {
......
<?php
/**
*
* @package mahara
* @subpackage artefact-peerassessment
* @author Catalyst IT Ltd
* @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.
*
*/
define('INTERNAL', 1);
define('JSON', 1);
require(dirname(dirname(dirname(__FILE__))) . '/init.php');
require_once(get_config('libroot') . 'view.php');
safe_require('artefact', 'peerassessment');
safe_require('blocktype', 'peerassessment');
$id = param_integer('id', null);
$blockid = param_integer('block', null);
$block = new BlockInstance($blockid);
// Is the block correct type
if ($block->get('blocktype') != 'peerassessment') {
json_reply('local', get_string('wrongblocktype', 'view'));
}
$view = $block->get_view();
$viewid = $view->get('id');
// Is the block on a page we can see
if (!can_view_view($viewid)) {
json_reply('local', get_string('noaccesstoview', 'view'));
}
$item = new ArtefactTypePeerassessment($id);
$data = new stdClass();
$data->id = $item->get('id');
$data->message = $item->get('description');
json_reply(false, array('data' => $data));
/**
* Javascript for the peerassessment artefact
*
* @package mahara
* @subpackage blocktype-peerassessment
* @author Catalyst IT Ltd
* @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.
*/
function isTinyMceUsed(elementname) {
return (typeof tinyMCE !== 'undefined' && typeof tinyMCE.get(elementname) !== 'undefined');
}
function initTinyMCE(formname) {
var textareaId = formname + '_message';
if (isTinyMceUsed(formname)) {
tinyMCE.execCommand('mceRemoveEditor', false, textareaId);
tinyMCE.execCommand('mceAddEditor', false, textareaId);
}
}
$(function() {
configureAssessmentCancel();
configureModalOpen();
});
jQuery(window).on('pageupdated', {}, function() {
configureAssessmentCancel();
configureModalOpen();
});
function configureModalOpen() {
$('.js-peerassessment-modal').off('click');
$('.js-peerassessment-modal').on('click', function(e) {
e.stopPropagation();
e.preventDefault();
var blockid = $(this).data('blockid');
var formname = $('#assessment_feedbackform_' + blockid).find('form')[0].id;
dock.show($('#assessment_feedbackform_' + blockid), false, true);
if ($(this).data('id')) {
sendjsonrequest(config.wwwroot + 'artefact/peerassessment/assessmentinfo.json.php', {
'id' : $(this).data('id'),
'block' : blockid,
}, 'POST', function (data) {
// Populate the form
$('#' + formname + '_assessment').val(data.data.id);
// Update TinyMCE
modifyTinyMCEContent(formname, data, data.data.message);
});
}
else {
$('#' + formname + '_assessment').val(0);
modifyTinyMCEContent(formname, null, '');
}
});
}
function configureAssessmentCancel() {
$('.feedbacktable.modal .cancel').off('click');
$('.feedbacktable.modal .cancel').on('click', function(e) {
e.stopPropagation();
e.preventDefault();
dock.hide();
});
};
function modifyPeerassessmentSuccess(form, data) {
var formname = form.name;
var limit = getUrlParameter('limit');
var offset = getUrlParameter('offset');
// Reload the peerassessment feedback table with the new feedback that's just been made public.
// Calls the save method on all editor instances
tinyMCE.triggerSave();
sendjsonrequest(config.wwwroot + 'artefact/peerassessment/peerassessment.json.php', {
'assessment' : jQuery('#' + formname + '_assessment').val(),
'view' : jQuery('#' + formname + '_view').val(),
'block' : jQuery('#' + formname + '_block').val(),
'limit' : limit,
'offset' : offset,
}, 'POST', function (data) {
var blockid = jQuery('#' + formname + '_block').val();
// Populate the div.
(function($) {
var scope = $('#assessmentfeedbacktable' + blockid);
scope.html(data.data.tablerows);
var scopepagination = scope.parent().find('.pagination-wrapper');
scopepagination.html(data.data.pagination);
dock.init(scope);
initTinyMCE(formname);
configureModalOpen();
})(jQuery);
});
// if we are in a modal close it
if (jQuery('#assessment_feedbacktable_' + jQuery('#' + formname + '_blockid').val()).hasClass('modal-docked')) {
dock.hide();
}
formSuccess(form, data);
}
function addPeerassessmentSuccess(form, data) {
var formname = form.name;
var blockid = jQuery('#' + formname + '_block').val();
var limit = getUrlParameter('limit');
var offset = getUrlParameter('offset');
var tinymce = jQuery('#' + form.id + '_message');
var assessmentpaginator = window['assessmentpaginator' + blockid];
if (typeof(assessmentpaginator) != 'undefined' && assessmentpaginator.id == 'peerassessment_pagination_' + blockid) {
// Make sure its using the peerassessment paginator.
assessmentpaginator.updateResults(data);
assessmentpaginator.alertProxy('pagechanged', data['data']);
configureModalOpen();
}
else {
// Reload the peerassessment feedback table with the new feedback that's just been entered.
// Calls the save method on all editor instances before
// assessment being submitted.
tinyMCE.triggerSave();
sendjsonrequest(config.wwwroot + 'artefact/peerassessment/peerassessment.json.php',
{
'block' : jQuery('#' + formname + '_block').val(),
'limit' : limit,
'offset' : offset,
}, 'POST', function (data) {
var blockid = jQuery('#' + formname + '_block').val();
// Populate the div
(function($) {
var scope = $('#assessmentfeedbacktable' + blockid);
scope.html(data.data.tablerows);
var scopepagination = scope.parent().find('.pagination-wrapper');
scopepagination.html(data.data.pagination);
dock.init(scope);
initTinyMCE(formname);
configureModalOpen();
})(jQuery);
});
}
dock.hide();
// Clear TinyMCE
modifyTinyMCEContent(formname, data, '');
formSuccess(form, data);
}
function modifyTinyMCEContent(formname, data, content) {
if (isTinyMceUsed(formname + '_message')) {
tinyMCE.get(formname + '_message').setContent(content);
}
// Clear the textarea (in case TinyMCE is disabled)
var messageid = 'message';
if (data && data.fieldnames && data.fieldnames.message) {
messageid = data.fieldnames.message;
}
jQuery('#' + formname + '_' + messageid).val(content);
}
/*
* This called when data of submitted feedback form are invalid
* This shows the tinymce editor and error message
*/
function addPeerassessmentError(form, data) {
var formname = form.id;
if (isTinyMceUsed()) {
var mce = tinyMCE.get(formname + '_message');
mce.show();
jQuery('.mce-toolbar.mce-first').siblings().addClass('hidden');
mce.focus();
}
if (jQuery('#' + formname).hasClass('modal-docked')) {
jQuery('#' + formname).removeClass('closed').addClass('active');
}
configureAssessmentCancel();
formError(form, data);
}
<?php
/**
*
* @package mahara
* @subpackage blocktype-peerassessment
* @author Catalyst IT Ltd
* @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.
*
*/
defined('INTERNAL') || die();
$string['title'] = 'Peer assessment';
$string['description'] = 'A block to display peer assessments';
$string['blockcontent'] = 'Instructions';
$string['nopeerassessment'] = 'No assessment feedback';
$string['addpeerassessment'] = 'Add peer assessment';
$string['instructions'] = 'Instructions';
$string['draft'] = 'Save draft';
$string['publish'] = 'Publish';
$string['savepublishhelp'] = '<p><strong>Save draft:</strong> Only you can view it. While your assessment is in draft status, you can make changes.</p>
<p><strong>Publish:</strong> The person for whom you are giving the peer assessment can see your assessment. Everybody else who has access to the portfolio can view it as well, unless the portfolio also contains the signoff block and is not signed off by the portfolio owner. You cannot revert a published assessment to draft status.</p>';
<?php
/**
*
* @package mahara
* @subpackage blocktype-peerassessment
* @author Catalyst IT Ltd
* @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.
*
*/
defined ('INTERNAL') || die();
class PluginBlocktypePeerassessment extends MaharaCoreBlocktype {
public static function should_ajaxify() {
// TinyMCE doesn't play well with loading by ajax
return false;
}
public static function single_only() {
return false;
}
public static function get_title() {
return get_string('title', 'blocktype.peerassessment/peerassessment');
}
public static function get_description() {
return get_string('description', 'blocktype.peerassessment/peerassessment');
}
public static function get_categories() {
return array("general" => 14600);
}
public static function get_viewtypes() {
return array('portfolio');
}
public static function render_instance(BlockInstance $instance, $editing=false) {
global $USER;
$configdata = $instance->get('configdata');
$instructions = false;
if (array_key_exists('instructions', $configdata)) {
$instructions = $configdata['instructions'];
}
safe_require('artefact', 'peerassessment');
// Peer assessment list pagination requires limit/offset params
$limit = param_integer('limit', 10);
$offset = param_integer('offset', 0);
$showcomment = param_integer('showcomment', null);
// Create the "make assessment private form" now if it's been submitted
if (param_exists('make_public_submit')) {
pieform(ArtefactTypePeerassessment::make_public_form(param_integer('assessment')));
}
else if (param_exists('delete_assessment_submit')) {
pieform(ArtefactTypePeerassessment::delete_assessment_form(param_integer('assessment'), param_integer('view'), param_integer('block')));
}
$view = new View($instance->get('view'));
safe_require('artefact', 'peerassessment');
$options = ArtefactTypePeerassessment::get_assessment_options();
$options->limit = $limit;
$options->offset = $offset;
$options->showcomment = $showcomment;
$options->view = $instance->get_view();
$options->block = $instance->get('id');
$feedback = ArtefactTypePeerassessment::get_assessments($options);
$feedbackform = ArtefactTypePeerassessment::add_assessment_form(true, $instance->get('id'), 0);
$smarty = smarty_core();
$smarty->assign('blockid', $instance->get('id'));
$smarty->assign('instructions', $instructions);
$smarty->assign('allowfeedback', true); // @TODO: restrict to peer/manager
$smarty->assign('addassessmentfeedbackform', pieform($feedbackform));
if ($feedback && $feedback->count > 0) {
$smarty->assign('feedback', $feedback);
}
else {
$smarty->assign('editing', $editing);
$smarty->assign('noassessment', get_string('nopeerassessment', 'blocktype.peerassessment/peerassessment'));
}
$html = $smarty->fetch('blocktype:peerassessment:peerassessment.tpl');
return $html;
}
public static function has_instance_config() {
return true;
}
public static function instance_config_form(BlockInstance $instance) {
$configdata = $instance->get('configdata');
if (!$height = get_config('blockeditorheight')) {
$cfheight = param_integer('cfheight', 0);
$height = $cfheight ? $cfheight * 0.7 : 150;
}
safe_require('artefact', 'peerassessment');
$view = $instance->get_view();
$instructions = '';
if (array_key_exists('instructions', $configdata)) {
$instructions = $configdata['instructions'];
}
$elements = array (
'instructions' => array (
'type' => 'wysiwyg',
'title' => get_string('blockcontent', 'blocktype.peerassessment/peerassessment'),
'width' => '100%',
'height' => $height . 'px',
'defaultvalue' => $instructions,
'rules' => array('maxlength' => 65536),
),
);
return $elements;
}
public static function instance_config_save($values, $instance) {
require_once('embeddedimage.php');
$newtext = EmbeddedImage::prepare_embedded_images($values['instructions'], 'peerinstruction', $instance->get('id'));
$values['instructions'] = $newtext;
return $values;
}
public static function get_artefacts(BlockInstance $instance) {
return array();
}
public static function get_instance_javascript(BlockInstance $bi) {
return array(
array(
'file' => 'js/peerassessment.js'
)
);
}
public static function delete_instance(BlockInstance $instance) {
$id = $instance->get('id');
require_once('embeddedimage.php');
EmbeddedImage::delete_embedded_images('peerinstruction', $id);
$artefacts = get_column('artefact_peer_assessment', 'assessment', 'block', $id);
if (!empty($artefacts)) {
safe_require('artefact', 'peerassessment');
foreach ($artefacts as $artefactid) {
// Delete the assessment.
$a = new ArtefactTypePeerAssessment($artefactid);
$a->delete();
}
}
}
}
<?php
/**
*
* @package mahara
* @subpackage artefact-comment
* @author Catalyst IT Ltd
* @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.
*
*/
defined('INTERNAL') || die();
$config = new StdClass;
$config->version = 2018073000;
$config->release = '1.0.0';
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="lib/db" VERSION="20100319" COMMENT="Peer assessment table"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
>
<TABLES>
<TABLE NAME="artefact_peer_assessment">
<FIELDS>
<FIELD NAME="assessment" TYPE="int" LENGTH="10" NOTNULL="true"/>
<FIELD NAME="block" TYPE="int" LENGTH="10" NOTNULL="true"/>
<FIELD NAME="usr" TYPE="int" LENGTH="10" NOTNULL="true"/>
<FIELD NAME="view" TYPE="int" LENGTH="10" NOTNULL="true"/>
<FIELD NAME="private" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" UNSIGNED="true"/>
</FIELDS>
<KEYS>
<KEY NAME="assessmentpk" TYPE="primary" FIELDS="assessment"/>
<KEY NAME="assessmentfk" TYPE="foreign" FIELDS="assessment" REFTABLE="artefact" REFFIELDS="id"/>
<KEY NAME="blockfk" TYPE="foreign" FIELDS="block" REFTABLE="block_instance" REFFIELDS="id"/>
<KEY NAME="usrfk" TYPE="foreign" FIELDS="usr" REFTABLE="usr" REFFIELDS="id"/>
<KEY NAME="viewfk" TYPE="foreign" FIELDS="view" REFTABLE="view" REFFIELDS="id"/>
</KEYS>
</TABLE>
<TABLE NAME="view_signoff_verify">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" />
<FIELD NAME="view" TYPE="int" LENGTH="10" NOTNULL="true"/>
<FIELD NAME="verifier" TYPE="int" LENGTH="10" NOTNULL="false"/>
<FIELD NAME="verifiedtime" TYPE="datetime" NOTNULL="false"/>
<FIELD NAME="signofftime" TYPE="datetime" NOTNULL="false"/>
<FIELD NAME="signoff" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" UNSIGNED="true"/>
<FIELD NAME="verified" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" UNSIGNED="true"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
<KEY NAME="verifierfk" TYPE="foreign" FIELDS="verifier" REFTABLE="usr" REFFIELDS="id"/>
<KEY NAME="viewfk" TYPE="foreign" FIELDS="view" REFTABLE="view" REFFIELDS="id"/>
</KEYS>
</TABLE>
</TABLES>
</XMLDB>
<?php
/**
*
* @package mahara
* @subpackage artefact-peerassessment
* @author Catalyst IT Ltd
* @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.
*
*/
define('INTERNAL', 1);
define('MENUITEM', 'myportfolio');
require(dirname(dirname(dirname(__FILE__))) . '/init.php');
define('TITLE', get_string('editassessment', 'artefact.peerassessment'));
safe_require('artefact', 'peerassessment');
$id = param_integer('id');
$viewid = param_integer('view');
$assessment = new ArtefactTypePeerassessment($id);
if ($USER->get('id') != $assessment->get('author')) {
throw new AccessDeniedException(get_string('canteditnotauthor', 'artefact.peerassessment'));
}
$onview = $assessment->get('onview');
if ($onview && $onview != $viewid) {
throw new NotFoundException(get_string('notinview', 'artefact.peerassessment', $id, $viewid));
}
$maxage = (int) get_config_plugin('artefact', 'comment', 'commenteditabletime');
$editableafter = time() - 60 * $maxage;
$goto = $assessment->get_view_url($viewid, false);
if ($assessment->get('ctime') < $editableafter) {
$SESSION->add_error_msg(get_string('cantedittooold', 'artefact.peerassessment', $maxage));
redirect($goto);
}
$lastcomment = ArtefactTypePeerassessment::last_public_assessment($viewid);
if (!$assessment->get('private') && $id != $lastcomment->id) {
$SESSION->add_error_msg(get_string('cantedithasreplies', 'artefact.peerassessment'));
redirect($goto);
}
$elements = array();
$elements['message'] = array(
'type' => 'wysiwyg',
'title' => get_string('message'),
'rows' => 5,
'cols' => 80,
'defaultvalue' => $assessment->get('description'),
'rules' => array('maxlength' => 8192),
);
$elements['ispublic'] = array(
'type' => 'switchbox',
'title' => get_string('makeassessmentpublic', 'artefact.peerassessment'),
'defaultvalue' => !$assessment->get('private'),
);
if (get_config('licensemetadata')) {
$elements['license'] = license_form_el_basic($assessment);
$elements['licensing_advanced'] = license_form_el_advanced($assessment);
}
$elements['submit'] = array(
'type' => 'submitcancel',
'class' => 'btn-primary',
'value' => array(get_string('save'), get_string('cancel')),
'goto' => $goto,
);
$form = pieform(array(
'name' => 'edit_assessment',
'method' => 'post',
'plugintype' => 'artefact',
'pluginname' => 'peerassessment',
'elements' => $elements,
));
function edit_assessment_submit(Pieform $form, $values) {
global $assessment, $SESSION, $goto, $USER;
require_once('embeddedimage.php');
db_begin();
require_once(get_config('libroot') . 'view.php');
$view = $assessment->get_view();
$owner = $view->get('owner');
$newdescription = EmbeddedImage::prepare_embedded_images($values['message'], 'assessment', $assessment->get('id'));
$assessment->set('description', $newdescription);
$assessment->set('private', 1 - (int) $values['ispublic']);
$assessment->commit();
require_once('activity.php');
$data = (object) array(
'assessmentid' => $assessment->get('id'),
'viewid' => $viewid,
);
db_commit();
$SESSION->add_ok_msg(get_string('assessmentupdated', 'artefact.peerassessment'));
redirect($goto);