Commit 2c861ec1 authored by Cecilia Vela Gurovic's avatar Cecilia Vela Gurovic Committed by Robert Lyon

Bug 1813987: Creating a new page with gristack layout

add a new block, place it anywhere in the grid, edit configuration
and delete it.

It will save it in the db on table block_instance_dimension
but not display it in view or edit mode yet

Also included a small fix in xmldb postgres class generator
to handle reserved words in getAlterFieldSQL function

Failing tests:

- most of them are failing when adding a new block to the page
because they expect to have a modal to choose the position
where to place block. That modal was removed for this patch but
there will be a similar one in patch:
 https://reviews.mahara.org/#/c/9952
and those tests will be fixed there

- a few tests failed when they couldn't find text inside the
blocks because they are not expanded to fit the content yet.
This is added in patch:
 https://reviews.mahara.org/#/c/9986
and they will be fixed there

behatnotneeded

Change-Id: If4521a6315f6e8cc5d88693f536946dace359288
parent 2a9211b3
...@@ -844,6 +844,10 @@ class BlockInstance { ...@@ -844,6 +844,10 @@ class BlockInstance {
private $temp = array(); private $temp = array();
private $tags = array(); private $tags = array();
private $inedit = false; private $inedit = false;
private $positionx;
private $positiony;
private $width;
private $height;
public function __construct($id=0, $data=null) { public function __construct($id=0, $data=null) {
if (!empty($id)) { if (!empty($id)) {
...@@ -869,6 +873,30 @@ class BlockInstance { ...@@ -869,6 +873,30 @@ class BlockInstance {
$this->{$field} = $value; $this->{$field} = $value;
} }
} }
$dimensiontable_exists = true;
if (defined('INSTALLER')) {
// Check to see if the table exists yet
require_once('ddl.php');
$dimensiontable_exists = table_exists(new XMLDBTable('block_instance_dimension'));
}
if ($dimensiontable_exists) {
$dimension = get_records_array('block_instance_dimension', 'block', $id);
if (is_array($dimension) && isset($dimension[0])) {
$this->positionx = $dimension[0]->positionx;
$this->positiony = $dimension[0]->positiony;
$this->width = $dimension[0]->width;
$this->height = $dimension[0]->height;
}
}
else {
$this->positionx = 0;
$this->positiony = 0;
$this->width = 4;
$this->height = 3;
}
$this->artefactplugin = blocktype_artefactplugin($this->blocktype); $this->artefactplugin = blocktype_artefactplugin($this->blocktype);
} }
...@@ -1200,6 +1228,12 @@ class BlockInstance { ...@@ -1200,6 +1228,12 @@ class BlockInstance {
$smarty->assign('row', $this->get('row')); $smarty->assign('row', $this->get('row'));
$smarty->assign('column', $this->get('column')); $smarty->assign('column', $this->get('column'));
$smarty->assign('order', $this->get('order')); $smarty->assign('order', $this->get('order'));
$smarty->assign('positionx', $this->get('positionx'));
$smarty->assign('positiony', $this->get('positiony'));
$smarty->assign('width', $this->get('width'));
$smarty->assign('height', $this->get('height'));
$smarty->assign('blocktype', $this->get('blocktype')); $smarty->assign('blocktype', $this->get('blocktype'));
$smarty->assign('configurable', call_static_method($blocktypeclass, 'has_instance_config')); $smarty->assign('configurable', call_static_method($blocktypeclass, 'has_instance_config'));
$smarty->assign('configure', $configure); // Used by the javascript to rewrite the block, wider. $smarty->assign('configure', $configure); // Used by the javascript to rewrite the block, wider.
...@@ -1725,6 +1759,7 @@ class BlockInstance { ...@@ -1725,6 +1759,7 @@ class BlockInstance {
call_static_method($classname, 'delete_instance', $this); call_static_method($classname, 'delete_instance', $this);
} }
delete_records('view_artefact', 'block', $this->id); delete_records('view_artefact', 'block', $this->id);
delete_records('block_instance_dimension', 'block', $this->id);
delete_records('block_instance', 'id', $this->id); delete_records('block_instance', 'id', $this->id);
delete_records('tag', 'resourcetype', 'blocktype', 'resourceid', $this->id); delete_records('tag', 'resourcetype', 'blocktype', 'resourceid', $this->id);
db_commit(); db_commit();
...@@ -2084,6 +2119,23 @@ class BlockInstance { ...@@ -2084,6 +2119,23 @@ class BlockInstance {
public static function group_tabs($groupid, $role) { public static function group_tabs($groupid, $role) {
return array(); return array();
} }
public function set_block_dimensions($positionx, $positiony, $width, $height) {
$obj = new StdClass();
$obj->block = $this->id;
$obj->positionx = $positionx;
$obj->positiony = $positiony;
$obj->height = $height;
$obj->width = $width;
//TODO: move this inside of the commit
ensure_record_exists('block_instance_dimension', (object) array('block' => $this->id), $obj);
$this->set('positionx', $positionx);
$this->set('positiony', $positiony);
$this->set('height', $height);
$this->set('width', $width);
}
} }
function require_blocktype_plugins() { function require_blocktype_plugins() {
......
...@@ -290,6 +290,31 @@ ...@@ -290,6 +290,31 @@
location.reload(); location.reload();
}); });
var serializeWidgetMap = function(items) {
// conseguir el id del bloque
// json call to update new position and/or dimension
var i;
if (typeof(items) != 'undefined') {
for (i=0; i<items.length; i++) {
if (typeof(items[i].id) != 'undefined') {
var blockid = items[i].id,
destination = {
'newx': items[i].x,
'newy': items[i].y,
'newheight': items[i].height,
'newwidth': items[i].width,
}
moveBlock(blockid, destination);
}
}
}
};
$('.grid-stack').on('change', function(event, items) {
serializeWidgetMap(items);
})
// images need time to load before height can be properly calculated // images need time to load before height can be properly calculated
window.setTimeout(function(){ window.setTimeout(function(){
$(window).trigger('colresize'); $(window).trigger('colresize');
...@@ -352,8 +377,6 @@ ...@@ -352,8 +377,6 @@
$(category).html(data.data); $(category).html(data.data);
makeNewBlocksDraggable(); makeNewBlocksDraggable();
// the column has changed size, pass on to listeners
$(window).trigger('colresize');
}); });
return false; return false;
} }
...@@ -422,7 +445,6 @@ ...@@ -422,7 +445,6 @@
$('.blocktype-drag').draggable({ $('.blocktype-drag').draggable({
start: function(event, ui) { start: function(event, ui) {
$(window).trigger('colresize');
}, },
helper: function(event) { helper: function(event) {
var original = $(this), var original = $(this),
...@@ -448,37 +470,14 @@ ...@@ -448,37 +470,14 @@
if (isHit(e) && !$('#addblock').hasClass('in')) { if (isHit(e) && !$('#addblock').hasClass('in')) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
startAddBlock($(this)); if (!addblockstarted) {
addblockstarted = true;
addNewBlock($(this).find('.blocktype-radio').val());
}
} }
}); });
} }
var addblockstarted = false; // To stop the double clicking of add block button causing multiple saving problem
function startAddBlock(element) {
var addblockdialog = $('#addblock');
addblockdialog.modal('show');
if (!addblockstarted) {
addblockstarted = true;
addblockdialog.one('dialog.end', function(event, options) {
if (options.saved) {
addNewBlock(options, element.find('.blocktype-radio').val());
}
else {
element.trigger("focus");
}
});
addblockdialog.find('h4.modal-title').text(get_string('addnewblock', 'view'));
computeColumnInputs(addblockdialog);
addblockdialog.find('.block-inner').removeClass('d-none');
addblockdialog.find('.cell-chooser input:first').prop('checked', true);
addblockdialog.find('.cell-chooser input:first').parent().addClass('focused active');
addblockdialog.find('.deletebutton').trigger("focus");
keytabbinginadialog(addblockdialog, addblockdialog.find('.deletebutton'), addblockdialog.find('.cancel'));
}
}
function cellChanged() { function cellChanged() {
$(this).closest('.js-cell-chooser').find('.active').removeClass('active'); $(this).closest('.js-cell-chooser').find('.active').removeClass('active');
...@@ -497,9 +496,8 @@ ...@@ -497,9 +496,8 @@
selectbox.html('<option>' + options.join('</option><option>') + '</option>'); selectbox.html('<option>' + options.join('</option><option>') + '</option>');
} }
function addNewBlock(whereTo, blocktype) { var addblockstarted = false; // To stop the double clicking of add block button causing multiple saving problem
function addNewBlock(blocktype) {
addblockstarted = false;
var pd = { var pd = {
'id': $('#viewid').val(), 'id': $('#viewid').val(),
'change': 1, 'change': 1,
...@@ -509,23 +507,17 @@ ...@@ -509,23 +507,17 @@
if (config.blockeditormaxwidth) { if (config.blockeditormaxwidth) {
pd['cfheight'] = $(window).height() - 100; pd['cfheight'] = $(window).height() - 100;
} }
pd['action_addblocktype_row_' + whereTo['row'] + '_column_' + whereTo['column'] + '_order_' + whereTo['order']] = true; pd['action_addblocktype_positionx_0_positiony_0_width_3_height_3'] = true;// The default 3x3 block at position 0,0
sendjsonrequest(config['wwwroot'] + 'view/blocks.json.php', pd, 'POST', function(data) { sendjsonrequest(config['wwwroot'] + 'view/blocks.json.php', pd, 'POST', function(data) {
var div = $('<div>').html(data.data.display.html), var div = $('<div>').html(data.data.display.html),
blockinstance = div.find('div.blockinstance'), blockinstance = div.find('div.grid-stack-item'),
configureButton = blockinstance.find('.configurebutton'); configureButton = blockinstance.find('.configurebutton');
addBlockCss(data.css); addBlockCss(data.css);
// Make configure button clickable, but disabled as blocks are rendered in configure mode by default
if (configureButton) {
rewriteConfigureButton(configureButton);
$('#action-dummy').attr('name', 'action_addblocktype_row_' + whereTo['row'] + '_column_' + whereTo['column'] + '_order_' + whereTo['order']);
}
insertBlockStub(blockinstance, whereTo); var grid = $('.grid-stack').data('gridstack');
addNewWidget(blockinstance, grid);
if (data.data.configure) { if (data.data.configure) {
showDock($('#configureblock'), true); showDock($('#configureblock'), true);
...@@ -535,10 +527,12 @@ ...@@ -535,10 +527,12 @@
rewriteDeleteButton(blockinstance.find('.deletebutton')); rewriteDeleteButton(blockinstance.find('.deletebutton'));
blockinstance.find('.deletebutton').trigger("focus"); blockinstance.find('.deletebutton').trigger("focus");
} }
addblockstarted = false;
}, },
function() { function() {
// On error callback we need to reset the Dock // On error callback we need to reset the Dock
hideDock(); hideDock();
addblockstarted = false;
}); });
} }
...@@ -550,26 +544,6 @@ ...@@ -550,26 +544,6 @@
}); });
} }
function insertBlockStub(newblock, whereTo) {
var columnContent = $('#row_'+whereTo['row']+'_column_'+whereTo['column']).find('div.column-content');
if (whereTo['order'] == 1) {
$(columnContent).prepend(newblock);
}
else {
var count = 1;
columnContent.children().each(function() {
count++;
if (count == whereTo['order']) {
$(this).after(newblock);
return false;
}
});
if (whereTo['order'] > count) {
columnContent.append(newblock);
}
}
}
/** /**
* Rewrites the blockinstance configure buttons to be AJAX * Rewrites the blockinstance configure buttons to be AJAX
*/ */
...@@ -630,10 +604,14 @@ ...@@ -630,10 +604,14 @@
pd[self.attr('name')] = 1; pd[self.attr('name')] = 1;
sendjsonrequest(config['wwwroot'] + 'view/blocks.json.php', pd, 'POST', function(data) { sendjsonrequest(config['wwwroot'] + 'view/blocks.json.php', pd, 'POST', function(data) {
if (blockinstanceId !== undefined && blockinstanceId !== null) { if (blockinstanceId !== undefined && blockinstanceId !== null) {
$('#blockinstance_' + blockinstanceId).remove(); $('#blockinstance_' + blockinstanceId).remove();
} }
var grid = $('.grid-stack').data('gridstack');
grid.removeWidget($('#block_' + blockinstanceId));
if (!$('#configureblock').hasClass('d-none')) { if (!$('#configureblock').hasClass('d-none')) {
hideDock(); hideDock();
showMediaPlayers(); showMediaPlayers();
...@@ -722,20 +700,15 @@ ...@@ -722,20 +700,15 @@
} }
function moveBlock(whereTo, instanceId) { function moveBlock(id, whereTo) {
var pd = { var pd = {
'id': $('#viewid').val(), 'id': $('#viewid').val(),
'change': 1 'change': 1
}; };
if (config.blockeditormaxwidth) {
pd['cfheight'] = $(window).height() - 100; pd['action_moveblockinstance_id_' + id + '_newx_' + whereTo['newx'] + '_newy_' + whereTo['newy'] + '_newheight_' + whereTo['newheight'] + '_newwidth_' + whereTo['newwidth']] = true;
}
pd['action_moveblockinstance_id_' + instanceId + '_row_' + whereTo['row'] + '_column_' + whereTo['column'] + '_order_' + whereTo['order']] = true; sendjsonrequest(config['wwwroot'] + 'view/blocks.json.php', pd, 'POST');
sendjsonrequest(config['wwwroot'] + 'view/blocks.json.php', pd, 'POST', function(data) {
if (data.data.html) {
$('#blockinstance_' + instanceId + ' .blockinstance-content').html(data.data.html);
}
});
} }
/** /**
...@@ -753,7 +726,9 @@ ...@@ -753,7 +726,9 @@
sendjsonrequest(config['wwwroot'] + 'view/blocks.json.php', pd, 'POST', function(data) { sendjsonrequest(config['wwwroot'] + 'view/blocks.json.php', pd, 'POST', function(data) {
$('#blockinstance_' + blockinstanceId).remove(); var grid = $('.grid-stack').data('gridstack'),
item = $('#block_' + blockinstanceId);
grid.removeWidget(item);
if (!$('#configureblock').hasClass('d-none')) { if (!$('#configureblock').hasClass('d-none')) {
hideDock(); hideDock();
...@@ -1076,3 +1051,15 @@ function blockConfigError(form, data) { ...@@ -1076,3 +1051,15 @@ function blockConfigError(form, data) {
function wire_blockoptions() { function wire_blockoptions() {
return ViewManager.blockOptions(); return ViewManager.blockOptions();
} }
/* GRIDSTACK functions */
function addNewWidget(blockContent, grid) {
var node = {
x: 0,
y: 0,
width: 3,
height: 3
};
grid.addWidget(blockContent, node.x, node.y, node.width, node.height);
return false;
}
...@@ -1392,21 +1392,38 @@ function xmldb_core_upgrade($oldversion=0) { ...@@ -1392,21 +1392,38 @@ function xmldb_core_upgrade($oldversion=0) {
if ($oldversion < 2019080600) { if ($oldversion < 2019080600) {
log_debug('create block dimension table for gridstack layout'); log_debug('create block dimension table for gridstack layout');
$table = new XMLDBTable('block_instance_dimension'); $table = new XMLDBTable('block_instance_dimension');
$table->addFieldInfo('block', XMLDB_TYPE_INTEGER, 10, false, XMLDB_NOTNULL); if (!table_exists($table)) {
$table->addFieldInfo('positionx', XMLDB_TYPE_INTEGER, 2, false, XMLDB_NOTNULL, null, null, null, 0); $table->addFieldInfo('block', XMLDB_TYPE_INTEGER, 10, false, XMLDB_NOTNULL);
$table->addFieldInfo('positiony', XMLDB_TYPE_INTEGER, 10, false, XMLDB_NOTNULL, null, null, null, 0); $table->addFieldInfo('positionx', XMLDB_TYPE_INTEGER, 2, false, XMLDB_NOTNULL, null, null, null, 0);
$table->addFieldInfo('width', XMLDB_TYPE_INTEGER, 2, false, XMLDB_NOTNULL, null, null, null, 4); $table->addFieldInfo('positiony', XMLDB_TYPE_INTEGER, 10, false, XMLDB_NOTNULL, null, null, null, 0);
$table->addFieldInfo('height', XMLDB_TYPE_INTEGER, 2, false, XMLDB_NOTNULL, null, null, null, 4); $table->addFieldInfo('width', XMLDB_TYPE_INTEGER, 2, false, XMLDB_NOTNULL, null, null, null, 4);
$table->addKeyInfo('primary', XMLDB_KEY_PRIMARY, array('block')); $table->addFieldInfo('height', XMLDB_TYPE_INTEGER, 2, false, XMLDB_NOTNULL, null, null, null, 4);
$table->addKeyInfo('blockfk', XMLDB_KEY_FOREIGN, array('block'), 'block_instance', array('id')); $table->addKeyInfo('primary', XMLDB_KEY_PRIMARY, array('block'));
$table->addKeyInfo('blockfk', XMLDB_KEY_FOREIGN, array('block'), 'block_instance', array('id'));
create_table($table);
create_table($table);
}
log_debug('drop constraint from block_instance table in row, column and order'); log_debug('drop constraint from block_instance table in row, column and order');
$table = new XMLDBTable('block_instance'); $table = new XMLDBTable('block_instance');
$key = new XMLDBKey('viewrowcolumnorderuk'); $key = new XMLDBKey('viewrowcolumnorderuk');
$key->setAttributes(XMLDB_KEY_UNIQUE, array('view', 'row', 'column', 'order')); $key->setAttributes(XMLDB_KEY_UNIQUE, array('view', 'row', 'column', 'order'));
drop_key($table, $key); drop_key($table, $key);
log_debug('remove NOT NULL modifier from row, column, order in block_instance table');
$table = new XMLDBTable('block_instance');
$field = new XMLDBField('row');
$field->setAttributes(XMLDB_TYPE_INTEGER, 2, null, null, null, null, null, null);
change_field_notnull($table, $field);
$field = new XMLDBField('column');
$field->setAttributes(XMLDB_TYPE_INTEGER, 2, null, null, null, null, null, null);
change_field_notnull($table, $field);
$field = new XMLDBField('order');
$field->setAttributes(XMLDB_TYPE_INTEGER, 2, null, null, null, null, null, null);
change_field_notnull($table, $field);
} }
return $status; return $status;
......
This diff is collapsed.
...@@ -255,7 +255,7 @@ class XMLDBpostgres extends XMLDBgenerator { ...@@ -255,7 +255,7 @@ class XMLDBpostgres extends XMLDBgenerator {
/// Take a look to field metadata /// Take a look to field metadata
$meta = array_change_key_case($db->MetaColumns($tablename)); $meta = array_change_key_case($db->MetaColumns($tablename));
$metac = $meta[$fieldname]; $metac = $meta[$xmldb_field->getName()];
$oldtype = strtolower($metac->type); $oldtype = strtolower($metac->type);
$oldmetatype = column_type($xmldb_table->getName(), $fieldname); $oldmetatype = column_type($xmldb_table->getName(), $fieldname);
$oldlength = $metac->max_length; $oldlength = $metac->max_length;
......
...@@ -15,6 +15,7 @@ $animation_speed: .3s !default; ...@@ -15,6 +15,7 @@ $animation_speed: .3s !default;
.grid-stack { .grid-stack {
position: relative; position: relative;
min-height: 80vh;
&.grid-stack-rtl { &.grid-stack-rtl {
direction: ltr; direction: ltr;
......
...@@ -45,7 +45,9 @@ ...@@ -45,7 +45,9 @@
<div class="col"> <div class="col">
<div id="bottom-pane" data-role="workspace"> <div id="bottom-pane" data-role="workspace">
<div id="column-container" class="user-page-content"> <div id="column-container" class="user-page-content">
{$columns|safe} <div class="grid-stack">
{$columns|safe}
</div>
</div> </div>
</div> </div>
</div> </div>
......
<div id="row_{$row}_column_{$column}" class="column column-layout columns{$numcolumns}{if $column == 1} firstcolumn{/if}{if $column == $numcolumns} lastcolumn{/if} {if $width}col-width-{$width}{/if}" {if $width}style="width:{if $width == 100}{$width }%;{else}{$width - 2}%;{/if}"{/if}> <div id="row_{$row}_column_{$column}" >
<div class="grid-stack-item-content ui-draggable-handle">
<div class="column-header-empty"></div> <div class="column-header-empty"></div>
<div class="column-content">
{$blockcontent|safe} {$blockcontent|safe}
</div> </div>
</div> </div>
<div id="block_{$id}"
class="grid-stack-item"
data-gs-x="{$positionx}"
data-gs-y="{$positiony}"
data-gs-width="{$width}"
data-gs-height="{$height}"
data-gs-id="{$id}"
>
<div class="grid-stack-item-content ui-draggable-handle">
<div class="column-header-empty"></div>
{$blockcontent|safe}
</div>
</div>
<div id="row_{$row}" class="js-col-row row-content col-row editing clearfix"{if $width} style="width: {$width-2}%;"{/if}>
{$rowcontent|safe} {$rowcontent|safe}
</div>
...@@ -144,8 +144,13 @@ if ($viewtheme && !isset($allowedthemes[$viewtheme])) { ...@@ -144,8 +144,13 @@ if ($viewtheme && !isset($allowedthemes[$viewtheme])) {
} }
$javascript = array('views', 'tinymce', 'paginator', 'js/jquery/jquery-ui/js/jquery-ui.min.js', $javascript = array('views', 'tinymce', 'paginator', 'js/jquery/jquery-ui/js/jquery-ui.min.js',
'js/jquery/jquery-ui/js/jquery-ui.touch-punch.min.js', 'tablerenderer', 'artefact/file/js/filebrowser.js', 'js/jquery/jquery-ui/js/jquery-ui.touch-punch.min.js', 'tablerenderer',
'lib/pieforms/static/core/pieforms.js', 'js/switchbox.js'); 'artefact/file/js/filebrowser.js',
'lib/pieforms/static/core/pieforms.js', 'js/switchbox.js',
'js/lodash/lodash.js',
'js/gridstack/gridstack.js',
'js/gridstack/gridstack.jQueryUI.js',
);
$blocktype_js = $view->get_all_blocktype_javascript(); $blocktype_js = $view->get_all_blocktype_javascript();
$javascript = array_merge($javascript, $blocktype_js['jsfiles']); $javascript = array_merge($javascript, $blocktype_js['jsfiles']);
if (is_plugin_active('externalvideo', 'blocktype')) { if (is_plugin_active('externalvideo', 'blocktype')) {
...@@ -155,6 +160,21 @@ $inlinejs = "jQuery( function() {\n" . join("\n", $blocktype_js['initjs']) . "\n ...@@ -155,6 +160,21 @@ $inlinejs = "jQuery( function() {\n" . join("\n", $blocktype_js['initjs']) . "\n
require_once('pieforms/pieform/elements/select.php'); require_once('pieforms/pieform/elements/select.php');
$inlinejs .= pieform_element_select_get_inlinejs(); $inlinejs .= pieform_element_select_get_inlinejs();
$inlinejs .= "jQuery(window).on('pageupdated', {}, function() { dock.init(jQuery(document)); });"; $inlinejs .= "jQuery(window).on('pageupdated', {}, function() { dock.init(jQuery(document)); });";
$inlinejs .="
$(function () {
var options = {
verticalMargin: 10,
float: true, //to place a block in any part of the page and the position will remain fixed
resizable: false,
};
var grid = $('.grid-stack');
grid.gridstack(options);
grid = $('.grid-stack').data('gridstack');
grid.resizable('.grid-stack-item', true);
});
";
// The form for adding blocks via the keyboard // The form for adding blocks via the keyboard
$addform = pieform(array( $addform = pieform(array(
'name' => 'newblock', 'name' => 'newblock',
......
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