Commit 479ff998 authored by Cecilia Vela Gurovic's avatar Cecilia Vela Gurovic
Browse files

Bug 1857936: Lock page and artefact instructions

- A new page setting “Template” is added to the
“Advanced” panel on all portfolio page areas of
Mahara (personal, group, institution, site)
with a Yes/No switch
- added 2 columns to view table:
'templatetocopy': indicates if the view is a
  template that can be copied
'originaltemplate': if it has a value then the view
  is a copy of a template and should have
  the instructions locked.
  The value it contains is the viewid of
  the original template.
- if a view is a copy of a template: display
instructions in view settings and text blocks
but don't allow to edit them. If empty, don't show them.
- When adding a new text block to a page,
don't allow to add instructions
- Allow to remove the lock on a copy for roles:
    site admins in institution and site pages
    institution admins in institution pages
    group admins in group pages
- If a template view is deleted from the site,
then all the locked copies it has will remain locked.

behatnotneeded

Change-Id: I47e98cf8413da5ba6d5dea0e29dddbd80c481317
parent 86a57a9f
......@@ -115,16 +115,36 @@ class PluginBlocktypePeerassessment extends MaharaCoreBlocktype {
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' => 1000000),
),
);
if (!$instance->get('view_obj')->is_instruction_locked()) {
$elements = array (
'instructions' => array (
'type' => 'wysiwyg',
'title' => get_string('blockcontent', 'blocktype.peerassessment/peerassessment'),
'width' => '100%',
'height' => $height . 'px',
'defaultvalue' => $instructions,
'rules' => array('maxlength' => 1000000),
),
);
}
else {
$elements = array (
'instructionstitle' => array(
'type' => 'html',
'value' => '<a href="#instconf_instructions_container" aria-controls="instconf_instructions_container" class="" data-toggle="collapse"
aria-expanded="' . (!empty($instructions) ? 'true' : 'false') . '">'
. get_string('instructions', 'view')
. '<span class="icon icon-chevron-down collapse-indicator right text-inline block-config-modal"></span>'
. '</a>',
),
'instructions' => array (
'name' => 'instructions',
'type' => 'html',
'value' => clean_html($instructions),
'class' => !empty($instructions) ? 'show' : '',
),
);
}
return $elements;
}
......
......@@ -61,6 +61,7 @@ class PluginBlocktypeText extends MaharaCoreBlocktype {
$newinstructions = ArtefactTypeFolder::append_view_url($configdata['instructions'], $instance->get('view'));
$smarty->assign('instructions', $newinstructions);
$smarty->assign('blockid', $instance->get('id'));
$smarty->assign('editing', $editing);
}
}
else {
......@@ -94,17 +95,20 @@ class PluginBlocktypeText extends MaharaCoreBlocktype {
// show the draft switch only if it's a new text block or if it's still in draft
// once the text gets published it can't be set back to draft
$showdraftswitch = ($new || param_boolean('new', false) || (isset($configdata['draft']) && $configdata['draft']));
$elements = array (
'instructionstitle' => array(
'type' => 'html',
'value' => '<a href="#instconf_instructions_container" aria-controls="instconf_instructions_container" class="" data-toggle="collapse"
'value' => '<a href="#instconf_instructions_container" aria-controls="instconf_instructions_container"
class="' . (empty($instructions) ? 'collapsed' : '') . '" data-toggle="collapse"
aria-expanded="' . (!empty($instructions) ? 'true' : 'false') . '">'
. get_string('instructions', 'view')
. '<span class="icon icon-chevron-down collapse-indicator right text-inline block-config-modal"></span>'
. '</a>',
),
'instructions' => array (
);
if (!$instance->get('view_obj')->is_instruction_locked()) {
$elements['instructions'] = array (
'name' => 'instructions',
'type' => 'wysiwyg',
'width' => '100%',
......@@ -112,15 +116,28 @@ class PluginBlocktypeText extends MaharaCoreBlocktype {
'defaultvalue' => $instructions,
'rules' => array('maxlength' => 1000000),
'class' => (!empty($instructions) ? '' : 'collapse'),
),
'text' => array (
'type' => 'wysiwyg',
'title' => get_string('blockcontent', 'blocktype.text'),
'width' => '100%',
'height' => $height . 'px',
'defaultvalue' => $text,
'rules' => array('maxlength' => 1000000),
),
);
}
else {
if (empty($instructions)) {
unset($elements['instructionstitle']);
}
else {
$elements['instructions'] = array (
'name' => 'instructions',
'type' => 'html',
'value' => clean_html($instructions),
'class' => 'collapse',
);
}
}
$elements['text'] = array (
'type' => 'wysiwyg',
'title' => get_string('blockcontent', 'blocktype.text'),
'width' => '100%',
'height' => $height . 'px',
'defaultvalue' => $text,
'rules' => array('maxlength' => 1000000),
);
if ($showdraftswitch) {
$elements['draft'] = array(
......
......@@ -978,7 +978,7 @@
* changes the intructions so they are for ajax
*/
$('#blocksinstruction').html(strings['blocksinstructionajaxlive1']);
$('#viewinstructions-dropdown').on('hide.bs.collapse show.bs.collapse', function(event) {
$('.blocks #viewinstructions-dropdown').on('hide.bs.collapse show.bs.collapse', function(event) {
var pd = {
'viewid': $('#viewid').val(),
'action': event.type
......
......@@ -543,7 +543,18 @@ $string['accessibilitymodedescription'] = 'This page has the accessible layout e
To change a block position, navigate to it, grab it with the \'Enter\' key, and move it up and down the list of blocks with the arrow keys.';
$string['blocktypeis'] = ' %s blocktype';
// Cover image
$string['coverimage'] = 'Cover image';
$string['coverimagefolder'] = 'Cover images';
$string['coverimagedescription'] = 'The recommended dimensions are 180px wide by 130px high.';
// templates and copies
$string['locktemplate'] = 'Template';
$string['locktemplatedescription'] = 'When this is set to "Yes", people copying the page into their personal account will not be able to change any page or artefact instructions.';
$string['linktooriginaltemplate'] = 'Original template';
$string['linktooriginaltemplatedescription'] = 'This page is based on a template. This is the link to it.';
$string['linktooriginaltemplatedescriptiondeleted'] = 'This page is based on a template.';
$string['deletedview'] = 'Page deleted';
$string['copylocked'] = 'Copy locked';
$string['copylockeddescription'] = 'This is a template copy. Change this setting to lock/unlock the instruction fields on this copy.';
......@@ -758,6 +758,7 @@
<FIELD NAME="instructionscollapsed" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" />
<FIELD NAME="accessibleview" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" />
<FIELD NAME="coverimage" TYPE="int" LENGTH="10" NOTNULL="false"/>
<FIELD NAME="locktemplate" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" />
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" />
......@@ -1475,5 +1476,17 @@
<KEY NAME="usrfk" TYPE="foreign" FIELDS="usr" REFTABLE="usr" REFFIELDS="id"/>
</KEYS>
</TABLE>
<TABLE NAME="view_instructions_lock">
<FIELDS>
<FIELD NAME="view" TYPE="int" LENGTH="10" NOTNULL="true"/>
<FIELD NAME="originaltemplate" TYPE="int" LENGTH="10" NOTNULL="true"/>
<FIELD NAME="locked" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="view" />
<KEY NAME="viewfk" TYPE="foreign" FIELDS="view" REFTABLE="view" REFFIELDS="id"/>
<KEY NAME="templatefk" TYPE="foreign" FIELDS="originaltemplate" REFTABLE="view" REFFIELDS="id"/>
</KEYS>
</TABLE>
</TABLES>
</XMLDB>
......@@ -1812,5 +1812,26 @@ function xmldb_core_upgrade($oldversion=0) {
}
}
if ($oldversion < 2020060501) {
log_debug('Adding locktemplate column to view table');
$table = new XMLDBTable('view');
$field = new XMLDBField('locktemplate');
if (!field_exists($table, $field)) {
$field->setAttributes(XMLDB_TYPE_INTEGER, 1, null, XMLDB_NOTNULL, null, null, null, 0);
add_field($table, $field);
}
log_debug('Adding view_instructions_lock');
$table = new XMLDBTable('view_instructions_lock');
if (!table_exists($table)) {
$table->addFieldInfo('view', XMLDB_TYPE_INTEGER, 10, null, true);
$table->addFieldInfo('originaltemplate', XMLDB_TYPE_INTEGER, 10, null, true);
$table->addFieldInfo('locked', XMLDB_TYPE_INTEGER, 1, null, XMLDB_NOTNULL, null, null, null, 0);
$table->addKeyInfo('primary', XMLDB_KEY_PRIMARY, array('view'));
$table->addKeyInfo('viewfk', XMLDB_KEY_FOREIGN, array('view'), 'view', array('id'));
create_table($table);
}
}
return $status;
}
......@@ -16,7 +16,7 @@ $config = new stdClass();
// See https://wiki.mahara.org/wiki/Developer_Area/Version_Numbering_Policy
// For upgrades on stable branches, increment the version by one. On master, use the date.
$config->version = 2020060500;
$config->version = 2020060501;
$config->series = '20.10';
$config->release = '20.10dev';
$config->minupgradefrom = 2017031605;
......
......@@ -67,6 +67,7 @@ class View {
private $grid;
private $accessibleview = 0;
private $coverimage;
private $locktemplate = 0;
const UNSUBMITTED = 0;
const SUBMITTED = 1;
......@@ -466,6 +467,11 @@ class View {
// Lockblocks if set on template
$view->set('lockblocks', $template->get('lockblocks'));
if ($template->get('locktemplate')) {
$view->set('locktemplate', 0);
$view->lock_instructions_edit($template->get('id'));
}
$view->commit();
$blocks = get_records_array('block_instance', 'view', $view->get('id'));
......@@ -979,6 +985,7 @@ class View {
delete_records('view_visit','view',$this->id);
delete_records('view_versioning', 'view', $this->id);
delete_records('existingcopy', 'view', $this->id);
delete_records('view_instructions_lock', 'view', $this->id);
$eventdata = array('id' => $this->id, 'eventfor' => 'view');
if ($collection = $this->get_collection()) {
$eventdata['collection'] = $collection->get('id');
......@@ -7297,6 +7304,53 @@ class View {
public function get_progress_action($column = 'owner') {
return new ProgressAction($this, $column);
}
/**
* Checks if the view is a copy of a template and it has the instructions locked for edit
* @return boolean
*/
public function is_instruction_locked() {
if (get_field('view_instructions_lock', 'locked', 'view', $this->get('id'))) {
return true;
}
return false;
}
/*
* Gets the id of the view that this view is a copy of
* @return integer view id of the original view, 0 if this view is not a copy
*/
public function get_original_template() {
if (($originaltemplate = get_field('view_instructions_lock', 'originaltemplate', 'view', $this->get('id')))
&& get_record('view', 'id', $originaltemplate)) {
return $originaltemplate;
}
return 0;
}
/*
* Lock the instructions edit for this copy
* @param integer $templateid the view id of the template this view is a copy of
*/
public function lock_instructions_edit($templateid) { //todo: dont allow to change the template id if the record exists already
ensure_record_exists('view_instructions_lock',
(object) array(
'view'=> $this->get('id')
),
(object) array(
'view'=> $this->get('id'),
'originaltemplate'=> $templateid,
'locked'=> 1
)
);
}
/*
* Unlock the instructions edit for this copy
*/
public function unlock_instructions_edit() {
set_field('view_instructions_lock', 'locked', 0, 'view', $this->get('id'));
}
}
class ProgressAction {
......
......@@ -3,13 +3,13 @@
<fieldset class="pieform-fieldset collapsible collapsible-small">
<legend>
<h4>
<a href="#dropdown_{$blockid}" data-toggle="collapse" aria-expanded="false" aria-controls="dropdown" class="collapsed linkinstructions">
<a href="#dropdown_{$blockid}" data-toggle="collapse" aria-expanded="{if $editing}true{else}false{/if}" aria-controls="dropdown" class="{if $editing}show{else}collapsed{/if} linkinstructions">
{str tag='instructions' section='view'}
<span class="icon icon-chevron-down collapse-indicator right float-right"> </span>
</a>
</h4>
</legend>
<div class="fieldset-body collapse " id="dropdown_{$blockid}">
<div class="fieldset-body {if $editing}show{else}collapse{/if} " id="dropdown_{$blockid}">
{$instructions|clean_html|safe}
</div>
</fieldset>
......
......@@ -443,7 +443,8 @@ div.toolbarhtml {
}
#viewinstructions,
.peerinstructions {
.peerinstructions,
.blockinstructions {
&.with-toolbar {
margin-right: 20px;
}
......
......@@ -4,7 +4,7 @@
<span class="sr-only">{str tag=accessibilitymodedescription section=view}</span>
{/if}
<div class="view-instructions">
<div class="view-instructions blocks">
<form action="{$formurl}" method="post" class="row">
<input type="submit" name="{$action_name}" id="action-dummy" class="d-none">
<input type="hidden" id="viewid" name="id" value="{$view}">
......@@ -18,13 +18,13 @@
<fieldset class="pieform-fieldset collapsible collapsible-small">
<legend>
<h4>
<a href="#viewinstructions-dropdown" data-toggle="collapse" aria-expanded="false" aria-controls="viewinstructions-dropdown" class="{if $instructionscollapsed}collapsed{/if}">
<a href="#viewinstructions-dropdown" data-toggle="collapse" aria-expanded="{if $instructionscollapsed}false{else}true{/if}" aria-controls="viewinstructions-dropdown" class="{if $instructionscollapsed}collapsed{/if}">
{str tag='instructions' section='view'}
<span class="icon icon-chevron-down collapse-indicator right text-inline"></span>
</a>
</h4>
</legend>
<div class="fieldset-body collapse viewinstructions {if !$instructionscollapsed} in {/if}" id="viewinstructions-dropdown">
<div class="fieldset-body collapse viewinstructions {if !$instructionscollapsed} show {/if}" id="viewinstructions-dropdown">
{$instructions|clean_html|safe}
</div>
</fieldset>
......
......@@ -334,8 +334,19 @@ function get_advanced_elements() {
$ownerformatoptions[FORMAT_NAME_STUDENTID] = sprintf($formatstring, get_string('studentid'), $studentid);
}
$elements = array(
'instructions' => array(
$elements = array();
if ($view->is_instruction_locked()) {
if (!empty($view->get('instructions'))) {
$elements['instructions'] = array(
'type' => 'html',
'title' => get_string('instructions','view'),
'class' => 'view-description',
'value' => clean_html($view->get('instructions')),
);
}
}
else {
$elements['instructions'] = array(
'type' => 'wysiwyg',
'title' => get_string('instructions','view'),
'rows' => 5,
......@@ -343,17 +354,19 @@ function get_advanced_elements() {
'class' => 'view-description',
'defaultvalue' => $view->get('instructions'),
'rules' => array('maxlength' => 1000000),
),
'urlid' => array(
'type' => 'text',
'title' => get_string('viewurl', 'view'),
'prehtml' => '<span class="description">' . (isset($cleanurlbase) ? $cleanurlbase : '') . '</span> ',
'description' => get_string('viewurldescription', 'view') . ' ' . get_string('cleanurlallowedcharacters'),
'defaultvalue' => $view->get('urlid'),
'rules' => array('maxlength' => 100, 'regex' => get_config('cleanurlvalidate')),
'ignore' => !$urlallowed,
),
);
}
$elements['urlid'] = array(
'type' => 'text',
'title' => get_string('viewurl', 'view'),
'prehtml' => '<span class="description">' . (isset($cleanurlbase) ? $cleanurlbase : '') . '</span> ',
'description' => get_string('viewurldescription', 'view') . ' ' . get_string('cleanurlallowedcharacters'),
'defaultvalue' => $view->get('urlid'),
'rules' => array('maxlength' => 100, 'regex' => get_config('cleanurlvalidate')),
'ignore' => !$urlallowed,
);
if ($group) {
$grouproles = $USER->get('grouproles');
if ($grouproles[$group] == 'admin') {
......@@ -430,6 +443,67 @@ function get_advanced_elements() {
'unselectcallback' => 'delete_view_coverimage',
);
if (!$view->is_instruction_locked()) { //later i'll need to check the role of the login user
$elements['locktemplate'] = array(
'type' => 'switchbox',
'title' => get_string('locktemplate','view'),
'description' => get_string('locktemplatedescription','view'),
'defaultvalue' => $view->get('locktemplate'),
);
}
else {
if ($originaltemplate = $view->get_original_template()) {
$originaltemplate = new View($originaltemplate);
if (can_view_view($view)) {
$html = '<a href="' . $originaltemplate->get_url() . '">' . $originaltemplate->get('title') . '</a>';
}
else {
$html = $originaltemplate->get('title');
}
$description = get_string('linktooriginaltemplatedescription', 'view');
}
else {
$html = get_string('deletedview', 'view');
$description = get_string('linktooriginaltemplatedescriptiondeleted', 'view');
}
$elements['linktooriginaltemplate'] = array(
'type' => 'html',
'title' => get_string('linktooriginaltemplate', 'view'),
'value' => $html,
'description' => $description,
);
}
// give possibility to unlock the view to some roles
// site admins in institution and site pages
// institution admins in institution pages
// group admins in group pages
if (record_exists('view_instructions_lock', 'view', $view->get('id'))) {
$canremovelock = false;
// site admin
if ($USER->get('admin') && $view->get('institution')) {
$canremovelock = true;
}
//institution admin
else if ($institution = $view->get('institution') && $USER->is_institutional_admin($institution)) {
$canremovelock = true;
}
// group admin
else if ($group = $view->get('group')) {
$role = get_field('group_member', 'role', 'group', $group, 'member', $USER->get('id'));
if ($role == 'admin') {
$canremovelock = true;
}
}
if ($canremovelock) {
$elements['copylocked'] = array(
'type' => 'switchbox',
'title' => get_string('copylocked','view'),
'description' => get_string('copylockeddescription','view'),
'defaultvalue' => $view->is_instruction_locked(),
);
}
}
// Theme dropdown
$theme = $view->set_user_theme();
$allowedthemes = get_user_accessible_themes();
......@@ -592,6 +666,15 @@ function settings_submit(Pieform $form, $values) {
}
$view->set('coverimage', (isset($values['coverimage']) ? $values['coverimage'] : null));
if (isset($values['copylocked'])) {
if ($values['copylocked']) {
$view->lock_instructions_edit($view->get_original_template());
}
else {
$view->unlock_instructions_edit();
}
}
$view->commit();
$result = array(
......@@ -899,12 +982,15 @@ function set_view_title_and_description(Pieform $form, $values) {
if (isset($values['accessibleview'])) {
$view->set('accessibleview', (int)$values['accessibleview']);
}
if (isset($values['locktemplate'])) {
$view->set('locktemplate', (int)$values['locktemplate']);
}
}
function set_view_advanced(Pieform $form, $values) {
global $view, $urlallowed, $new;
if (trim($values['instructions']) !== '') {
if (isset($values['instructions']) && trim($values['instructions']) !== '') {
require_once('embeddedimage.php');
$view->set('instructions', EmbeddedImage::prepare_embedded_images($values['instructions'], 'instructions', $view->get('id')));
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment