<?php
/*******************************************************************************
 *
 * LEIDEN OPEN VARIATION DATABASE (LOVD)
 *
 * Created     : 2006-09-28
 * Modified    : 2009-11-17
 * For LOVD    : 2.0-23
 *
 * Access      : Administrator and managers.
 * Purpose     : Create and manage LOVD custom links.
 *
 * Copyright   : 2004-2009 Leiden University Medical Center; http://www.LUMC.nl/
 * Programmer  : Ing. Ivo F.A.C. Fokkema <I.F.A.C.Fokkema@LUMC.nl>
 * Last edited : Ing. Ivo F.A.C. Fokkema <I.F.A.C.Fokkema@LUMC.nl>
 *
 *
 * This file is part of LOVD.
 *
 * LOVD is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * LOVD is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LOVD; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *************/

define('ROOT_PATH', './');
require ROOT_PATH . 'inc-init.php';

if (HAS_AUTH) {
    // If authorized, check for updates.
    require ROOT_PATH . 'inc-upgrade.php';
}

// Require manager clearance.
lovd_requireAUTH(LEVEL_MANAGER);





if ($_GET['action'] == 'view_all') {
    // View all links.

    require ROOT_PATH . 'inc-lib-list.php';
    require ROOT_PATH . 'inc-top.php';
    lovd_printHeader('setup_links_manage', 'LOVD Setup - Manage custom links');

    list($nTotal) = mysql_fetch_row(mysql_query('SELECT COUNT(*) FROM ' . TABLE_LINKS));
    if (!$nTotal) {
        lovd_showInfoTable('There are no custom links configured in this system.', 'information');
        require ROOT_PATH . 'inc-bot.php';
        exit;
    }

    // Standard query, will be extended later on.
    $sQ = 'SELECT l.*, !(l.active) AS deleted, COUNT(c2l.colid) AS active_columns FROM ' . TABLE_LINKS . ' AS l LEFT OUTER JOIN ' . TABLE_COLS2LINKS . ' AS c2l USING (linkid)';

    // SORT: Current settings.
    // 2008-08-07; 2.0-10; Implement XSS check on order variable.
    if (isset($_GET['order']) && $_GET['order'] && $_GET['order'] == strip_tags($_GET['order'])) {
        $aOrder = explode(',', $_GET['order']);
    } else {
        $aOrder = array('', '');
    }

    // SORT: Column data.
    $aOrderList = array('linkid' => array('linkid', 'ASC'), 'linkname' => array('linkname', 'ASC'), 'pattern_text' => array('pattern_text', 'ASC'), 'replace_text' => array('replace_text', 'ASC'));
    if (!array_key_exists($aOrder[0], $aOrderList)) {
        $aOrder[0] = 'linkname';
    }
    if ($aOrder[1] != 'ASC' && $aOrder[1] != 'DESC') {
        $aOrder[1] = $aOrderList[$aOrder[0]][1];
    }

    $sQueryLimit = lovd_pagesplitInit($nTotal, 25);
    $sQ .= ' GROUP BY l.linkid';
    $sQ .= ' ORDER BY active ASC, ' . $aOrderList[$aOrder[0]][0] . ' ' . $aOrder[1] . ', linkname ASC ' . $sQueryLimit;



    // Show form; required for sorting and searching.
    print('      <FORM action="' . $_SERVER['PHP_SELF'] . '" method="get" style="margin : 0px;">' . "\n" .
          '        <INPUT type="hidden" name="action" value="' . $_GET['action'] . '">' . "\n" .
          '        <INPUT type="hidden" name="order" value="' . implode(',', $aOrder) . '">');
    print('</FORM>' . "\n\n");

    $q = mysql_query($sQ);
    if (!$q) {
        lovd_dbFout('Links' . ucfirst($_GET['action']), $sQ, mysql_error());
    }

    print('      <SPAN class="S11">' . $nTotal . ' entr' . ($nTotal == 1? 'y' : 'ies') . '</SPAN><BR>' . "\n");

    // Array which will make up the table (header and data).
    $aTable =
             array(
                    'linkname' => array('Name', '*'),
                    'pattern_text' => array('Pattern', '*'),
                    'replace_text' => array('Replacement', '*'),
                    'active_columns' => array('# Columns', '70', 'align="right"'),
                  );

    // Table.
    print('      <TABLE border="0" cellpadding="0" cellspacing="1" width="950" class="data">' . "\n" .
          '        <TR>');

    foreach ($aTable as $sField => $aCol) {
        print("\n" . '          <TH' . (!empty($aCol[2])? ' ' . $aCol[2] : '') . (is_numeric($aCol[1])? ' width="' . $aCol[1] . '"' : '') . (array_key_exists($sField, $aOrderList)? ' class="order' . ($aOrder[0] == $sField? 'ed' : '') . '" onclick="document.forms[0].order.value=\'' . $sField . ',' . ($aOrder[0] == $sField? ($aOrder[1] == 'ASC'? 'DESC' : 'ASC') : $aOrderList[$sField][1]) . '\';document.forms[0].submit();"' : '') . '>' .
              (array_key_exists($sField, $aOrderList)? "\n" .
                                                           '            <TABLE border="0" cellpadding="0" cellspacing="0" width="100%" class="S11">' . "\n" .
                                                           '              <TR>' . "\n" .
                                                           '                <TH>' . str_replace(' ', '&nbsp;', $aCol[0]) . '</TH>' . "\n" .
                                                           '                <TD align="right"><IMG src="gfx/order_arrow_desc' . ($aOrder[0] == $sField && $aOrder[1] == 'DESC'? '_sel' : '') . '.png" alt="Descending" title="Descending" width="13" height="6"><BR><IMG src="gfx/order_arrow_asc' . ($aOrder[0] == $sField && $aOrder[1] == 'ASC'? '_sel' : '') . '.png" alt="Ascending" title="Ascending" width="13" height="6"></TD></TR></TABLE>' : $aCol[0]) . '</TH>');
    }
    print('</TR>');

    while ($zData = mysql_fetch_assoc($q)) {
        print("\n" .
              '        <TR' . ($zData['deleted']? ' class="del"' : '') . ' style="cursor : pointer; cursor : hand;" onmouseover="this.className = \'' . ($zData['deleted']? 'del_' : '') . 'hover\';" onmouseout="this.className = \'' . ($zData['deleted']? 'del' : '') . '\';" onclick="window.location.href = \'' . $_SERVER['PHP_SELF'] . '?action=view&amp;view=' . $zData['linkid'] . lovd_showSID(true, true) . '\';">');

        $zData['linkname']     = '<A href="' . $_SERVER['PHP_SELF'] . '?action=view&amp;view=' . $zData['linkid'] . '" class="data">' . $zData['linkname'] . '</A>';
        $zData['replace_text'] = htmlspecialchars(lovd_shortenString($zData['replace_text'], 98));

        foreach ($aTable as $sField => $aCol) {
            print("\n" . '          <TD' . (!empty($aCol[2])? ' ' . $aCol[2] : '') . (is_numeric($aCol[1])? ' width="' . $aCol[1] . '"' : '') . ($aOrder[0] == $sField? ' class="ordered"' : '') . '>' . (!$zData[$sField]? '-' : $zData[$sField]) . '</TD>');
        }
        print('</TR>');
    }
    print('</TABLE>' . "\n\n");

    // URL pagelink.
    $sPageLink = 'order=' . rawurlencode(implode(',', $aOrder));

    lovd_pagesplitShowNav($sPageLink);

    require ROOT_PATH . 'inc-bot.php';
    exit;





} elseif ($_GET['action'] == 'view' && is_numeric($_GET['view'])) {
    // View specific custom link.

    require ROOT_PATH . 'inc-top.php';
    lovd_printHeader('setup_links_manage', 'LOVD Setup - Manage custom links');

    $zData = @mysql_fetch_assoc(mysql_query('SELECT l.*, COUNT(c2l.colid) AS active_columns, u_c.name AS created_by, u_e.name AS edited_by FROM ' . TABLE_LINKS . ' AS l LEFT OUTER JOIN ' . TABLE_COLS2LINKS . ' AS c2l USING (linkid) LEFT OUTER JOIN ' . TABLE_USERS . ' AS u_c ON (l.created_by = u_c.userid) LEFT OUTER JOIN ' . TABLE_USERS . ' AS u_e ON (l.edited_by = u_e.userid) WHERE l.linkid = "' . $_GET['view'] . '" GROUP BY l.linkid'));
    if (!$zData) {
        // Wrong ID, apparently.
        lovd_showInfoTable('No such ID!', 'stop');
        require ROOT_PATH . 'inc-bot.php';
        exit;
    }

    // Array which will make up the data table.
    $aTable =
             array(
                    'linkid' => 'Link ID',
                    'linkname' => 'Link name',
                    'pattern_text' => 'Pattern text',
                    'replace_text' => 'Replace text',
                    'link_description' => 'Description',
                    'active' => 'Active?',
                    'active_columns_' => 'Active columns',
                    'created_by' => 'Created by',
                    'created_date' => 'Date created',
                    'edited_by' => 'Last edited by',
                    'edited_date' => 'Date last edited',
                  );

    // Remove unnecessary columns.
    if ($zData['edited_by'] == NULL) {
        // Never been edited.
        unset($aTable['edited_by'], $aTable['edited_date']);
    }

    // Table.
    print('      <TABLE border="0" cellpadding="0" cellspacing="1" width="600" class="data">');

    $zData['replace_text'] = htmlspecialchars($zData['replace_text']);
    // MySQL 4.1 allows group_concat, which would come in handy now. Query is backwards compatible with 4.0.
    $zData['active_columns_'] = '';
    if ($zData['active_columns']) {
        $q = mysql_query('SELECT c.head_column FROM ' . TABLE_COLS2LINKS . ' AS c2l LEFT JOIN ' . TABLE_COLS . ' AS c USING (colid) WHERE c2l.linkid = "' . $zData['linkid'] . '" ORDER BY c.head_column');
        while (list($sCol) = mysql_fetch_row($q)) {
            if (!$sCol) {
                // 2008-02-15; 2.0-04; Attempt to fix problem not showing Gene/Reference.
                // FIXME; this is just a temporary fix to show Gene/Reference; an column introduced in 2.0-beta-03 to allow custom link functionality in a gene homepage column.
                $sCol = 'Gene reference';
            }
            $zData['active_columns_'] .= ($zData['active_columns_']? ', ' : '') . $sCol;
        }
    }
    $zData['link_description'] = str_replace(array("\r\n", "\r", "\n"), '<BR>', $zData['link_description']);
    $zData['active']           = '<IMG src="gfx/mark_' . $zData['active'] . '.png" alt="' . $zData['active'] . '" width="11" height="11">';

    foreach ($aTable as $sField => $sHeader) {
        print("\n" .
              '        <TR>' . "\n" .
              '          <TH valign="top">' . str_replace(' ', '&nbsp;', $sHeader) . '</TH>' . "\n" .
              '          <TD>' . (!$zData[$sField]? '-' : $zData[$sField]) . '</TD></TR>');
    }
    print('</TABLE>' . "\n\n");

    $sNavigation = '';
    // Authorized user (admin or manager) is logged in. Provide tools.
    $sNavigation = '<A href="' . $_SERVER['PHP_SELF'] . '?action=edit&amp;edit=' . $zData['linkid'] . '">Edit custom link</A>';
    $sNavigation .= ' | <A href="' . $_SERVER['PHP_SELF'] . '?action=drop&amp;drop=' . $zData['linkid'] . '">Delete custom link</A>';
 
    if ($sNavigation) {
        print('      <IMG src="gfx/trans.png" alt="" width="1" height="5"><BR>' . "\n");
        lovd_viewNavigation($sNavigation);
    }

    require ROOT_PATH . 'inc-bot.php';
    exit;





} elseif ($_GET['action'] == 'create') {
    // Create new link.

    // Require form functions.
    require ROOT_PATH . 'inc-lib-form.php';

    if (isset($_GET['sent'])) {
        lovd_errorClean();

        // Mandatory fields.
        $aCheck =
                 array(
                        'linkname' => 'Link name',
                        'pattern_text' => 'Pattern',
                        'replace_text' => 'Replacement text',
                        'link_description' => 'Link description',
                      );

        foreach ($aCheck as $key => $val) {
            if (empty($_POST[$key])) {
                lovd_errorAdd('Please fill in the \'' . $val . '\' field.');
            }
        }

        // Link name must be unique.
        if ($_POST['linkname']) {
            // Enforced in the table, but we want to handle this gracefully.
            list($n) = mysql_fetch_row(mysql_query('SELECT COUNT(*) FROM ' . TABLE_LINKS . ' WHERE linkname = "' . $_POST['linkname'] . '"'));
            if ($n) {
                lovd_errorAdd('There is already a custom link with this link name. Please choose another one.');
            }
        }

        // On the pattern text.
        if ($_POST['pattern_text']) {
            // Pattern text must be unique.
            // Enforced in the table, but we want to handle this gracefully.
            list($n) = mysql_fetch_row(mysql_query('SELECT COUNT(*) FROM ' . TABLE_LINKS . ' WHERE pattern_text = "' . $_POST['pattern_text'] . '"'));
            if ($n) {
                lovd_errorAdd('There is already a custom link with this pattern. Please choose another one.');

            } else {
                // Pattern text pattern.
                if (strlen($_POST['pattern_text']) > 20 || !preg_match('/^\{([A-Z0-9 :;,_-]|\[[0-9]{1,2}\])+\}$/i', $_POST['pattern_text'])) {
                    lovd_errorAdd('The link pattern is found to be incorrect. It must start with \'{\', end with \'}\' and can contain letters, numbers, spaces, some special characters (:;,_-) and references ([1] to [99]) and must be 3-20 characters long.');
                }

                // References shouldn't follow each other directly, because LOVD wouldn't know the separation character.
                if (preg_match('/(\[[0-9]{1,2}\]){2,}/', $_POST['pattern_text'])) {
                    lovd_errorAdd('The link pattern is found to be incorrect. Two or more references directly after each other must be separated by at least one character to keep the two apart.');
                }
            }
        }

        // Check references in the pattern and replacement texts.
        if (!lovd_error()) {
            // Isolate reference numbers.
            $aPattern = explode(']', $_POST['pattern_text']);
            $aPatternRefs = array();
            foreach ($aPattern as $val) {
                if (substr_count($val, '[')) {
                    $aPatternRefs[] = substr(strrchr($val, '['), 1);
                }
            }

            // Isolate reference numbers.
            $aReplace = explode(']', $_POST['replace_text']);
            $aReplaceRefs = array();
            foreach ($aReplace as $val) {
                if (substr_count($val, '[')) {
                    $aReplaceRefs[] = substr(strrchr($val, '['), 1);
                }
            }

            // Check for reference order and/or references missing from the replacement text.
            reset($aPatternRefs);
            for ($i = 1; list(,$nRef) = each($aPatternRefs); $i ++) {
                if ($nRef != $i) {
                    lovd_errorAdd('The link pattern is found to be incorrect. Expected reference [' . $i . '] ' . ($i == 1? 'first' : 'after [' . ($i - 1) . ']') . ', got [' . $nRef . '].');
                }
            }

            foreach ($aReplaceRefs as $nRef) {
                if (!in_array($nRef, $aPatternRefs)) {
                    lovd_errorAdd('The link replacement text is found to be incorrect. Could not find used reference [' . $nRef . '] in link pattern.');
                }
            }
        }

        if (!lovd_error()) {
            // Query text.
            $sQ = 'INSERT INTO ' . TABLE_LINKS . ' VALUES (NULL, ';

            // Standard fields to be used.
            $aQ = array('linkname', 'pattern_text', 'replace_text', 'link_description', 'active');

            foreach ($aQ as $key => $val) {
                $sQ .= ($key? ', ' : '') . '"' . $_POST[$val] . '"';
            }

            $sQ .= ', ' . $_AUTH['userid'] . ', NOW(), NULL, NULL)';

            $q = mysql_query($sQ);
            if (!$q) {
                $sError = mysql_error(); // Save the mysql_error before it disappears.
                require ROOT_PATH . 'inc-top.php';
                lovd_printHeader('setup_links_create', 'LOVD Setup - Create new custom link');
                lovd_dbFout('LinkCreate', $sQ, $sError);
            }

            $nID = str_pad(mysql_insert_id(), 3, '0', STR_PAD_LEFT);

            // Write to log...
            lovd_writeLog('MySQL:Event', 'LinkCreate', $_AUTH['username'] . ' (' . mysql_real_escape_string($_AUTH['name']) . ') successfully created link ' . $nID . ' (' . $_POST['linkname'] . ')');

            // Add column.
            if (isset($_POST['active_columns'])) {
                foreach ($_POST['active_columns'] AS $sCol) {
                    // User has requested addition...
                    $q = mysql_query('INSERT INTO ' . TABLE_COLS2LINKS . ' VALUES ("' . $sCol . '", "' . $nID . '")');
                    if (!$q) {
                        // Silent error.
                        lovd_writeLog('MySQL:Error', 'LinkCreate', $nID . ' (' . $_POST['linkname'] . ') could not be added to ' . $sCol);
                    } else {
                        lovd_writeLog('MySQL:Event', 'LinkCreate', $nID . ' (' . $_POST['linkname'] . ') successfully added to ' . $sCol);
                    }
                }
            }

            // Thank the user...
            header('Refresh: 3; url=' . PROTOCOL . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . '?action=view&view=' . $nID . lovd_showSID(true));

            require ROOT_PATH . 'inc-top.php';
            lovd_printHeader('setup_links_create', 'LOVD Setup - Create new custom link');
            print('      Successfully created link \'' . stripslashes($_POST['linkname']) . '\'!<BR><BR>' . "\n\n");

            require ROOT_PATH . 'inc-bot.php';
            exit;

        } else {
            // Errors, so the whole lot returns to the form.
            lovd_magicUnquoteAll();
        }

    } else {
        // Default values.
        $_POST['active'] = 1;
    }



    require ROOT_PATH . 'inc-top.php';
    lovd_printHeader('setup_links_create', 'LOVD Setup - Create new custom link');

    if (!isset($_GET['sent'])) {
        print('      To create a new custom link, please fill out the form below.<BR>' . "\n" .
              '      <BR>' . "\n\n");
    }

    lovd_errorPrint();

    // Table.
    print('      <FORM action="' . $_SERVER['PHP_SELF'] . '?action=' . $_GET['action'] . '&amp;sent=true" method="post">' . "\n" .
          '        <TABLE border="0" cellpadding="0" cellspacing="1" width="760">');

    // Get column list, to connect link to column.
    $aCols = array();
    $aCols['Gene/Reference'] = 'Gene/Reference (Gene homepage)';
    $qCols = mysql_query('SELECT colid, CONCAT(colid, " (", head_column, ")") AS head_column FROM ' . TABLE_COLS . ' ORDER BY colid');
    $nCols = mysql_num_rows($qCols);
    $nColSize = ($nCols < 10? $nCols : 10);
    while ($r = mysql_fetch_row($qCols)) {
        $aCols[$r[0]] = $r[1];
    }

    // Array which will make up the form table.
    $aForm = array(
                    array('POST', '', '', '50%', '50%'),
                    array('', 'print', '<B>Link details</B>'),
                    array('Link name', 'text', 'linkname', 30),
                    array('Pattern', 'text', 'pattern_text', 30),
                    array('', 'print', '<SPAN class="form_note">The pattern is bound to some rules:<UL style="margin : 0px; padding-left : 1.5em;"><LI>It must start with \'{\' and end with \'}\'.</LI><LI>It can contain letters, numbers, spaces, some special characters (:;,_-) and references ([1] to [99]).</LI><LI>It must be 3-20 characters long.</LI><LI>Two or more references directly after each other must be separated by at least one character to keep the two apart.</LI></UL></SPAN>'),
                    array('Replacement text', 'textarea', 'replace_text', 40, 3),
                    array('', 'print', '<SPAN class="form_note">Make sure you use all references from the pattern in the replacement text.</SPAN>'),
                    array('Link description', 'textarea', 'link_description', 40, 3),
                    array('', 'print', '<SPAN class="form_note">To aid other users in using your custom link, please provide some information on what the link is for and how to use the references.</SPAN>'),
                    'skip',
                    array('', 'print', '<B>Link settings</B>'),
                    array('Active', 'checkbox', 'active', 1),
                    array('Active for columns', 'select', 'active_columns', $nColSize, $aCols, false, true, false),
                    'skip',
                    array('', 'submit', 'Create custom link'),
                  );
    $_MODULES->processForm('SetupLinksCreate', $aForm);
    lovd_viewForm($aForm);

    print('</TABLE></FORM>' . "\n\n");

    require ROOT_PATH . 'inc-bot.php';
    exit;





} elseif ($_GET['action'] == 'edit' && is_numeric($_GET['edit']) && $_AUTH['level'] >= LEVEL_MANAGER) {
    // Edit specific link.

    $zData = @mysql_fetch_assoc(mysql_query('SELECT * FROM ' . TABLE_LINKS . ' WHERE linkid = "' . $_GET['edit'] . '"'));
    if (!$zData) {
        // Wrong ID, apparently.
        require ROOT_PATH . 'inc-top.php';
        lovd_printHeader('setup_links_manage', 'LOVD Setup - Manage custom links');
        lovd_showInfoTable('No such ID!', 'stop');
        require ROOT_PATH . 'inc-bot.php';
        exit;
    }

    // Require form functions.
    require ROOT_PATH . 'inc-lib-form.php';

    if (isset($_GET['sent'])) {
        lovd_errorClean();

        // Mandatory fields.
        $aCheck =
                 array(
                        'linkname' => 'Link name',
                        'pattern_text' => 'Pattern',
                        'replace_text' => 'Replacement text',
                        'link_description' => 'Link description',
                      );

        foreach ($aCheck as $key => $val) {
            if (empty($_POST[$key])) {
                lovd_errorAdd('Please fill in the \'' . $val . '\' field.');
            }
        }

        // Link name must be unique.
        if ($_POST['linkname']) {
            // Enforced in the table, but we want to handle this gracefully.
            list($n) = mysql_fetch_row(mysql_query('SELECT COUNT(*) FROM ' . TABLE_LINKS . ' WHERE linkname = "' . $_POST['linkname'] . '" AND linkid != "' . $zData['linkid'] . '"'));
            if ($n) {
                lovd_errorAdd('There is already a custom link with this link name. Please choose another one.');
            }
        }

        // On the pattern text.
        if ($_POST['pattern_text']) {
            // Pattern text must be unique.
            // Enforced in the table, but we want to handle this gracefully.
            list($n) = mysql_fetch_row(mysql_query('SELECT COUNT(*) FROM ' . TABLE_LINKS . ' WHERE pattern_text = "' . $_POST['pattern_text'] . '" AND linkid != "' . $zData['linkid'] . '"'));
            if ($n) {
                lovd_errorAdd('There is already a custom link with this pattern. Please choose another one.');

            } else {
                // Pattern text pattern.
                if (strlen($_POST['pattern_text']) > 20 || !preg_match('/^\{([A-Z0-9 :;,_-]|\[[0-9]{1,2}\])+\}$/i', $_POST['pattern_text'])) {
                    lovd_errorAdd('The link pattern is found to be incorrect. It must start with \'{\', end with \'}\' and can contain letters, numbers, spaces, some special characters (:;,_-) and references ([1] to [99]) and must be 3-20 characters long.');
                }

                // References shouldn't follow each other directly, because LOVD wouldn't know the separation character.
                if (preg_match('/(\[[0-9]{1,2}\]){2,}/', $_POST['pattern_text'])) {
                    lovd_errorAdd('The link pattern is found to be incorrect. Two or more references directly after each other must be separated by at least one character to keep the two apart.');
                }
            }
        }

        // Check references in the pattern and replacement texts.
        if (!lovd_error()) {
            // Isolate reference numbers.
            $aPattern = explode(']', $_POST['pattern_text']);
            $aPatternRefs = array();
            foreach ($aPattern as $val) {
                if (substr_count($val, '[')) {
                    $aPatternRefs[] = substr(strrchr($val, '['), 1);
                }
            }

            // Isolate reference numbers.
            $aReplace = explode(']', $_POST['replace_text']);
            $aReplaceRefs = array();
            foreach ($aReplace as $val) {
                if (substr_count($val, '[')) {
                    $aReplaceRefs[] = substr(strrchr($val, '['), 1);
                }
            }

            // Check for reference order and/or references missing from the replacement text.
            reset($aPatternRefs);
            for ($i = 1; list(,$nRef) = each($aPatternRefs); $i ++) {
                if ($nRef != $i) {
                    lovd_errorAdd('The link pattern is found to be incorrect. Expected reference [' . $i . '] ' . ($i == 1? 'first' : 'after [' . ($i - 1) . ']') . ', got [' . $nRef . '].');
                }
            }

            foreach ($aReplaceRefs as $nRef) {
                if (!in_array($nRef, $aPatternRefs)) {
                    lovd_errorAdd('The link replacement text is found to be incorrect. Could not find used reference [' . $nRef . '] in link pattern.');
                }
            }
        }

        if (!lovd_error()) {
            // Query text.
            $sQ = 'UPDATE ' . TABLE_LINKS . ' SET ';

            // Standard fields to be used.
            $aQ = array('linkname', 'pattern_text', 'replace_text', 'link_description', 'active');

            foreach ($aQ as $key => $val) {
                $sQ .= ($key? ', ' : '') . $val . ' = "' . $_POST[$val] . '"';
            }

            $sQ .= ', edited_by = "' . $_AUTH['userid'] . '", edited_date = NOW() WHERE linkid = ' . $zData['linkid'];

            $q = mysql_query($sQ);
            if (!$q) {
                $sError = mysql_error(); // Save the mysql_error before it disappears.
                require ROOT_PATH . 'inc-top.php';
                lovd_printHeader('setup_links_manage', 'LOVD Setup - Manage custom links');
                lovd_dbFout('LinkEdit', $sQ, $sError);
            }

            // Write to log...
            lovd_writeLog('MySQL:Event', 'LinkEdit', $_AUTH['username'] . ' (' . mysql_real_escape_string($_AUTH['name']) . ') successfully edited link ' . $zData['linkid'] . ' (' . $_POST['linkname'] . ')');

            // Change active columns?
            // Fetch columns link is currently active for.
            $aCols = array();
            $q = mysql_query('SELECT colid FROM ' . TABLE_COLS2LINKS . ' WHERE linkid = "' . $zData['linkid'] . '"');
            while ($r = mysql_fetch_row($q)) {
                $aCols[] = $r[0];
            }

            // 2008-03-04; 2.0-05; To prevent errors to show up on screen.
            if (!isset($_POST['active_columns'])) {
                $_POST['active_columns'] = array();
            }

            // Remove column.
            foreach ($aCols AS $sCol) {
                if (!in_array($sCol, $_POST['active_columns'])) {
                    // User has requested removal...
                    $q = mysql_query('DELETE FROM ' . TABLE_COLS2LINKS . ' WHERE colid = "' . $sCol . '" AND linkid = "' . $zData['linkid'] . '"');
                    if (!$q) {
                        // Silent error.
                        lovd_writeLog('MySQL:Error', 'LinkEdit', $zData['linkid'] . ' (' . $_POST['linkname'] . ') could not be removed from ' . $sCol);
                    } else {
                        lovd_writeLog('MySQL:Event', 'LinkEdit', $zData['linkid'] . ' (' . $_POST['linkname'] . ') successfully removed from ' . $sCol);
                    }
                }
            }

            // Add column.
            foreach ($_POST['active_columns'] AS $sCol) {
                if (!in_array($sCol, $aCols)) {
                    // User has requested addition...
                    $q = mysql_query('INSERT INTO ' . TABLE_COLS2LINKS . ' VALUES ("' . $sCol . '", "' . $zData['linkid'] . '")');
                    if (!$q) {
                        // Silent error.
                        lovd_writeLog('MySQL:Error', 'LinkEdit', $zData['linkid'] . ' (' . $_POST['linkname'] . ') could not be added to ' . $sCol);
                    } else {
                        lovd_writeLog('MySQL:Event', 'LinkEdit', $zData['linkid'] . ' (' . $_POST['linkname'] . ') successfully added to ' . $sCol);
                    }
                }
            }

            // Thank the user...
            header('Refresh: 3; url=' . PROTOCOL . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . '?action=view&view=' . $_GET['edit'] . lovd_showSID(true));

            require ROOT_PATH . 'inc-top.php';
            lovd_printHeader('setup_links_manage', 'LOVD Setup - Manage custom links');
            print('      Successfully updated link ' . stripslashes($_POST['linkname']) . '!<BR><BR>' . "\n\n");

            require ROOT_PATH . 'inc-bot.php';
            exit;

        } else {
            // Errors, so the whole lot returns to the form.
            lovd_magicUnquoteAll();
        }

    } else {
        foreach ($zData as $key => $val) {
            if (!isset($_POST[$key]) || !$_POST[$key]) {
                $_POST[$key] = $val;
            }
        }
        $_POST['password'] = '';

        // Load connected columns.
        $_POST['active_columns'] = array();
        $q = mysql_query('SELECT colid FROM ' . TABLE_COLS2LINKS . ' WHERE linkid = "' . $zData['linkid'] . '"');
        while ($r = mysql_fetch_row($q)) {
            $_POST['active_columns'][] = $r[0];
        }
    }



    require ROOT_PATH . 'inc-top.php';
    lovd_printHeader('setup_links_manage', 'LOVD Setup - Manage custom links');

    lovd_errorPrint();

    // Table.
    print('      <FORM action="' . $_SERVER['PHP_SELF'] . '?action=' . $_GET['action'] . '&amp;edit=' . $zData['linkid'] . '&amp;sent=true" method="post">' . "\n" .
          '        <TABLE border="0" cellpadding="0" cellspacing="1" width="760">');

    // Get column list, to connect link to column.
    $aCols = array();
    $aCols['Gene/Reference'] = 'Gene/Reference (Gene homepage)';
    $qCols = mysql_query('SELECT colid, CONCAT(colid, " (", head_column, ")") AS head_column FROM ' . TABLE_COLS . ' ORDER BY colid');
    $nCols = mysql_num_rows($qCols);
    $nColSize = ($nCols < 10? $nCols : 10);
    while ($r = mysql_fetch_row($qCols)) {
        $aCols[$r[0]] = $r[1];
    }

    // Array which will make up the form table.
    $aForm = array(
                    array('POST', '', '', '50%', '50%'),
                    array('', 'print', '<B>Link details</B>'),
                    array('Link name', 'text', 'linkname', 30),
                    array('Pattern', 'text', 'pattern_text', 30),
                    array('', 'print', '<SPAN class="form_note">The pattern is bound to some rules:<UL style="margin : 0px; padding-left : 1.5em;"><LI>It must start with \'{\' and end with \'}\'.</LI><LI>It can contain letters, numbers, spaces, some special characters (:;,_-) and references ([1] to [99]).</LI><LI>It must be 3-20 characters long.</LI><LI>Two or more references directly after each other must be separated by at least one character to keep the two apart.</LI></UL></SPAN>'),
                    array('Replacement text', 'textarea', 'replace_text', 40, 3),
                    array('', 'print', '<SPAN class="form_note">Make sure you use all references from the pattern in the replacement text.</SPAN>'),
                    array('Link description', 'textarea', 'link_description', 40, 3),
                    array('', 'print', '<SPAN class="form_note">To aid other users in using your custom link, please provide some information on what the link is for and how to use the references.</SPAN>'),
                    'skip',
                    array('', 'print', '<B>Link settings</B>'),
                    array('Active', 'checkbox', 'active', 1),
                    array('Active for columns', 'select', 'active_columns', $nColSize, $aCols, false, true, false),
                    'skip',
                    array('', 'submit', 'Edit custom link'),
                  );
    $_MODULES->processForm('SetupLinksEdit', $aForm);
    lovd_viewForm($aForm);

    print('</TABLE></FORM>' . "\n\n");

    require ROOT_PATH . 'inc-bot.php';
    exit;





} elseif ($_GET['action'] == 'drop' && is_numeric($_GET['drop'])) {
    // Drop specific link.

    $zData = @mysql_fetch_assoc(mysql_query('SELECT * FROM ' . TABLE_LINKS . ' WHERE linkid = "' . $_GET['drop'] . '"'));
    if (!$zData) {
        // Wrong ID, apparently.
        require ROOT_PATH . 'inc-top.php';
        lovd_printHeader('setup_links_manage', 'LOVD Setup - Manage custom links');
        print('      No such ID!<BR>' . "\n");
        require ROOT_PATH . 'inc-bot.php';
        exit;
    }

    // Require form functions.
    require ROOT_PATH . 'inc-lib-form.php';

    if (isset($_GET['sent'])) {
        lovd_errorClean();

        // Mandatory fields.
        $aCheck =
                 array(
                        'password' => 'Enter your password for authorization',
                      );

        foreach ($aCheck as $key => $val) {
            if (empty($_POST[$key])) {
                lovd_errorAdd('Please fill in the \'' . $val . '\' field.');
            }
        }

        // User had to enter his/her password for authorization.
        if ($_POST['password'] && md5($_POST['password']) != $_AUTH['password']) {
            lovd_errorAdd('Please enter your correct password for authorization.');
        }

        if (!lovd_error()) {
            // Query text; clean columns associations first.
            $sQ = 'DELETE FROM ' . TABLE_COLS2LINKS . ' WHERE linkid = "' . $zData['linkid'] . '"';
            $q = mysql_query($sQ);
            if (!$q) {
                lovd_dbFout('LinkDrop', $sQ, mysql_error(), false);
            }

            // Query text.
            $sQ = 'DELETE FROM ' . TABLE_LINKS . ' WHERE linkid = "' . $zData['linkid'] . '"';

            $q = mysql_query($sQ);
            if (!$q) {
                $sError = mysql_error(); // Save the mysql_error before it disappears.
                require ROOT_PATH . 'inc-top.php';
                lovd_printHeader('setup_links_manage', 'LOVD Setup - Manage custom links');
                lovd_dbFout('LinkDrop', $sQ, $sError);
            }

            // Write to log...
            lovd_writeLog('MySQL:Event', 'LinkDrop', $_AUTH['username'] . ' (' . mysql_real_escape_string($_AUTH['name']) . ') successfully deleted link ' . $zData['linkid'] . ' (' . mysql_real_escape_string($zData['linkname']) . ')');

            // Thank the user...
            header('Refresh: 3; url=' . PROTOCOL . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . '?action=view_all' . lovd_showSID(true));

            require ROOT_PATH . 'inc-top.php';
            lovd_printHeader('setup_links_manage', 'LOVD Setup - Manage custom links');
            print('      Successfully deleted link ' . $zData['linkname'] . '!<BR><BR>' . "\n\n");

            require ROOT_PATH . 'inc-bot.php';
            exit;

        } else {
            // Errors, so the whole lot returns to the form.
            lovd_magicUnquoteAll();

            // Because we're sending the data back to the form, I need to unset the password fields!
            unset($_POST['password']);
        }
    }



    require ROOT_PATH . 'inc-top.php';
    lovd_printHeader('setup_links_manage', 'LOVD Setup - Manage custom links');

    lovd_errorPrint();

    // Table.
    print('      <FORM action="' . $_SERVER['PHP_SELF'] . '?action=' . $_GET['action'] . '&amp;drop=' . $zData['linkid'] . '&amp;sent=true" method="post">' . "\n" .
          '        <TABLE border="0" cellpadding="0" cellspacing="1" width="760">');

    // Array which will make up the form table.
    $aForm = array(
                    array('POST', '', '', '50%', '50%'),
                    array('Deleting custom link', 'print', $zData['linkid'] . ' (' . $zData['linkname'] . ')'),
                    'skip',
                    array('Enter your password for authorization', 'password', 'password', 20),
                    array('', 'submit', 'Delete custom link'),
                  );
    $_MODULES->processForm('SetupLinksDelete', $aForm);
    lovd_viewForm($aForm);

    print('</TABLE></FORM>' . "\n\n");

    require ROOT_PATH . 'inc-bot.php';
    exit;





} else {
    // Default action:
    header('Location: ' . PROTOCOL . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . '?action=view_all' . lovd_showSID(true));
    exit;
}
?>
