<?php
/**
 * $Horde: horde/portal_edit.php,v 1.13 2003/04/20 20:50:36 chuck Exp $
 *
 * Copyright 1999-2003 Chuck Hagenbuch <chuck@horde.org>
 * Copyright 2003 Mike Cochrane <mike@graftonhall.co.nz>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 */

/**
 * Does a row exist?
 *
 * @param integer $row  The row to look for
 *
 * @return boolean  True if the row exists
 *                  False if the row does not exist
 */
function rowExists($row)
{
    global $layout_pref;
    return isset($layout_pref[$row]);
}

/**
 * Does a column exist?
 *
 * @param integer $col  The column to look for
 *
 * @return boolean  True if the column exists
 *                  False if the column does not exist
 */
function colExists($col)
{
    global $layout_pref;
    return isset($layout_pref[0][$col]);
}

/**
 * Get the width of the block at a given location.
 * This returns the width if there is a block at this
 * location, otherwise returns 1.
 *
 * @param integer $row  The row of the block
 * @param integer $col  The column of the block
 *
 * @return integer  The number of columns this block spans
 */
function getWidth($row, $col)
{
    global $layout_pref;

    if (!is_array($layout_pref[$row][$col])) {
        return 1;
    }
    if (!isset($layout_pref[$row][$col]['width'])) {
        $layout_pref[$row][$col]['width'] = 1;
    }
    return $layout_pref[$row][$col]['width'];
}

/**
 * Get the height of the block at a given location.
 * This returns the height if there is a block at this
 * location, otherwise returns 1.
 *
 * @param integer $row  The row of the block
 * @param integer $col  The column of the block
 *
 * @return integer  The number of rows this block spans
 */
function getHeight($row, $col)
{
    global $layout_pref;

    if (!is_array($layout_pref[$row][$col])) {
        return 1;
    }
    if (!isset($layout_pref[$row][$col]['height'])) {
        $layout_pref[$row][$col]['height'] = 1;
    }
    return $layout_pref[$row][$col]['height'];
}

/**
 * Checks to see if a given location if being used by a block
 *
 * @param integer $row  The row of the location to check
 * @param integer $col  The column of the location to check
 *
 * @return boolean  True if the location is empty
 *                  False is the location is being used.
 */
function emptyBlock($row, $col)
{
    global $layout_pref;
    return $layout_pref[$row][$col] == 'empty';
}

/**
 * Add a new row to the layout.
 *
 * @param integer $row  The number of the row to add
 */
function addRow($row)
{
    global $layout_pref;

    $layout_pref[$row] = array();
    reset($layout_pref);
    foreach(current($layout_pref) as $id => $val) {
        $layout_pref[$row][$id] = 'empty';
    }
}

/**
 * Add a new column to the layout.
 *
 * @param integer $col  The number of the column to add
 */
function addCol($col)
{
    global $layout_pref;

    foreach($layout_pref as $id => $val) {
        $layout_pref[$id][$col] = 'empty';
    }
}

/**
 * Remove a row if it's empty.
 *
 * @param integer $row  The number of the row to to check
 *
 * @return boolean  True if the row is now removed.
 *                  False if the row still exists.
 */
function removeRowIfEmpty($row)
{
    global $layout_pref;

    if (!rowExists($row)) {
        return true;
    }

    $used = false;
    for ($i = 0; $i < count($layout_pref[$row]); $i++) {
        if (isset($layout_pref[$row][$i]) && $layout_pref[$row][$i] != 'empty') {
            $used = true;
            break;
        }
    }
    if (!$used) {
        unset($layout_pref[$row]);
        return true;
    }
    return false;
}

/**
 * Remove a column if it's empty.
 *
 * @param integer $col  The number of the column to to check
 *
 * @return boolean  True if the column is now removed.
 *                  False if the column still exists.
 */
function removeColIfEmpty($col)
{
    global $layout_pref;

    if (!colExists($col)) {
        return true;
    }

    $used = false;
    for ($i = 0; $i < count($layout_pref); $i++) {
        if (isset($layout_pref[$i][$col]) && $layout_pref[$i][$col] != 'empty') {
            $used = true;
            break;
        }
    }
    if (!$used) {
        for ($i = 0; $i < count($layout_pref); $i++) {
            unset($layout_pref[$i][$col]);
            return true;
        }
    }
    return false;
}

@define('HORDE_BASE', dirname(__FILE__));
require_once HORDE_BASE . '/lib/base.php';
require_once HORDE_BASE . '/lib/Block.php';
require_once HORDE_BASE . '/lib/Identity.php';
require_once HORDE_BASE . '/lib/Menu.php';
require_once HORDE_BASE . '/lib/Help.php';

if (!Auth::isAuthenticated()) {
    Horde::authenticationFailureRedirect();
}

// Get form values
$col = Horde::getFormData('col');
$row = Horde::getFormData('row');
$action = Horde::getFormData('action');
$edit = array();

// Get full name for title
$identity = &new Identity();
$fullname = $identity->getValue('fullname');
if (empty($fullname)) {
    $fullname = Auth::getAuth();
}

// Get available blocks from all apps
$blocks = array();
foreach ($registry->listApps() as $app) {
    if ($registry->hasMethod('blocks', $app)) {
        $blocks[$app] = $registry->callByPackage($app, 'blocks');
    }
}

// Load layout from preferences
$layout = array();
$layout_pref = unserialize($prefs->getValue('portal_layout'));

// Handle requested actions
switch ($action) {
    // Move a block up
    case 'up':
        if (rowExists($row - 1)) {
            // See if there's room to move into
            $empty = true;
            for ($i = 0; $i < getWidth($row, $col); $i++) {
                if (!emptyBlock($row - 1, $col + $i)) {
                    $empty = false;
                    break;
                }
            }

            if ($empty) {
                $lastrow = $row + getHeight($row, $col) - 1;
                $w = getWidth($row, $col);
                for ($i = 0; $i < $w; $i++) {
                    $prev = $layout_pref[$row][$col + $i];
                    // Move top edge
                    $layout_pref[$row - 1][$col + $i] = $prev;
                    $layout_pref[$row][$col + $i] = 'covered';
                    // Move bottom edge
                    $layout_pref[$lastrow][$col + $i] = 'empty';
                }
                if (!rowExists($lastrow + 1)) {
                    // Was on the bottom row
                    removeRowIfEmpty($lastrow);
                }
            } else {
                // No where to go
                $notification->push(_("Shrink or move neighbouring block(s) out of the way first"), 'horde.warning');
            }
        }
        break;

    // Move a block down
    case 'down':
        $lastrow = $row + getHeight($row, $col) - 1;
        if (rowExists($lastrow + 1)) {
            // See if there's room to move into
            $empty = true;
            for ($i = 0; $i < getWidth($row, $col); $i++) {
                if (!emptyBlock($lastrow + 1, $col + $i)) {
                    $empty = false;
                    break;
                }
            }
        } else {
            // Make room to move into
            addRow($lastrow + 1);
            $empty = true;
        }

        if ($empty) {
            $w = getWidth($row, $col);
            for ($i = 0; $i < $w; $i++) {
                $prev = $layout_pref[$row][$col + $i];
                // Move bottom edge
                $layout_pref[$lastrow + 1][$col + $i] = 'covered';
                // Move top edge
                $layout_pref[$row + 1][$col + $i] = $prev;
                $layout_pref[$row][$col + $i] = 'empty';
            }
        } else {
            // No where to go
            $notification->push(_("Shrink or move neighbouring block(s) out of the way first"), 'horde.warning');
        }
        break;

    // Move a block left.
    case 'left':
        if (colExists($col - 1)) {
            // See if there's room to move into.
            $empty = true;
            for ($i = 0; $i < getHeight($row, $col); $i++) {
                if (!emptyBlock($row + $i, $col - 1)) {
                    $empty = false;
                    break;
                }
            }

            if ($empty) {
                $lastcol = $col + getWidth($row, $col) - 1;
                $h = getHeight($row, $col);
                for ($i = 0; $i < $h; $i++) {
                    $prev = $layout_pref[$row + $i][$col];
                    // Move left hand edge
                    $layout_pref[$row + $i][$col - 1] = $prev;
                    $layout_pref[$row + $i][$col] = 'covered';
                    // Move right hand edge
                    $layout_pref[$row + $i][$lastcol] = 'empty';
                }
                if (!colExists($lastcol + 1)) {
                    // Was on the right-most column
                    removeColIfEmpty($lastcol);
                }
            } else {
                // No where to go
                $notification->push(_("Shrink or move neighbouring block(s) out of the way first"), 'horde.warning');
            }
        }
        break;

    // Move a block right.
    case 'right':
        $lastcol = $col + getWidth($row, $col) - 1;
        if (colExists($lastcol + 1)) {
            // See if there's room to move into.
            $empty = true;
            for ($i = 0; $i < getHeight($row, $col); $i++) {
                if (!emptyBlock($row + $i, $lastcol + 1)) {
                    $empty = false;
                    break;
                }
            }
        } else {
            // Make room to move into.
            $empty = true;
            addCol($lastcol + 1);
        }

        if ($empty) {
            $h = getHeight($row, $col);
            for ($i = 0; $i < $h; $i++) {
                $prev = $layout_pref[$row + $i][$col];
                // Move right hand edge
                $layout_pref[$row + $i][$lastcol + 1] = 'covered';
                // Move left hand edge
                $layout_pref[$row + $i][$col + 1] = $prev;
                $layout_pref[$row + $i][$col] = 'empty';
            }
        } else {
            // No where to go
            $notification->push(_("Shrink or move neighbouring block(s) out of the way first"), 'horde.warning');
        }
        break;

    // Make a block taller by moving the top up
    case 'expand-top':
        if (rowExists($row - 1)) {
            // See if there's room to expand into
            $empty = true;
            for ($i = 0; $i < getWidth($row, $col); $i++) {
                if (!emptyBlock($row - 1, $col + $i)) {
                    $empty = false;
                    break;
                }
            }

            if ($empty) {
                $w = getWidth($row, $col);
                for ($i = 0; $i < $w; $i++) {
                    $layout_pref[$row - 1][$col + $i] = $layout_pref[$row][$col + $i];
                    $layout_pref[$row][$col + $i] = 'covered';
                }
                $layout_pref[$row - 1][$col]['height'] = getHeight($row - 1, $col) + 1;
            } else {
                // No where to go
                $notification->push(_("Shrink or move neighbouring block(s) out of the way first"), 'horde.warning');
            }
        }
        break;

    // Make a block taller by moving the bottom down
    case 'expand-bottom':
        $lastrow = $row + getHeight($row, $col) - 1;
        if (!rowExists($lastrow + 1)) {
            // Add a new row.
            addRow($lastrow + 1);
            for ($i = 0; $i < getWidth($row, $col); $i++) {
                $layout_pref[$lastrow + 1][$col + $i] = 'covered';
            }
            $layout_pref[$row][$col]['height'] = getHeight($row, $col) + 1;
        } else {
            // See if there's room to expand into
            $empty = true;
            for ($i = 0; $i < getWidth($row, $col); $i++) {
                if (!emptyBlock($lastrow + 1, $col + $i)) {
                    $empty = false;
                    break;
                }
            }

            if ($empty) {
                for ($i = 0; $i < getWidth($row, $col); $i++) {
                    $layout_pref[$lastrow + 1][$col + $i] = 'covered';
                }
                $layout_pref[$row][$col]['height'] = getHeight($row, $col) + 1;
            } else {
                // No where to go
                $notification->push(_("Shrink or move neighbouring block(s) out of the way first"), 'horde.warning');
            }
        }
        break;

    // Make a block wider by moving the left side out
    case 'expand-left':
        if (colExists($col - 1)) {
            // See if there's room to expand into
            $empty = true;
            for ($i = 0; $i < getHeight($row, $col); $i++) {
                if (!emptyBlock($row + $i, $col - 1)) {
                    $empty = false;
                    break;
                }
            }

            if ($empty) {
                $h = getHeight($row, $col);
                for ($i = 0; $i < $h; $i++) {
                    $layout_pref[$row + $i][$col - 1] = $layout_pref[$row + $i][$col];
                    $layout_pref[$row + $i][$col] = 'covered';
                }
                $layout_pref[$row][$col - 1]['width'] = getWidth($row, $col - 1) + 1;
            } else {
                // No where to go
                $notification->push(_("Shrink or move neighbouring block(s) out of the way first"), 'horde.warning');
            }
        }
        break;

    // Make a block wider by moving the right side out
    case 'expand-right':
        $lastcol = $col + getWidth($row, $col) - 1;
        if (colExists($lastcol + 1)) {
            // See if there's room to expand into
            $empty = true;
            for ($i = 0; $i < getHeight($row, $col); $i++) {
                if (!emptyBlock($row + $i, $lastcol + 1)) {
                    $empty = false;
                    break;
                }
            }

            if ($empty) {
                for ($i = 0; $i < getHeight($row, $col); $i++) {
                    $layout_pref[$row + $i][$lastcol + 1] = 'covered';
                }
                $layout_pref[$row][$col]['width'] = getWidth($row, $col) + 1;
            } else {
                // No where to go
                $notification->push(_("Shrink or move neighbouring block(s) out of the way first"), 'horde.warning');
            }
        } else {
            // Add new column
            addCol($lastcol + 1);
            $h = getHeight($row, $col);
            for ($i = 0; $i < $h; $i++) {
                $layout_pref[$row + $i][$lastcol + 1] = 'covered';
            }
            $layout_pref[$row][$col]['width'] = getWidth($row, $col) + 1;
        }
        break;

    // Make a block narrower by moving the left side in
    case 'shrink-left':
        if (getWidth($row, $col) > 1) {
            $h = getHeight($row, $col);
            for ($i = 0; $i < $h; $i++) {
                $layout_pref[$row + $i][$col + 1] = $layout_pref[$row + $i][$col];
                $layout_pref[$row + $i][$col] = 'empty';
            }
            $layout_pref[$row][$col + 1]['width'] = getWidth($row, $col + 1) - 1;
        }
        break;

    // Make a block narrower by moving the right side in
    case 'shrink-right':
        if (getWidth($row, $col) > 1) {
            $lastcol = $col + getWidth($row, $col) - 1;
            for ($i = 0; $i < getHeight($row, $col); $i++) {
                $layout_pref[$row + $i][$lastcol] = 'empty';
            }
            $layout_pref[$row][$col]['width'] = getWidth($row, $col) - 1;
            removeColIfEmpty($lastcol);
        }
        break;

    // Make a block shorter by moving the top down
    case 'shrink-top':
        if (getHeight($row, $col) > 1) {
            $w = getWidth($row, $col);
            for ($i = 0; $i < $w; $i++) {
                $layout_pref[$row + 1][$col + $i] = $layout_pref[$row][$col + $i];
                $layout_pref[$row][$col + $i] = 'empty';
            }
            $layout_pref[$row + 1][$col]['height'] = getHeight($row + 1, $col) - 1;
        }
        break;

    // Make a block shorter by moving the bottom up
    case 'shrink-bottom':
        if (getHeight($row, $col) > 1) {
            $lastrow = $row + getHeight($row, $col) - 1;
            for ($i = 0; $i < getWidth($row, $col); $i++) {
                $layout_pref[$lastrow][$col + $i] = 'empty';
            }
            $layout_pref[$row][$col]['height'] = getHeight($row, $col) - 1;
            if (!rowExists($lastrow + 1)) {
                // Was on the bottom row
                removeRowIfEmpty($lastrow);
            }
        }
        break;

    // Remove a block
    case 'remove':
        $h = getHeight($row, $col);
        $w = getWidth($row, $col);
        for ($i = $h - 1; $i >= 0; $i--) {
            for ($j = $w - 1; $j >= 0; $j--) {
                $layout_pref[$row + $i][$col + $j] = 'empty';
                if (!colExists($col + $j + 1)) {
                    removeColIfEmpty($col + $j);
                }
            }
            if (!rowExists($row + $i + 1) && rowExists($row + $i)) {
                removeRowIfEmpty($row + $i);
            }
        }
        if (!rowExists($row)) {
            $row--;
            while ($row >= 0 && removeRowIfEmpty($row)) {
                $row--;
            }
        }
        if (!colExists($col)) {
            $col--;
            while ($col >= 0 && removeColIfEmpty($col)) {
                $col--;
            }
        }
        break;

    // Save the changes made to a block
    case 'save':
    // Save the changes made to a block and continue editing
    case 'save-resume':
        // Make sure there is somewhere to put it
        if (!rowExists($row)) {
            addRow($row);
        }
        if (!colExists($col)) {
            addCol($col);
        }

        if (emptyBlock($row, $col)) {
            // New block, setup defaults.
            $layout_pref[$row][$col] = array();
            $layout_pref[$row][$col]['app'] = '';
            $layout_pref[$row][$col]['height'] = 1;
            $layout_pref[$row][$col]['width'] = 1;
        }

        // Get requested block type
        $t = explode(':', Horde::getFormData('app'));
        $newapp = $t[0];
        $newtype = $t[1];

        // Get target block
        $block = &$layout_pref[$row][$col];

        if ($block['app'] != $newapp || $block['params']['type'] != $newtype) {
            // Change app or type
            $block['app'] = $newapp;
            $block['params']['type'] = $newtype;
            $block['params']['params'] = array();
            if (isset($blocks[$newapp][$newtype]['params'])) {
                foreach ($blocks[$newapp][$newtype]['params'] as $key => $newparam) {
                    if (isset($newparam['default'])) {
                        $block['params']['params'][$key] = $newparam['default'];
                    } else {
                        $block['params']['params'][$key] = '';
                    }
                }
            }
        } else {
            // Change values
            $params = Horde::getFormData('params', array());
            foreach ($params as $key => $val) {
                $block['params']['params'][$key] = $val;
            }
        }
        if ($action == 'save') {
            break;
        }

    // Make a block the current block for editing.
    case 'edit':
        $edit[$row] = array();
        $edit[$row][$col] = true;
        break;
}

// Save changes.
$prefs->setValue('portal_layout', serialize($layout_pref));

// Create all the blocks for display.
foreach ($layout_pref as $row_num => $row) {
    foreach ($row as $col_num => $item) {
        if (is_array($item)) {
            $layout[$row_num][$col_num] = $registry->callByPackage($item['app'], 'block', $item['params']);
            if (isset($item['height'])) {
                $layout[$row_num][$col_num]->setHeight($item['height']);
            }
            if (isset($item['width'])) {
                $layout[$row_num][$col_num]->setWidth($item['width']);
            }
        } else {
            $layout[$row_num][$col_num] = null;
        }
    }
}

// Create block selection options.
$blockselectoptions = '';
foreach ($blocks as $app => $appblocks) {
    if (is_a($appblocks, 'PEAR_Error')) {
        continue;
    }
    foreach ($appblocks as $blockid => $appblock) {
        $blockselectoptions .= '<option value="' . $app . ':' . $blockid . '">' . $registry->getParam('name', $app) . ': ' . $appblock['name'] . '</option>';
    }
}

$title = _("My Summary Layout");
$show_home = true;
require HORDE_TEMPLATES . '/common-header.inc';
require HORDE_TEMPLATES . '/portal/menu.inc';
$notification->notify();
require HORDE_TEMPLATES . '/portal/portal_edit.inc';
require HORDE_TEMPLATES . '/common-footer.inc';
