Commit 882259ee authored by Robert Lyon's avatar Robert Lyon

Bug 1829940: Placeholder option update page with block of choice

This patch does the following:
- Make 'placeholder' configure options clickable so that on clicking
an option updates the block on the page and changes the configure form
to the form of the blocktype chosen
- On 'add placeholder' -> 'choose option' we have 'remove' option on
form and it removes the block from the page
- On 'add placeholder' -> 'save' -> 'configure block' -> 'choose option'
we have 'cancel' option on form and it reverts the block back to
'placeholder'

behatnotneeded

Change-Id: I1681ef92c5ccc14cda2167a040bb751501bfdbcd
Signed-off-by: Robert Lyon's avatarRobert Lyon <robertl@catalyst.net.nz>
parent 0f8d5b31
......@@ -19,6 +19,7 @@ safe_require('blocktype', 'placeholder');
$offset = param_integer('offset', 0);
$limit = param_integer('limit', 8);
$viewid = param_integer('viewid');
$blockid = param_integer('blockid');
if (!can_view_view($viewid)) {
json_reply('local', get_string('accessdenied', 'error'));
}
......@@ -35,10 +36,12 @@ $pagination = build_showmore_pagination(array(
'databutton' => 'showmorebtn',
'jscall' => 'wire_blockoptions',
'jsonscript' => 'blocktype/placeholder/blockoptions.json.php',
'extra' => array('viewid' => $viewid),
'extra' => array('viewid' => $viewid,
'blockid' => $blockid),
));
$smarty = smarty_core();
$smarty->assign('blockid', $blockid);
$smarty->assign('types', $types);
$typeslist = $smarty->fetch('blocktype:placeholder:contenttypeslist.tpl');
$typeslist .= $pagination['html'];
......
......@@ -65,9 +65,11 @@ class PluginBlocktypePlaceholder extends MaharaCoreBlocktype {
'databutton' => 'showmorebtn',
'jscall' => 'wire_blockoptions',
'jsonscript' => 'blocktype/placeholder/blockoptions.json.php',
'extra' => array('viewid' => $view->get('id')),
'extra' => array('viewid' => $view->get('id'),
'blockid' => $instance->get('id')),
));
$smarty = smarty_core();
$smarty->assign('blockid', $instance->get('id'));
$smarty->assign('types', $types);
$typeslist = $smarty->fetch('blocktype:placeholder:contenttypeslist.tpl');
$smarty->assign('typeslist', $typeslist);
......
......@@ -110,12 +110,16 @@
rewriteConfigureButton(newblock.find('.configurebutton'));
rewriteDeleteButton(newblock.find('.deletebutton'));
}
hideDock();
showMediaPlayers();
setTimeout(function() {
newblock.find('.configurebutton').trigger("focus");
}, 1);
if (data.closemodal) {
hideDock();
showMediaPlayers();
setTimeout(function() {
newblock.find('.configurebutton').trigger("focus");
}, 1);
}
else {
return newblock;
}
};
/**
......@@ -126,6 +130,7 @@
if (data.formelementsuccess) {
eval(data.formelementsuccess + '(form, data)');
}
data.closemodal = true;
if (data.blockid) {
ViewManager.replaceConfigureBlock(data);
}
......@@ -173,7 +178,83 @@
}
ViewManager.blockOptions = function() {
console.log('in block options');
$('#placeholderlist .card-option .card').each(function (idx, val) {
$(val).off();
$(val).on('click', function(ev, d) {
ev.stopPropagation();
ev.preventDefault();
var blockid = $(ev.currentTarget).data('blockid');
var option = $(ev.currentTarget).data('option');
var title = encodeURIComponent($('#instconf_title').val());
var isnew = $('#instconf_new').val() == '1' ? '1' : '0';
var pd = {
'id': $('#viewid').val(),
'change': 1,
'blocktype': 'placeholder',
};
pd['action_changeblockinstance_id_' + blockid + '_new_' + isnew + '_blocktype_' + option + '_title_' + title] = true;
sendjsonrequest(config['wwwroot'] + 'view/blocks.json.php', pd, 'POST', function(data) {
if (data.data.returnCode == 1) {
console.log('error: ' + data.data.message);
}
else {
console.log('success: ' + data.data.message);
// Update block on page to be of new type
var newdata = {};
newdata.blockid = data.data.blockid;
newdata.viewid = data.data.viewid;
newdata.data = {};
newdata.data.html = data.data.display.html;
newdata.data.javascript = data.data.display.javascript;
var blockinstance = ViewManager.replaceConfigureBlock(newdata);
if (data.data.configure) {
// The new block has configuration so update config modal to have new config form
if (data.data.isnew) {
addConfigureBlock(blockinstance, data.data.configure, true);
}
else {
// wire up the cancel button on chosen blocktype form to revert the block back to placeholder block
addConfigureBlock(blockinstance, data.data.configure);
var blockinstanceId = blockinstance.attr('data-id');
var cancelbutton = jQuery('#cancel_instconf_action_configureblockinstance_id_' + blockinstanceId);
cancelbutton.off('click');
cancelbutton.on('click',function(e) {
e.stopPropagation();
e.preventDefault();
var revpd = {
'id': $('#viewid').val(),
'change': 1,
'blocktype': 'placeholder',
};
revpd['action_revertblockinstance_id_' + data.data.blockid + '_title_' + data.data.oldtitle] = true;
sendjsonrequest(config['wwwroot'] + 'view/blocks.json.php', revpd, 'POST', function(revdata) {
if (data.data.returnCode == 1) {
console.log('error: ' + revdata.data.message);
}
else {
console.log('success: ' + revdata.data.message);
var revnewdata = {};
revnewdata.blockid = revdata.data.blockid;
revnewdata.viewid = revdata.data.viewid;
revnewdata.data = {};
revnewdata.data.html = revdata.data.display.html;
revnewdata.data.javascript = revdata.data.display.javascript;
var blockinstance = ViewManager.replaceConfigureBlock(revnewdata);
var configbutton = jQuery('.view-container button[name="action_configureblockinstance_id_' + revdata.data.blockid + '"]');
onModalCancel(e, configbutton);
}
});
});
}
}
else {
// No configure form so we just need to close the modal
hideDock();
}
}
});
});
});
}
//Private Methods
......
......@@ -79,6 +79,7 @@ $string['blocktypeprovidedbyartefactnotinstallable'] = 'This will be installed a
$string['blockconfigdatacalledfromset'] = 'Configdata should not be set directly. Use PluginBlocktype::instance_config_save instead.';
$string['invaliddirection'] = 'Invalid direction %s.';
$string['onlyoneprofileviewallowed'] = 'You are only allowed one profile page.';
$string['cannotputblocktypeintoview'] = 'Cannot put %s blocktypes into this page';
$string['onlyoneblocktypeperview'] = 'Cannot put more than one %s block type into a page.';
// if you change these next two , be sure to change them in libroot/errors.php
......
......@@ -304,6 +304,10 @@ $string['blockconfigurationrenderingerror'] = 'Configuration failed because the
$string['blocksintructionnoajax'] = 'Select a block and choose where to add it to your page. You can position a block using the arrow buttons in its titlebar.';
$string['blocksinstructionajaxlive'] = 'This area shows a preview of what your page looks like. Changes are saved automatically.<br>Drag blocks onto the page to add them. You can then also drag them around the page to change their position.';
$string['blockchangedsuccess'] = "Changed placeholder block to a '%s' block successful";
$string['blockchangederror'] = "Changing block to a '%s' block failed";
$string['blockchangedbacksuccess'] = "Changed block back to placeholder block";
$string['blockchangedbackerror'] = "Changing block back to placeholder block failed";
$string['addblock'] = 'Add block: %s';
$string['blockcell'] = 'Cell';
......
......@@ -2071,7 +2071,6 @@ class View {
if (substr($actionstring, -2) == '_x' || substr($actionstring, -2) == '_y') {
$actionstring = substr($actionstring, 0, -2);
}
$values = self::get_values_for_action($actionstring);
}
......@@ -2085,6 +2084,8 @@ class View {
break;
case 'removeblockinstance': // requires action_removeblockinstance_id_\d
break;
case 'changeblockinstance': // requires action_changeblockinstance_id_\d_new_\d_blocktype_\s_title_\s
case 'revertblockinstance': // requires action_revertblockinstance_id_\d_title_\s
case 'configureblockinstance': // requires action_configureblockinstance_id_\d_column_\d_order_\d
case 'acsearch': // requires action_acsearch_id_\d
case 'moveblockinstance': // requires action_moveblockinstance_id_\d_row_\d_column_\d_order_\d
......@@ -2401,7 +2402,7 @@ class View {
safe_require('blocktype', $values['blocktype']);
if (!call_static_method(generate_class_name('blocktype', $values['blocktype']), 'allowed_in_view', $this)) {
throw new UserException('[translate] Cannot put ' . $values['blocktype'] . ' blocktypes into this view');
throw new UserException(get_string('cannotputblocktypeintoview', error, $values['blocktype']));
}
if (call_static_method(generate_class_name('blocktype', $values['blocktype']), 'single_only', $this)) {
......@@ -2490,6 +2491,138 @@ class View {
$this->dirtycolumns[$bi->get('row')][$bi->get('column')] = 1;
}
/**
* Changes a placeholder block into the new type of block
*/
public function changeblockinstance($values) {
$currentblock = get_record('block_instance', 'id', $values['id']); // get direct from db as we want to change it
$requires = array('blocktype');
foreach ($requires as $require) {
if (!array_key_exists($require, $values) || empty($values[$require])) {
throw new ParamOutOfRangeException(get_string('missingparam'. $require, 'error'));
}
}
safe_require('blocktype', $values['blocktype']);
if (!call_static_method(generate_class_name('blocktype', $values['blocktype']), 'allowed_in_view', $this)) {
throw new UserException(get_string('cannotputblocktypeintoview', error, $values['blocktype']));
}
if (call_static_method(generate_class_name('blocktype', $values['blocktype']), 'single_only', $this)) {
$count = count_records_select('block_instance', '"view" = ? AND blocktype = ?',
array($this->id, $values['blocktype']));
if ($count > 0) {
throw new UserException(get_string('onlyoneblocktypeperview', 'error', $values['blocktype']));
}
}
$blocktypeclass = generate_class_name('blocktype', $values['blocktype']);
$newtitle = method_exists($blocktypeclass, 'get_instance_title') ? '' : call_static_method($blocktypeclass, 'get_title');
if (!empty($values['title'])) {
$newtitle = hsc(urldecode($values['title']));
}
$currentblocktags = get_records_sql_assoc("SELECT id, tag FROM {tag} WHERE resourcetype = ? AND resourceid = ?", array('blocktype', $currentblock->id));
// Set up a dummy block instance of new blocktype with the data we need
// So we can get the initial display and configure form data
$bi = new BlockInstance(0,
array(
'id' => $currentblock->id,
'blocktype' => $values['blocktype'],
'title' => $newtitle,
'view' => $this->get('id'),
'view_obj' => $this,
'row' => $currentblock->row,
'column' => $currentblock->column,
'order' => $currentblock->order,
)
);
$result = array('blockid' => $currentblock->id,
'viewid' => $currentblock->view,
'newblocktype' => $values['blocktype']);
if ($currentblocktags) {
// We need to decide what to do with placeholder block tags
$droptags = true;
$cform = method_exists($blocktypeclass, 'has_instance_config') ? call_static_method($blocktypeclass, 'instance_config_form', $bi) : false;
if ($cform) {
foreach ($cform as $element) {
if ($element['type'] == 'tags') {
$droptags = false;
}
}
}
if ($droptags) {
foreach ($currentblocktags as $t) {
execute_sql("DELETE FROM {tag} WHERE id = ?", array($t->id));
}
}
}
$newdata = array('title' => $newtitle, 'blocktype' => $values['blocktype'], 'configdata' => serialize(array()));
$update = update_record('block_instance', (object) $newdata, (object) array('id' => $values['id']));
if (!$update) {
$result['returnCode'] = 1;
$result['message'] = get_string('blockchangederror', 'view', $values['blocktype']);
}
else {
// Return new block rendered in both configure mode and (editing) display mode
$isnew = (bool)$values['new'];
$result['display'] = $bi->render_editing(false, $isnew);
if (call_static_method(generate_class_name('blocktype', $values['blocktype']), 'has_instance_config')) {
$result['configure'] = $bi->render_editing(true, $isnew);
}
else {
$result['configure'] = false;
}
$result['returnCode'] = 0;
$result['message'] = get_string('blockchangedsuccess', 'view', $values['blocktype']);
$result['isnew'] = $isnew;
$result['oldtitle'] = $currentblock->title;
}
return $result;
}
/**
* Changes a placeholder block into the new type of block
*/
public function revertblockinstance($values) {
$currentblock = get_record('block_instance', 'id', $values['id']); // get direct from db as we want to change it
safe_require('blocktype', 'placeholder');
$oldtitle = hsc(urldecode($values['title']));
// Set up a dummy block instance of new blocktype with the data we need
// So we can get the initial display and configure form data
$bi = new BlockInstance(0,
array(
'id' => $currentblock->id,
'blocktype' => 'placeholder',
'title' => $oldtitle,
'view' => $this->get('id'),
'view_obj' => $this,
'row' => $currentblock->row,
'column' => $currentblock->column,
'order' => $currentblock->order,
)
);
$result = array('blockid' => $currentblock->id,
'viewid' => $currentblock->view,
'newblocktype' => 'placeholder');
$newdata = array('title' => $oldtitle, 'blocktype' => 'placeholder', 'configdata' => serialize(array()));
$update = update_record('block_instance', (object) $newdata, (object) array('id' => $values['id']));
if (!$update) {
$result['returnCode'] = 1;
$result['message'] = get_string('blockchangedbackerror', 'view', $values['blocktype']);
}
else {
// Return new block rendered in both configure mode and (editing) display mode
$result['display'] = $bi->render_editing(false, false);
$result['returnCode'] = 0;
$result['message'] = get_string('blockchangedbacksuccess', 'view');
}
return $result;
}
/**
* moves a block instance to a specified location
*
......
......@@ -76,6 +76,7 @@ define ("LOCATOR_CONSTANTS", json_encode(array(
'Show more tags' => array(".text-small .icon-ellipsis-h", "css_element"),
'Terms and conditions Edit icon' => array("#termsandconditions .btn-secondary","css_element"),
'Privacy statement Edit icon' => array("#privacy .btn-group","css_element"),
'Content types' => array("#placeholderlist", "css_element"),
// properties with xpath_elements
'Settings sub-menu' => array("//span[@innertext='Settings']", "xpath_element"),
'Settings' => array("//ul[#'userchildmenu-8']/?/?/a[@innertext='Settings']", "xpath_element"),
......
{foreach from=$types item=type}
<div class="card-option card-quarter">
<div class="card placeholder btn-secondary" data-option="{$type.name}" title="{$type.description}">
<button class="card placeholder btn-secondary" data-option="{$type.name}" title="{$type.description}" data-blockid="{$blockid}">
<div class="icon icon-lg icon-{$type.cssicon}"></div>
<div>{$type.title}</div>
</div>
</button>
</div>
{/foreach}
......@@ -232,9 +232,10 @@
@include media-breakpoint-up(md) {
width: 25%;
}
.card {
.card.placeholder {
padding: 10px;
cursor: pointer;
width: 100%;
div {
text-align: center;
&.icon {
......
......@@ -2,6 +2,7 @@
Feature: Adding a placeholder block to a page
As a student
I need to be able to add a placeholder block to my portfolio
and then change it to be a block of my choosing
Background:
Given the following "users" exist:
......@@ -23,5 +24,17 @@ Scenario:
And I fill in the following:
| Block title | Mahara placeholder block |
And I press "Save"
And I display the page
Then I should see "Please configure the block to choose what type of block this should be"
# Edit placeholder block and check we can see more options
And I configure the block "Mahara placeholder block"
And I click on "Show more"
Then I should see "Image gallery"
# Change placeholder block to a text block
And I fill in the following:
| Block title | Mahara text block title |
And I click on "Text" in the "Content types" property
And I set the field "Block content" to "Mahara text block content"
And I press "Save"
Then I should see "Mahara text block title"
Then I should see "Mahara text block content"
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