Commit 02fb5d96 authored by Penny Leach's avatar Penny Leach Committed by Nigel McNie
Browse files

Finally, the non javascript version of the view page actually works,

sends data back to database, reads data from database.

Getting the reordering done with the unique constraint on view/column/order
was .. interesting ...

Next: ajax version
parent c2e41b52
......@@ -191,24 +191,7 @@ class BlockInstance {
$this->{$field} = $value;
return true;
}
throw new InvalidArgumentException("Field $field wasn't found in class " . get_class($this));
}
/**
* converts this instance to a stdclass object
* that can be used in data structures
*/
public function to_stdclass() {
$stdclass = new StdClass;
$vars = get_object_vars($this);
foreach (array_keys($vars) as $field) {
if ($field == 'dirty') {
continue;
}
$stdclass->{$field} = $this->get($field); // do it this way for calculated ones
}
$stdclass->content = $this->render();
return $stdclass;
throw new ParamOutOfRangeException("Field $field wasn't found in class " . get_class($this));
}
/**
......
......@@ -84,5 +84,7 @@ $string['artefactnotfoundmaybedeleted'] = "Artefact with id %s not found (maybe
$string['notartefactowner'] = 'You do not own this artefact';
$string['blockinstancednotfound'] = 'Block instance with id %s not found';
$string['invalidviewaction'] = 'Invalid view control action: %s';
$string['missingblocktype'] = 'Try selecting a block type to add first';
?>
......@@ -76,4 +76,18 @@ $string['viewcreatedsuccessfully'] = 'View created successfully';
$string['invalidcolumn'] = 'Column %s out of range';
// view control stuff
$string['success.addblocktype'] = 'Added block type successfully';
$string['err.addblocktype'] = 'Could not add the block to your view';
$string['success.moveblockinstance'] = 'Moved block successfully';
$string['err.moveblockinstance'] = 'Could not move the block to the specified position';
$string['success.removeblockinstance'] = 'Deleted block instance successfully';
$string['err.removeblockinstance'] = 'Could not delete block instance';
$string['success.addcolumn'] = 'Added column successfully';
$string['err.addcolumn'] = 'Failed to add new column';
$string['success.removecolumn'] = 'Deleted column successfully';
$string['err.removecolumn'] = 'Failed to delete column';
?>
......@@ -48,6 +48,7 @@ class View {
private $ownerobj;
private $numcolumns;
private $columns;
private $dirtycolumns; // for when we change stuff
public function __construct($id=0, $data=null) {
if (!empty($id)) {
......@@ -72,6 +73,8 @@ class View {
}
}
$this->atime = time();
$this->columns = array();
$this->dirtycolumns = array();
}
public function get($field) {
......@@ -282,8 +285,8 @@ class View {
* @private
* @return void
*/
private function build_column_datastructure() {
if (!empty($this->columns)) { // we've already built it up
private function build_column_datastructure($force=false) {
if (!empty($this->columns) && empty($force)) { // we've already built it up
return;
}
......@@ -301,8 +304,9 @@ class View {
}
foreach ($data as $block) {
require_once(get_config('docroot') . 'blocktype/lib.php');
$b = new BlockInstance($block->id, (array)$block);
$this->columns[$block->column]['blockinstances'][] = (array)$b->to_stdclass();
$this->columns[$block->column]['blockinstances'][] = $b;
}
}
......@@ -315,19 +319,232 @@ class View {
*/
public function get_column_datastructure($column=0) {
// make sure we've already built up the structure
$this->build_column_datastructure();
$force = false;
if (array_key_exists($column, $this->dirtycolumns)) {
$force = true;
}
$this->build_column_datastructure($force);
if (empty($column)) {
return $this->columns;
}
if (!array_key_exists($column, $this->columns)) {
throw new InvalidArgumentException(get_string('invalidcolumn', 'view', $column));
throw new ParameterOutOfRangeException(get_string('invalidcolumn', 'view', $column));
}
return $this->columns[$column];
}
// ******** functions to do with the view creation ui ************** //
public function get_viewcontrol_ok_string($functionname) {
return get_string('success.' . $functionname, 'view');
}
public function get_viewcontrol_err_string($functionname) {
return get_string('err.' . $functionname, 'view');
}
public function addblocktype($values) {
if (!array_key_exists('blocktype', $values) || empty($values['blocktype'])) {
throw new ParameterException(get_string('missingblocktype', 'error'));
}
safe_require('blocktype', $values['blocktype']);
$bi = new BlockInstance(0,
array(
'blocktype' => $values['blocktype'],
'title' => call_static_method(generate_class_name('blocktype', $values['blocktype']), 'get_title'),
'view' => $this->get('id'),
'column' => $values['column'],
'order' => $values['order'],
)
);
$this->shuffle_column($values['column'], $values['order']);
$bi->commit();
$this->dirtycolumns[$values['column']] = 1;
}
public function removeblockinstance($values) {
require_once(get_config('docroot') . 'blocktype/lib.php');
$bi = new BlockInstance($values['id']); // get it so we can reshuffle stuff
db_begin();
delete_records('block_instance', 'id', $bi->get('id'));
$this->shuffle_column($bi->get('column'), null, $bi->get('order'));
db_commit();
$this->dirtycolumns[$bi->get('column')] = 1;
}
public function moveblockinstance($values) {
require_once(get_config('docroot') . 'blocktype/lib.php');
$bi = new BlockInstance($values['id']);
db_begin();
// moving within the same column
if ($bi->get('column') == $values['column']) {
if ($values['order'] == $bi->get('order') + 1
|| $values['order'] == $bi->get('order') -1) {
// we're switching two, it's a bit different
// set the one we're moving to out of range (to 0)
set_field('block_instance', 'order', 0, 'view', $this->get('id'), 'column', $values['column'], 'order', $values['order']);
// set the new order
set_field('block_instance', 'order', $values['order'], 'view', $this->get('id'), 'column', $values['column'], 'order', $bi->get('order'));
// move the old one back to where the moving one was.
set_field('block_instance', 'order', $bi->get('order'), 'view', $this->get('id'), 'column', $values['column'], 'order', 0);
// and set it in the object for good measure.
$bi->set('order', $values['order']);
}
else {
$this->shuffle_column($bi->get('column'), $values['order'], $bi->get('order'));
}
}
// moving to another column
else {
// first figure out if we've asked to add it somewhere sensible
// eg if we're moving a low down block into an empty column
$newmax = $this->get_current_max_order($values['column']);
if ($values['order'] > $newmax+1) {
$values['order'] = $newmax+1;
}
// remove it from the old column
$this->shuffle_column($bi->get('column'), null, $bi->get('order'));
// and make a hole in the new column
$this->shuffle_column($values['column'], $values['order']);
}
$bi->set('column', $values['column']);
$bi->set('order', $values['order']);
$bi->commit();
$this->dirtycolumns[$bi->get('column')] = 1;
$this->dirtycolumns[$values['column']] = 1;
db_commit();
}
public function addcolumn($values) {
db_begin();
$this->set('numcolumns', $this->get('numcolumns') + 1);
if ($values['before'] != ($this->get('numcolumns') + 1)) {
$this->shuffle_helper('column', 'up', '>=', $values['before']);
}
$this->commit();
// @TODO this could be optimised by actually moving the keys around,
// but I don't think there's much point as the objects aren't persistent
for ($i = $values['before']; $i <= $this->get('numcolumns'); $i++) {
$this->dirtycolumns[$i] = 1;
}
$this->columns[$this->get('numcolumns')] = null; // set the key
db_commit();
}
public function removecolumn($values) {
db_begin();
$this->set('numcolumns', $this->get('numcolumns') - 1);
$columnmax = array(); // keep track of where we're at in each column
$currentcol = 1;
if ($blocks = $this->get_column_datastructure($values['column'])) {
// we have to rearrange them first
foreach ($blocks['blockinstances'] as $block) {
if ($currentcol > $this->get('numcolumns')) {
$currentcol = 1;
}
if ($currentcol == $values['column']) {
$currentcol++; // don't redistrubute blocks here!
}
if (!array_key_exists($currentcol, $columnmax)) {
$columnmax[$currentcol] = $this->get_current_max_order($currentcol);
}
$this->shuffle_column($currentcol, $columnmax[$currentcol]+1);
$block->set('column', $currentcol);
$block->set('order', $columnmax[$currentcol]+1);
$block->commit();
$columnmax[$currentcol]++;
$currentcol++;
}
}
// now shift all blocks one left and we're done
$this->shuffle_helper('column', 'down', '>', $values['column']);
$this->commit();
db_commit();
unset($this->columns); // everything has changed
}
/**
* helper function for re-ordering block instances within a column
* @param int $column the column to re-order
* @param int $insert the order we need to insert
* @param int $remove the order we need to move out of the way
*/
private function shuffle_column($column, $insert=0, $remove=0) {
/*
inserting something in the middle from somewhere else (insert and remove)
we're either reshuffling after a delete, (no insert),
inserting something in the middle out of nowhere (no remove)
*/
// inserting and removing
if (!empty($remove)) {
// move it out of range (set to 0)
set_field('block_instance', 'order', 0, 'order', $remove, 'column', $column, 'view', $this->get('id'));
if (!empty($insert)) {
// shuffle everything up
$this->shuffle_helper('order', 'up', '>=', $insert, '"column" = ?', array($column));
}
// shuffle everything down
$this->shuffle_helper('order', 'down', '>', $remove, '"column" = ?', array($column));
if (!empty($insert)) {
// now move it back
set_field('block_instance', 'order', $insert, 'view', $this->get('id'), 'column', $column, 'order', 0);
}
}
else if (!empty($insert)) {
// shuffle everything up
$this->shuffle_helper('order', 'up', '>=', $insert, '"column" = ?', array($column));
}
}
private function shuffle_helper($field, $direction, $operator, $value, $extrawhere='', $extravalues='') {
// doing this with execute_sql rather than set_field and friends because of
// adodb retardedly trying to make "order"+1 and friends into a string
// I couldn't find a way to shift a bunch of rows in step even with set constraints deferred.
// the two options I found were to move them all out of range (eg start at max +1) and then back again
// or move them into negative and back into positive (Grant's suggestion) which I like more.
if (empty($extrawhere)) {
$extrawhere = '';
}
else {
$extrawhere = ' AND ' . $extrawhere;
}
if (empty($extravalues) || !is_array($extravalues) || count($extravalues) == 0) {
$extravalues = array();
}
// first move them one but switch to negtaive
$sql = 'UPDATE {block_instance}
SET "' . $field .'" = (-1 * ("' . $field . '") ' . (($direction == 'up') ? '-' : '+') . ' 1)
WHERE "view" = ? AND "' . $field . '"' . $operator . ' ? ' . $extrawhere;
execute_sql($sql, array_merge(array($this->get('id'), $value), $extravalues));
// and now flip to positive again
$sql = 'UPDATE {block_instance}
SET "' . $field . '" = ("' . $field . '" * -1)
WHERE "view" = ? AND "' . $field . '" < 0 ' . $extrawhere;
execute_sql($sql, array_merge(array($this->get('id')), $extravalues));
}
private function get_current_max_order($column) {
return get_field('block_instance', 'max("order")', 'column', $column, 'view', $this->get('id'));
}
}
?>
......@@ -72,6 +72,8 @@ function view_build_category_list($defaultcategory, $javascript=false) {
if ($classes) {
$result .= ' class="' . hsc(implode(' ', $classes)) . '"';
}
// @TODO fix this url (apart from anything else it needs the view id!)
// and probably should be in a smarty template
$result .= '<a href="viewrework.php?category=' . hsc($cat['name']) . '">' . hsc($cat['title']) . "</a></li>\n";
}
$result .= "</ul>\n";
......@@ -164,22 +166,22 @@ function view_build_column(View $view, $column, $javascript=false) {
if ($column == 1) {
$result .= ' <div class="add-column-left">
<input type="submit" class="submit addcolumn" name="action_add_column_before_1" value="Add Column">
<input type="submit" class="submit addcolumn" name="action_addcolumn_before_1" value="Add Column">
</div>';
}
$result .= ' <div class="remove-column">
<input type="submit" class="submit removecolumn" name="action_remove_column_' . $column . '" value="Remove Column">
<input type="submit" class="submit removecolumn" name="action_removecolumn_column_' . $column . '" value="Remove Column">
</div>';
if ($column == $view->get('numcolumns')) {
$result .= ' <div class="add-column-right">
<input type="submit" class="submit addcolumn" name="action_add_column_before_' . ($column + 1) . '" value="Add Column">
<input type="submit" class="submit addcolumn" name="action_addcolumn_before_' . ($column + 1) . '" value="Add Column">
</div>';
}
else {
$result .= ' <div class="add-column-center">
<input type="submit" class="submit addcolumn" name="action_add_column_before_' . ($column + 1) . '" value="Add Column">
<input type="submit" class="submit addcolumn" name="action_addcolumn_before_' . ($column + 1) . '" value="Add Column">
</div>';
}
......@@ -188,44 +190,45 @@ function view_build_column(View $view, $column, $javascript=false) {
<div class="column-content">';
if (!$javascript) {
$result .= ' <div class="add-button">
<input type="submit" class="submit newblockhere" name="action_blocktype_add_top_' . $column . '" value="Add new block here">
<input type="submit" class="submit newblockhere" name="action_addblocktype_column_' . $column . '_order_1" value="Add new block here">
</div>';
}
// Blocktype loop here
foreach($data['blockinstances'] as $blockinstance) {
$result .= ' <div class="blockinstance" id="blockinstance_' . $blockinstance['id'] . '">
$result .= ' <div class="blockinstance" id="blockinstance_' . $blockinstance->get('id') . '">
<div class="blockinstance-header">
<h4>' . hsc($blockinstance['title']) . '</h4>
<h4>' . hsc($blockinstance->get('title')) . '</h4>
</div>
<div class="blockinstance-controls">';
if (!$javascript) {
// FIXME loop pls!
if ($blockinstance['canmoveleft']) {
$result .= '<input type="submit" class="submit movebutton" name="blockinstance_' . $blockinstance['id'] . '_moveleft" value="&larr;">';
$movestart = '<input type="submit" class="submit movebutton" name="action_moveblockinstance_id_' . $blockinstance->get('id');
if ($blockinstance->get('canmoveleft')) {
$result .= $movestart . '_column_' . ($column - 1) . '_order_' . $blockinstance->get('order') . '" value="&larr;">';
}
if ($blockinstance['canmovedown']) {
$result .= '<input type="submit" class="submit movebutton" name="blockinstance_' . $blockinstance['id'] . '_movedown" value="&darr;">';
if ($blockinstance->get('canmovedown')) {
$result .= $movestart . '_column_' . $column . '_order_' . ($blockinstance->get('order') + 1) . '" value="&darr;">';
}
if ($blockinstance['canmoveup']) {
$result .= '<input type="submit" class="submit movebutton" name="blockinstance_' . $blockinstance['id'] . '_moveup" value="&uarr;">';
if ($blockinstance->get('canmoveup')) {
$result .= $movestart . '_column_' . $column . '_order_' . ($blockinstance->get('order') - 1) . '" value="&uarr;">';
}
if ($blockinstance['canmoveright']) {
$result .= '<input type="submit" class="submit movebutton" name="blockinstance_' . $blockinstance['id'] . '_moveright" value="&rarr;">';
if ($blockinstance->get('canmoveright')) {
$result .= $movestart . '_column_' . ($column + 1) . '_order_' . $blockinstance->get('order') . '" value="&rarr;">';
}
}
$result .= '<input type="submit" class="submit deletebutton" name="blockinstance_' . $blockinstance['id'] .'_delete" value="X">';
$result .= '<input type="submit" class="submit deletebutton" name="action_removeblockinstance_id_' . $blockinstance->get('id') .'" value="X">';
$result .= ' </div>
<div class="blockinstance-content">
' . $blockinstance['content'] . '
' . $blockinstance->render() . '
</div>
</div>';
if (!$javascript) {
$result .= '
<div class="add-button">
<input type="submit" class="submit newblockhere" name="action_blocktype_add_after_' . $blockinstance['id'] . '" value="Add new block here">
<input type="submit" class="submit newblockhere" name="action_addblocktype_column_' . $column . '_order_' . ($blockinstance->get('order') + 1) . '" value="Add new block here">
</div>';
}
}
......@@ -242,8 +245,8 @@ function view_process_changes() {
if (!count($_POST)) {
return;
}
log_debug($_POST);
$view = param_integer('view');
$view = new View($view);
$action = '';
foreach ($_POST as $key => $value) {
......@@ -255,106 +258,66 @@ function view_process_changes() {
}
}
$value = view_get_value_for_action($action);
$actionstring = $action;
$action = substr($action, 0, strpos($action, '_'));
$actionstring = substr($actionstring, strlen($action) + 1);
$values = view_get_values_for_action($actionstring);
$result = null;
if (starts_with($action, 'blocktype_add_top')) {
// Done as "add_top" so that block instances can be added to columns with nothing in them
$blocktype = param_integer('blocktype', 0);
if (!$blocktype) {
$SESSION->add_info_msg('Please select a block type to add first');
return;
}
$result = view_blocktype_add_top($view, $blocktype, $value);
$okmsg = 'Added block type successfully';
$errmsg = 'Could not add the block to your view';
switch ($action) {
// the view class method is the same as the action,
// but I've left these here in case any additional
// parameter handling has to be done.
case 'addblocktype':
$values['blocktype'] = param_alpha('blocktype', null);
break;
case 'moveblockinstance':
case 'removeblockinstance':
//case 'configureblockinstance': // later
case 'addcolumn':
case 'removecolumn':
break;
default:
throw new InvalidArgumentException(get_string('noviewcontrolaction', 'error', $action));
}
else if (starts_with($action, 'blocktype_add_after')) {
$blockinstance = view_get_value_for_action($action);
$blocktype = param_integer('blocktype', 0);
if (!$blocktype) {
$SESSION->add_info_msg('Please select a block type to add first');
return;
}
$result = view_blocktype_add_after($view, $blocktype, $value);
$okmsg = 'Added block type successfully';
$errmsg = 'Could not add the block to your view';
try {
$view->$action($values);
$SESSION->add_ok_msg($view->get_viewcontrol_ok_string($action));
}
else if (starts_with($action, 'add_column_before')) {
$result = false;
$okmsg = '';
$errmsg = 'Not implemented yet';
catch (Exception $e) {
$SESSION->add_error_msg($view->get_viewcontrol_err_string($action) . ': ' . $e->getMessage());
}
else if (starts_with($action, 'remove_column')) {
$column = view_get_value_for_action($action);
// TODO fix this url
redirect('/viewrework.php?view=' . $view->get('id') . '&category=file');
}
log_debug("Remove column " . $column);
if (view_remove_column($view, $column)) {
$SESSION->add_ok_msg('Removed column successfully');
}
else {
$SESSION->add_ok_msg('Failed to remove column');
}
return;
}
if (!is_null($result)) {
if ($result) {
$SESSION->add_ok_msg($okmsg);
/**
* parses the string and returns a hash of values
* @param string $action expects format name_value_name_value
* where values are all numeric
* @return array associative
*/
function view_get_values_for_action($action) {
$values = array();
$bits = explode('_', $action);
if ((count($bits) % 2) == 1) {
throw new ParamOutOfRangeException(get_string('invalidviewaction', 'error', $action));
}
$lastkey = null;
foreach ($bits as $index => $bit) {
if ($index % 2 == 0) {
$lastkey = $bit;
}
else {
$SESSION->add_error_msg($errmsg);
$values[$lastkey] = $bit;
}
redirect('/viewrework.php');
}
throw new UserException('No valid action found');
}
function starts_with($haystack, $needle) {
return substr($haystack, 0, strlen($needle)) == $needle;
}
function view_get_value_for_action($action) {
$value = intval(substr($action, strrpos($action, '_') + 1));
if ($value == 0) {
throw new UserException('Value for action is not valid');
}
return $value;
}
function view_assert_data($data, $key) {
if (!isset($data[$key])) {
throw new UserException('The value for "' . $key . '" is not available for this action');
}
}
function view_blocktype_add_top($view, $blocktype, $column) {
// Stub
log_debug("Add block type " . $blocktype . ' to the top of column ' . $column);
return true;
return $values;
}
function view_blocktype_add_after($view, $blocktype, $blockinstance) {
// Stub
log_debug("Add block type " . $blocktype . ' below blockinstance ' . $blockinstance);
return true;
}
function view_add_column($view, $column) {
// Stub
log_debug('Adding column before current column ' . $column);
return true;
}
function view_remove_column($view, $column) {
// Stub
log_debug('Removing column ' . $column . ' from view ' . $view);
return true;
}
?>
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