function printFooterHTML($bCloseHTML = true) { // Print the LOVD footer, including the update checker and mapper (if $bFull == true). global $_AUTH, $_SETT, $_STAT; if (ROOT_PATH == '../' && !(defined('TAB_SELECTED') && TAB_SELECTED == 'docs')) { // In the install directory, closing the tables opened by /install/index.php that /install/inc-bot.php used to close. print "\n\n" . ' </TD>' . "\n" . ' </TR>' . "\n" . '</TABLE>' . "\n"; } ?> </TD> </TR> </TABLE> <?php if (!$this->bFull) { if ($bCloseHTML) { // Close the <BODY> and <HTML> tags. Normal behaviour except when for instance the Progress Bar is used. print "\n" . '</BODY>' . "\n" . '</HTML>' . "\n"; } return true; } ?> </DIV> <BR> <TABLE border="0" cellpadding="0" cellspacing="0" width="100%" class="footer"> <TR> <TD width="84"> </TD> <TD align="center"> <?php if (substr(lovd_getProjectFile(), 0, 6) == '/docs/') { // In documents section. print ' For the latest version of the LOVD manual, <A href="' . $_SETT['upstream_URL'] . $_SETT['system']['tree'] . '/docs/" target="_blank">check the online version</A>.<BR>' . "\n"; } print ' Powered by <A href="' . $_SETT['upstream_URL'] . $_STAT['tree'] . '/" target="_blank">LOVD v.' . $_STAT['tree'] . '</A> Build ' . $_STAT['build'] . '<BR>' . "\n" . ' LOVD software ©2004-2016 <A href="http://www.lumc.nl/" target="_blank">Leiden University Medical Center</A>' . "\n"; ?> </TD> <TD width="42" align="right"> <IMG src="gfx/lovd_mapping_99.png" alt="" title="" width="32" height="32" id="mapping_progress" style="margin : 5px;"> </TD> <TD width="42" align="right"> <?php if (!(defined('NOT_INSTALLED') || defined('MISSING_CONF') || defined('MISSING_STAT'))) { if (time() - strtotime($_STAT['update_checked_date']) > 60 * 60 * 24) { // Check for updates! $sImgURL = 'check_update?icon'; } else { // No need to re-check, use saved info. if ($_STAT['update_version'] == 'Error') { $sType = 'error'; } elseif (lovd_calculateVersion($_STAT['update_version']) > lovd_calculateVersion($_SETT['system']['version'])) { $sType = 'newer'; } else { $sType = 'newest'; } $sImgURL = 'gfx/lovd_update_' . $sType . '_blue.png'; } if ($_AUTH && ($_AUTH['level'] >= LEVEL_MANAGER || count($_AUTH['curates']))) { print ' <A href="#" onclick="lovd_openWindow(\'' . lovd_getInstallURL() . 'check_update\', \'CheckUpdate\', 650, 175); return false;"><IMG src="' . $sImgURL . '" alt="" width="32" height="32" style="margin : 5px;"></A>' . "\n"; } else { print ' <IMG src="' . $sImgURL . '" alt="" width="32" height="32" style="margin : 5px;">' . "\n"; } } ?> </TD> </TR> </TABLE> </TD></TR></TABLE> <SCRIPT type="text/javascript"> <!-- <?php if (!(ROOT_PATH == '../' && !(defined('TAB_SELECTED') && TAB_SELECTED == 'docs') || defined('NOT_INSTALLED'))) { // In install directory. print ' function lovd_mapVariants () { // This function requests the script that will do the actual work. // First unbind any onclick handlers on the status image. $("#mapping_progress").unbind(); // Now request the script. $.get("ajax/map_variants.php", function (sResponse) { // The server responded successfully. Let\'s see what he\'s saying. aResponse = sResponse.split("\\t"); $("#mapping_progress").attr({"src": "gfx/lovd_mapping_" + aResponse[1] + (aResponse[1] == "preparing"? ".gif" : ".png"), "title": aResponse[2]}); if (sResponse.indexOf("Notice") >= 0 || sResponse.indexOf("Warning") >= 0 || sResponse.indexOf("Error") >= 0 || sResponse.indexOf("Fatal") >= 0) { // Something went wrong while processing the request, don\'t try again. $("#mapping_progress").attr({"src": "gfx/lovd_mapping_99.png", "title": "There was a problem with LOVD while mapping variants to transcripts: " + sResponse}); } else if (aResponse[0] == "' . AJAX_TRUE . '") { // More variants to map. Re-call. setTimeout("lovd_mapVariants()", 50); } else { // No more variants to map. But allow the user to try. $("#mapping_progress").click(lovd_mapVariants); } } ).fail(function (oObject, sStatus) { // Something went wrong while contacting the server, don\'t try again. $("#mapping_progress").attr({"src": "gfx/lovd_mapping_99.png", "title": "There was a problem with LOVD while mapping variants to transcripts: " + sStatus}); } ); } '; // Not every page request should trigger the mapping... if (!empty($_SESSION['mapping']['time_complete']) && $_SESSION['mapping']['time_complete'] >= time() - 60 * 60 * 24) { // If it is less than one day ago that mapping was complete, don't start it automatically, but allow the user to start it himself. print '$("#mapping_progress").click(lovd_mapVariants);' . "\n"; } elseif (!empty($_SESSION['mapping']['time_error']) && $_SESSION['mapping']['time_error'] >= time() - 60 * 60) { // If it is less than one hour ago that an error occurred, don't start it either. print '$("#mapping_progress").click(lovd_mapVariants);' . "\n"; print '$("#mapping_progress").attr("Title", "Mapping is temporarily suspended because of network problems on the last attempt. Click to retry.");' . "\n"; } else { // Start mapping! print 'setTimeout("lovd_mapVariants()", 500);' . "\n"; } } ?> // --> </SCRIPT> <?php if ($bCloseHTML) { // Close the <BODY> and <HTML> tags. Normal behaviour except when for instance the Progress Bar is used. print '</BODY>' . "\n" . '</HTML>' . "\n"; } else { flush(); @ob_end_flush(); // Can generate errors on the screen if no buffer found. } return true; }
function viewList($sViewListID = false, $aColsToSkip = array(), $bNoHistory = false, $bHideNav = false, $bOptions = false, $bOnlyRows = false, $bFindReplace = false) { // Show a viewlist for the current object. // Params: // bFindReplace if true, find & replace option is shown in viewlist options menu. // Views list of entries in the database, allowing search. global $_DB, $_INI, $_SETT; if (!defined('LOG_EVENT')) { define('LOG_EVENT', $this->sObject . '::viewList()'); } if (FORMAT == 'text/plain' && !defined('FORMAT_ALLOW_TEXTPLAIN')) { die('text/plain not allowed here'); } $bAjax = substr(lovd_getProjectFile(), 0, 6) == '/ajax/'; // ViewLists need an ID to identify the specific viewList, in case there are a few in one document. if (!$sViewListID || !is_string($sViewListID)) { $sViewListID = lovd_generateRandomID(); } else { $sViewListID = preg_replace('/[^A-Z0-9._-]+/i', '', $sViewListID); } if (!is_array($aColsToSkip)) { $aColsToSkip = array($aColsToSkip); } foreach ($this->aColumnsViewList as $sCol => $aCol) { if (!$aCol['view'] && !in_array($sCol, $aColsToSkip)) { $aColsToSkip[] = $sCol; } } require_once ROOT_PATH . 'inc-lib-viewlist.php'; // First, check if entries are in the database at all. $nTotal = $this->getCount(); if (!$nTotal && FORMAT == 'text/html') { if ($bOnlyRows) { die('0'); // Silent error. } lovd_showInfoTable('No entries in the database yet!', 'stop'); return 0; } // Process search fields (i.e. $_GET['search_...'] values) for viewlist. list($WHERE, $HAVING, $aArguments, $aBadSyntaxColumns, $aColTypes) = $this->processViewListSearchArgs($_GET); if ($WHERE) { $this->aSQLViewList['WHERE'] .= ($this->aSQLViewList['WHERE'] ? ' AND ' : '') . $WHERE; } if ($HAVING) { $this->aSQLViewList['HAVING'] .= ($this->aSQLViewList['HAVING'] ? ' AND ' : '') . $HAVING; } // SORT: Current settings, also implementing XSS check. if (!empty($_GET['order']) && $_GET['order'] === strip_tags($_GET['order'])) { $aOrder = explode(',', $_GET['order']); } else { $aOrder = array('', ''); } // SORT: Verify request and set default. if (empty($this->aColumnsViewList[$aOrder[0]]['db'][1])) { $aOrder[0] = $this->sSortDefault; } if ($aOrder[1] != 'ASC' && $aOrder[1] != 'DESC') { $aOrder[1] = $this->aColumnsViewList[$aOrder[0]]['db'][1]; } $sSQLOrderBy = $this->aColumnsViewList[$aOrder[0]]['db'][0] . ' ' . $aOrder[1]; if (in_array($aOrder[0], array('chromosome', 'VariantOnGenome/DNA'))) { // 2014-03-07; 3.0-10; We need to find the table alias of the VOG or genes table, because otherwise MySQL fails here ('chromosome' is ambiguous) if both are joined. // 2014-04-28; 3.0-10; Prefer the genes table, since it joins to VOG as well, but may not have results which messes up the order. $sAlias = ''; if (preg_match('/' . TABLE_GENES . ' AS ([a-z]+)/i', $this->aSQLViewList['FROM'], $aRegs)) { $sAlias = $aRegs[1]; } elseif (preg_match('/' . TABLE_VARIANTS . ' AS ([a-z]+)/i', $this->aSQLViewList['FROM'], $aRegs)) { $sAlias = $aRegs[1]; } $this->aSQLViewList['FROM'] .= ' LEFT OUTER JOIN ' . TABLE_CHROMOSOMES . ' AS chr ON (' . (!$sAlias ? '' : $sAlias . '.') . 'chromosome = chr.name)'; $sSQLOrderBy = 'chr.sort_id ' . $aOrder[1]; if ($aOrder[0] == 'VariantOnGenome/DNA') { $sSQLOrderBy .= ', position_g_start ' . $aOrder[1] . ', position_g_end ' . $aOrder[1] . ', `VariantOnGenome/DNA` ' . $aOrder[1]; } } elseif ($aOrder[0] == 'VariantOnTranscript/DNA') { $sSQLOrderBy = 'position_c_start ' . $aOrder[1] . ', position_c_start_intron ' . $aOrder[1] . ', position_c_end ' . $aOrder[1] . ', position_c_end_intron ' . $aOrder[1] . ', `VariantOnTranscript/DNA` ' . $aOrder[1]; } // At this point, we're not sure if we'll actually use the ORDER BY at all. $this->aSQLViewList['ORDER_BY'] = $sSQLOrderBy . (empty($this->aSQLViewList['ORDER_BY']) ? '' : ', ' . $this->aSQLViewList['ORDER_BY']); // Only print stuff if we're not in Ajax right now. if (!$bAjax && FORMAT == 'text/html') { // Keep the URL clean; disable any fields that are not used. lovd_includeJS('inc-js-viewlist.php' . (!$bNoHistory ? '' : '?nohistory')); lovd_includeJS('inc-js-tooltip.php'); // Print form; required for sorting and searching. // Because we don't want the form to submit itself while we are waiting for the Ajax response, we need to kill the native submit() functionality. print ' <FORM action="' . CURRENT_PATH . '" method="get" id="viewlistForm_' . $sViewListID . '" style="margin : 0px;" onsubmit="return false;">' . "\n" . ' <INPUT type="hidden" name="viewlistid" value="' . $sViewListID . '">' . "\n" . ' <INPUT type="hidden" name="object" value="' . $this->sObject . '">' . "\n" . (!isset($this->sObjectID) ? '' : ' <INPUT type="hidden" name="object_id" value="' . $this->sObjectID . '">' . "\n") . (!isset($this->nID) ? '' : ' <INPUT type="hidden" name="id" value="' . $this->nID . '">' . "\n") . (!ACTION ? '' : ' <INPUT type="hidden" name="' . ACTION . '" value="">' . "\n") . ' <INPUT type="hidden" name="order" value="' . implode(',', $aOrder) . '">' . "\n"; // Skipping (permanently hiding) columns. foreach ($aColsToSkip as $sCol) { if (array_key_exists($sCol, $this->aColumnsViewList)) { // Internet Explorer refuses to submit input with equal names. If names are different, everything works fine. // Somebody please tell me it's a bug and nobody's logical thinking. Had to include $sCol to make it work. print ' <INPUT type="hidden" name="skip[' . $sCol . ']" value="' . $sCol . '">' . "\n"; // Check if we're skipping columns, that do have a search value. If so, it needs to be sent on like this. if (isset($_GET['search_' . $sCol])) { print ' <INPUT type="hidden" name="search_' . $sCol . '" value="' . htmlspecialchars($_GET['search_' . $sCol]) . '">' . "\n"; } } } if ($bHideNav) { print ' <INPUT type="hidden" name="hidenav" value="true">' . "\n"; } if ($bOptions) { print ' <INPUT type="hidden" name="options" value="true">' . "\n"; } print "\n"; } // Make a reference variable of the session for cleaner code. $aSessionViewList =& $_SESSION['viewlists'][$sViewListID]; // To make row ids persist when the viewList is refreshed, we must store the row id in $_SESSION. if (!empty($aSessionViewList['row_id'])) { $this->sRowID = $aSessionViewList['row_id']; } else { $aSessionViewList['row_id'] = $this->sRowID; // Implies array creation. } // To make row links persist when the viewList is refreshed, we must store the row link in $_SESSION. if (!empty($aSessionViewList['row_link'])) { $this->sRowLink = $aSessionViewList['row_link']; } else { $aSessionViewList['row_link'] = $this->sRowLink; // Implies array creation. } // Process input values regarding find & replace. // User clicked preview. $bFRPreview = !empty($_GET['FRPreviewClicked_' . $sViewListID]); // Selected field name for replace. $sFRFieldname = isset($_GET['FRFieldname_' . $sViewListID]) ? $_GET['FRFieldname_' . $sViewListID] : null; // Display name of selected field. $sFRFieldDisplayname = isset($_GET['FRFieldDisplayname_' . $sViewListID]) ? $_GET['FRFieldDisplayname_' . $sViewListID] : null; // Search query for find & replace. $sFRSearchValue = isset($_GET['FRSearch_' . $sViewListID]) ? $_GET['FRSearch_' . $sViewListID] : null; // Replace value for find & replace. $sFRReplaceValue = isset($_GET['FRReplace_' . $sViewListID]) ? $_GET['FRReplace_' . $sViewListID] : null; // Type of matching. $sFRMatchType = isset($_GET['FRMatchType_' . $sViewListID]) ? $_GET['FRMatchType_' . $sViewListID] : null; // Flag stating whether all field content sould be replaced. $bFRReplaceAll = !empty($_GET['FRReplaceAll_' . $sViewListID]); // Predicted affected row count. $nFRRowsAffected = null; // Find & replace options parameter. $aFROptions = array('sFRMatchType' => $sFRMatchType, 'bFRReplaceAll' => $bFRReplaceAll); $nTotal = 0; // Overwrites the previous $nTotal. if (!count($aBadSyntaxColumns)) { // Build argument list. $aArgs = array_merge($aArguments['WHERE'], $aArguments['HAVING']); if ($bFRPreview) { // User clicked 'preview' in Find&Replace form, add F&R changes as a separate // column in the query. $nFRRowsAffected = $this->previewColumnFindAndReplace($sFRFieldname, $sFRFieldDisplayname, $sFRSearchValue, $sFRReplaceValue, $aArgs, $aFROptions); } // First find the amount of rows returned. We can use the SQL_CALC_FOUND_ROWS() // function, but we'll try to avoid that due to extreme slowness in some cases. // getRowCountForViewList() will take care of that. // There is talk about a possible race condition using this technique on the mysql_num_rows man page, but I could find no evidence of it's existence on InnoDB tables. // Just to be sure, I'm implementing a serializable transaction, which should lock the table between the two SELECT queries to ensure proper results. // Last checked 2010-01-25, by Ivo Fokkema. $_DB->query('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE'); $_DB->beginTransaction(); // For ALL viewlists, we store the number of hits that we get, including the current filters. // For large tables, getting a count can take a long time (especially when using SQL_CALC_FOUND_ROWS). // ORDER BY is absolutely killing on large result sets. // So, long time to retrieve count (>1s) => don't count again, and no sort. // Count OK (<=1s), but big result set (250K) => no sort. ($_SETT['lists']['max_sortable_rows']) // 1) If we don't have a count in memory, request count separately. // Also if last count was >15min ago, request again. $bTrueCount = false; // Indicates whether or not we are sure about the number of results. $sFilterMD5 = md5($WHERE . '||' . $HAVING . '||' . implode('|', $aArgs)); // A signature for the filters, NOTE that this depends on the column order! // FIXME: If this count takes longer than 1s, we don't estimate anymore like we used to (see line 1543). if (true || !isset($aSessionViewList['counts'][$sFilterMD5]['n'])) { $t = microtime(true); // Now, get the total number of hits if no LIMIT was used. Note that $nTotal gets overwritten here. $nTotal = $this->getRowCountForViewList($this->aSQLViewList, $aArgs); $tQ = microtime(true) - $t; $aSessionViewList['counts'][$sFilterMD5]['n'] = $nTotal; $aSessionViewList['counts'][$sFilterMD5]['t'] = $tQ; $aSessionViewList['counts'][$sFilterMD5]['d'] = time(); $bTrueCount = true; } // Manipulate SELECT to include SQL_CALC_FOUND_ROWS. $bSQLCALCFOUNDROWS = false; // TODO: Remove this block. For now, this will be bypassed because $bTrueCount will always be true. if (!$bTrueCount && $_INI['database']['driver'] == 'mysql' && ($aSessionViewList['counts'][$sFilterMD5]['t'] < 1 || $aSessionViewList['counts'][$sFilterMD5]['d'] < time() - 60 * 15)) { // But only if we're using MySQL and it takes less than a second to get the correct number of results, or it's been more than 15 minutes since the last check! $this->aSQLViewList['SELECT'] = 'SQL_CALC_FOUND_ROWS ' . $this->aSQLViewList['SELECT']; $bSQLCALCFOUNDROWS = true; } if ($bOptions) { // If the session variable does not exist, create it! if (!isset($aSessionViewList['checked'])) { $aSessionViewList['checked'] = array(); } if (isset($_GET['ids_changed'])) { if ($_GET['ids_changed'] == 'all') { // If the select all button was clicked, fetch all entries and mark them as 'checked' in session. // This query is the same as the viewList query, but without the ORDER BY and LIMIT, so that we can get the full result // of the query. $sSQL = $this->buildSQL(array('SELECT' => $this->aSQLViewList['SELECT'], 'FROM' => $this->aSQLViewList['FROM'], 'WHERE' => $this->aSQLViewList['WHERE'], 'GROUP_BY' => $this->aSQLViewList['GROUP_BY'], 'HAVING' => $this->aSQLViewList['HAVING'])); $q = $_DB->query($sSQL, $aArgs); while ($zData = $q->fetchAssoc()) { $zData = $this->generateRowID($zData); // We only need the row_id here for knowing which ones we need to check. // 2015-09-18; 3.0-14; We need to run rawurldecode() or else Columns are not selectable this way. $aSessionViewList['checked'][] = rawurldecode($zData['row_id']); } } elseif ($_GET['ids_changed'] == 'none') { // If the unselect all button was clicked, reset the 'checked' array. $aSessionViewList['checked'] = array(); } else { // Get the changed ids and remove them from or add them to the session. $aIDsChanged = explode(';', $_GET['ids_changed']); // Flip the keys & values, so that we can do a simple isset() to see if the id is already present. $aSessionViewList['checked'] = array_flip($aSessionViewList['checked']); // Determine the highest key number, so we can use that later when adding new values to the array. $nIndex = count($aSessionViewList['checked']) ? max($aSessionViewList['checked']) + 1 : 0; foreach ($aIDsChanged as $nID) { if (isset($aSessionViewList['checked'][$nID])) { // ID is found in the array, but is also in the 'ids_changed' array, so remove it! unset($aSessionViewList['checked'][$nID]); } else { // ID is not found in the array, but IS in the 'ids_changed' array, so add it using the $nIndex as value we determined earlier. // Also add 1 to the $nIndex so that the next id that needs to be added will not overwrite this one. $aSessionViewList['checked'][$nID] = ++$nIndex; } } // Flip the array back to its original state. $aSessionViewList['checked'] = array_flip($aSessionViewList['checked']); } } } // ORDER BY will only occur when we estimate we have time for it. if ($aSessionViewList['counts'][$sFilterMD5]['t'] < 1 && $aSessionViewList['counts'][$sFilterMD5]['n'] <= $_SETT['lists']['max_sortable_rows']) { $bSortableVL = true; } else { // Not sortable, indicate this on the VL... $aOrder = array('', ''); $bSortableVL = false; // 2013-07-03; 3.0-07; However, we do try and sort because in principle, the order is random and this may cause confusion while paginating. // So, as a result we'll try and sort on the PK. We attempt to determine this from the GROUP BY or ID col in the VL columns list. $sCol = ''; if (isset($this->aSQLViewList['GROUP_BY'])) { $sCol = $this->aSQLViewList['GROUP_BY']; } elseif ($this->aColumnsViewList['id']) { $sCol = $this->aColumnsViewList['id']['db'][0]; } elseif ($this->aColumnsViewList['id_']) { $sCol = $this->aColumnsViewList['id_']['db'][0]; } $this->aSQLViewList['ORDER_BY'] = $sCol; } if (!$bHideNav && FORMAT == 'text/html') { // Implement LIMIT only if navigation is not hidden. // We have a problem here, because we don't know how many hits there are, // because we're using SQL_CALC_FOUND_ROWS which only gives us the number // of hits AFTER we run the whole query. This means we should just assume // the page number is possible. $this->aSQLViewList['LIMIT'] = lovd_pagesplitInit(); // Function requires variable names $_GET['page'] and $_GET['page_size']. } $sSQL = $this->buildSQL($this->aSQLViewList); // Run the viewList query. // FIXME; what if using AJAX? Probably we should generate a number here, if this query fails, telling the system to try once more. If that fails also, the JS should throw a general error, maybe. $q = $_DB->query($sSQL, $aArgs); // Now, get the total number of hits as if no LIMIT was used (when we have used the proper SELECT syntax). Note that $nTotal gets overwritten here. if ($bSQLCALCFOUNDROWS) { // FIXME: 't' needs to be recalculated as well! $nTotal = $_DB->query('SELECT FOUND_ROWS()')->fetchColumn(); $aSessionViewList['counts'][$sFilterMD5]['n'] = $nTotal; $aSessionViewList['counts'][$sFilterMD5]['d'] = time(); $bTrueCount = true; } else { // Estimate the number of results! $nTotal = $aSessionViewList['counts'][$sFilterMD5]['n']; } $_DB->commit(); // To end the transaction and the locks that come with it. } else { // Set certain values that are needed for hiding notices, applicable for the "incorrect syntax" error message. $bTrueCount = true; // Yes, we're sure we have 0 results. $bSortableVL = false; // Sorting makes no sense when you have no results. } // If no results are found, try to figure out if it was because of the user's searching or not. if (!$nTotal) { $bSearched = false; $aHiddenSearch = array(); foreach ($_GET as $key => $value) { if (substr($key, 0, 7) == 'search_') { $sColumn = substr($key, 7); if (!in_array($sColumn, $aColsToSkip)) { $bSearched = true; } elseif ($this->aColumnsViewList[$sColumn]['view']) { $sColHeader = $this->aColumnsViewList[$sColumn]['view'][0]; // Make sure all hidden ID columns have "ID" in the header, so we can recognize them. if (substr(rtrim($sColumn, '_'), -2) == 'id' && substr($sColHeader, -3) != ' ID') { $sColHeader .= ' ID'; } $aHiddenSearch[$sColHeader] = $value; } } } } // FIXME; this is a temporary hack just to get the genes?authorize working when all users have been selected. // There is no longer a viewList when all users have been selected, but we need one for the JS execution. // Possibly, this code can be standardized a bit and, if necessary for other viewLists as well, can be kept here. if (!$nTotal && !$bSearched && ($this->sObject == 'User' && !empty($_GET['search_id']))) { // FIXME; Maybe check for JS contents of the rowlink? // There has been searched, but apparently the ID column is forced hidden. This must be the authorize page. $bSearched = true; // This will trigger the creation of the viewList table. } if (FORMAT == 'text/html' && ($nTotal || $bSearched)) { // Only print stuff if we're not just loading one entry right now. if (!$bOnlyRows) { if (!$bAjax) { print ' <DIV id="viewlistDiv_' . $sViewListID . '">' . "\n"; // These contents will be replaced by Ajax. } // If we have a legend, create a hidden DIV that will be used for the full legend. print ' <DIV id="viewlistLegend_' . $sViewListID . '" title="Legend" style="display : none;">' . "\n" . ' <H2 class="LOVD">Legend</H2>' . "\n\n" . ' <I class="S11">Please note that a short description of a certain column can be displayed when you move your mouse cursor over the column\'s header and hold it still. Below, a more detailed description is shown per column.</I><BR><BR>' . "\n\n"; $bLegend = false; // We need to check if we have a legend at all. foreach ($this->aColumnsViewList as $sField => $aCol) { if (!empty($aCol['legend'])) { $bLegend = true; if (empty($aCol['legend'][1])) { $aCol['legend'][1] = $aCol['legend'][0]; } print ' <B>' . $aCol['view'][0] . '</B>: ' . $aCol['legend'][1]; if (substr($aCol['legend'][1], -5) == '</UL>') { // No additional breaks, no possible listing of selection options. Column has its own UL already. print "\n\n"; continue; } if (isset($this->aColumns[$sField]) && $this->aColumns[$sField]['form_type'][2] == 'select') { // This is a custom column and it has a selection list with options. List the options below. print '<BR>' . "\n" . ' All options:' . "\n" . ' <UL style="margin-top : 0px;">' . "\n"; foreach ($this->aColumns[$sField]['select_options'] as $sOption) { print ' <LI>' . $sOption . '</LI>' . "\n"; } print ' </UL>' . "\n\n"; } else { print '<BR><BR>' . "\n\n"; } } } print ' </DIV>' . "\n\n"; if (!$bHideNav) { lovd_pagesplitShowNav($sViewListID, $nTotal, $bTrueCount, $bSortableVL, $bLegend); } // 'checked' attribute values for find & replace menu options. $sFRMatchtypeCheck1 = !isset($sFRMatchType) || $sFRMatchType == '1' ? 'checked' : ''; $sFRMatchtypeCheck2 = $sFRMatchType == '2' ? 'checked' : ''; $sFRMatchtypeCheck3 = $sFRMatchType == '3' ? 'checked' : ''; $sFRReplaceAllCheck = $bFRReplaceAll ? 'checked' : ''; $sFRRowsAffected = !is_null($nFRRowsAffected) ? strval($nFRRowsAffected) : ''; $sFRFieldname = htmlspecialchars($sFRFieldname); $sFRFieldDisplayname = htmlspecialchars($sFRFieldDisplayname); $sFRSearchValue = htmlspecialchars($sFRSearchValue); $sFRReplaceValue = htmlspecialchars($sFRReplaceValue); // Print options menu for find & replace (hidden by default). print <<<FROptions <DIV id="viewlistFRFormContainer_{$sViewListID}" class="fnroptionsmenu" style="display: none;"> <SPAN><B style="color: red">Note that find & replace is still in BETA. Changes made using this feature are not checked for errors, therefore using find & replace may have destructive consequences.<BR>Make a download or backup of the data you're about to edit. If uncertain, use the edit form of the data entries instead.</B><BR> Applying find & replace to column "<B id="viewlistFRColDisplay_{$sViewListID}">{$sFRFieldname}</B>". <INPUT id="FRFieldname_{$sViewListID}" type="hidden" name="FRFieldname_{$sViewListID}" value="{$sFRFieldname}" /> <INPUT id="FRFieldDisplayname_{$sViewListID}" type="hidden" name="FRFieldDisplayname_{$sViewListID}" value="{$sFRFieldDisplayname}" /> <INPUT id="FRRowsAffected_{$sViewListID}" type="hidden" value="{$sFRRowsAffected}" /> </SPAN> <BR /> <TABLE> <TR> <TD>Text to find</TD> <TD> <INPUT type="text" name="FRSearch_{$sViewListID}" value="{$sFRSearchValue}" style="width: 110px" /> </TD> <TD> <INPUT type="radio" name="FRMatchType_{$sViewListID}" value="1" {$sFRMatchtypeCheck1} />Match anywhere <INPUT type="radio" name="FRMatchType_{$sViewListID}" value="2" {$sFRMatchtypeCheck2} />Match at beginning of field <INPUT type="radio" name="FRMatchType_{$sViewListID}" value="3" {$sFRMatchtypeCheck3} />Match at end of field </TD> </TR> <TR> <TD>Replace with</TD> <TD> <INPUT type="text" name="FRReplace_{$sViewListID}" value="{$sFRReplaceValue}" style="width: 110px" /> </TD> <TD> <INPUT type="checkbox" name="FRReplaceAll_{$sViewListID}" value="1" {$sFRReplaceAllCheck} />Replace everything in field </TD> </TR> </TABLE> <INPUT id="FRPreview_{$sViewListID}" type="button" value="Preview" /> <INPUT id="FRCancel_{$sViewListID}" type="button" value="Cancel" style="border : 1px solid #FF4422;" /> <DIV id="FRSubmitDiv_{$sViewListID}"> <BR> Enter your password to apply find and replace:<BR> <INPUT type="password" name="password" size="20" /> <INPUT id="FRSubmit_{$sViewListID}" type="submit" value="Submit" /> </DIV> </DIV> FROptions; // Table and search headers (if applicable). print ' <TABLE border="0" cellpadding="0" cellspacing="1" class="data" id="viewlistTable_' . $sViewListID . '">' . "\n" . ' <THEAD>' . "\n" . ' <TR>' . ($bOptions ? "\n" . ' <TH valign="center" style="text-align:center;">' . "\n" . ' <IMG id="viewlistOptionsButton_' . $sViewListID . '" src="gfx/options.png" width="16" height="16" style="cursor : pointer;"></TH>' : ''); foreach ($this->aColumnsViewList as $sField => $aCol) { if (in_array($sField, $aColsToSkip)) { continue; } $bSortable = !empty($aCol['db'][1]) && $bSortableVL; // If we can't sort at all, nothing is sortable. $bSearchable = !empty($aCol['db'][2]); $nAllowFindAndReplace = (int) (!empty($aCol['allowfnr'])); // Later allow other columns as well, such as owned_by or statusid or so. $sImg = ''; $sAlt = ''; if ($bSortable && $aOrder[0] == $sField) { $sImg = $aOrder[1] == 'DESC' ? '_desc' : '_asc'; $sAlt = $aOrder[1] == 'DESC' ? 'Descending' : 'Ascending'; } print "\n" . ' <TH valign="top"' . ($bSortable ? ' class="order' . ($aOrder[0] == $sField ? 'ed' : '') . '"' : '') . (empty($aCol['legend'][0]) ? '' : ' title="' . htmlspecialchars($aCol['legend'][0]) . '"') . ' data-allowfnr="' . $nAllowFindAndReplace . '" data-fieldname="' . $sField . '">' . "\n" . ' <IMG src="gfx/trans.png" alt="" width="' . $aCol['view'][1] . '" height="1" id="viewlistTable_' . $sViewListID . '_colwidth_' . $sField . '"><BR>' . (!$bSortable ? str_replace(' ', ' ', $aCol['view'][0]) . '<BR>' : "\n" . ' <DIV onclick="document.forms[\'viewlistForm_' . $sViewListID . '\'].order.value=\'' . $sField . ',' . ($aOrder[0] == $sField ? $aOrder[1] == 'ASC' ? 'DESC' : 'ASC' : $aCol['db'][1]) . '\'; if (document.forms[\'viewlistForm_' . $sViewListID . '\'].page) { document.forms[\'viewlistForm_' . $sViewListID . '\'].page.value=1; } lovd_AJAX_viewListSubmit(\'' . $sViewListID . '\');" style="position : relative;">' . "\n" . ' <IMG src="gfx/order_arrow' . $sImg . '.png" alt="' . $sAlt . '" title="' . $sAlt . '" width="13" height="12" style="position : absolute; top : 2px; right : 0px;">' . str_replace(' ', ' ', $aCol['view'][0]) . ' </DIV>') . (!$bSearchable ? '' : "\n" . ' <INPUT type="text" name="search_' . $sField . '" value="' . (!isset($_GET['search_' . $sField]) ? '' : htmlspecialchars($_GET['search_' . $sField])) . '" title="' . $aCol['view'][0] . ' field should contain...' . (!empty($_GET['search_' . $sField]) ? "\nCurrent search:\n\n" . htmlspecialchars(lovd_formatSearchExpression($_GET['search_' . $sField], $aColTypes[$sField])) : '') . '" style="width : ' . ($aCol['view'][1] - 6) . 'px; font-weight : normal;" onkeydown="if (event.keyCode == 13) { if (document.forms[\'viewlistForm_' . $sViewListID . '\'].page) { document.forms[\'viewlistForm_' . $sViewListID . '\'].page.value=1; } setTimeout(\'lovd_AJAX_viewListSubmit(\\\'' . $sViewListID . '\\\')\', 0); return false;}">') . '</TH>'; } print '</TR></THEAD>'; } } elseif (FORMAT == 'text/plain') { // Download format: show headers. $sObject = $this->sObject == 'Custom_ViewList' ? $this->sObjectID : $this->sObject . 's'; header('Content-type: text/plain; charset=UTF-8'); header('Content-Disposition: attachment; filename="LOVD_' . $sObject . '_' . date('Y-m-d_H.i.s') . '.txt"'); header('Pragma: public'); print '### LOVD-version ' . lovd_calculateVersion($_SETT['system']['version']) . ' ### ' . $sObject . ' Quick Download format ### This file can not be imported ###' . "\r\n"; // FIXME: this has to be done better, we can't see what we're filtering for, because it's in the arguments! $sFilter = $WHERE . ($WHERE && $HAVING ? ' AND ' : '') . $HAVING; if ($sFilter) { if (count($aArgs) == substr_count($sFilter, '?')) { foreach ($aArgs as $sArg) { $sFilter = preg_replace('/\\?/', ctype_digit($sArg) ? $sArg : '"' . $sArg . '"', $sFilter, 1); } } print '## Filter: ' . $sFilter . "\r\n"; } if (ACTION == 'downloadSelected') { print '## Filter: selected = ' . implode(',', $aSessionViewList['checked']) . "\r\n"; } print '# charset=UTF-8' . "\r\n"; $i = 0; foreach ($this->aColumnsViewList as $sField => $aCol) { if (in_array($sField, $aColsToSkip)) { continue; } print ($i++ ? "\t" : '') . '"{{' . $sField . '}}"'; } print "\r\n"; } if (!$nTotal && FORMAT == 'text/html') { if ($bSearched) { // Searched, but no results. FIXME: link to the proper documentation entry about search expressions $sBadSyntaxColumns = implode(', ', array_unique($aBadSyntaxColumns)); // FIXME; use an IF here. $sMessageNormal = 'No results have been found that match your criteria.<BR>Please redefine your search criteria.'; $sMessageBadSyntax = 'Your search column' . (count($aBadSyntaxColumns) > 1 ? 's contain' : ' contains') . ' incorrect search expression syntax at: ' . $sBadSyntaxColumns . '.'; $sMessage = empty($aBadSyntaxColumns) ? $sMessageNormal : $sMessageBadSyntax; if ($bOnlyRows) { die('0'); // Silent error. } // FIXME; This code is sort of duplicated, some 100 lines below we also print this, *if* results are found. print '</TABLE><BR>' . "\n"; // <BR> is necessary to keep the InfoTable apart from the data headers. if (!$bHideNav) { print ' <INPUT type="hidden" name="total" value="' . $nTotal . '" disabled>' . "\n" . ' <INPUT type="hidden" name="page_size" value="' . $_GET['page_size'] . '">' . "\n" . ' <INPUT type="hidden" name="page" value="' . $_GET['page'] . '">' . "\n"; } lovd_showInfoTable($sMessage, 'stop'); print ' </DIV></FORM>' . "\n\n"; } else { if ($bOnlyRows) { die('0'); // Silent error. } print ' <DIV id="viewlistDiv_' . $sViewListID . '">' . "\n"; // These contents will be replaced by Ajax. if (substr($this->sObject, -7) == 'Variant') { $sUnit = 'variants' . (substr($this->sObject, 0, 10) == 'Transcript' ? ' on transcripts' : ''); } elseif ($this->sObject == 'Custom_Viewlist') { $sUnit = 'entries'; } elseif ($this->sObject == 'Shared_Column') { $sUnit = 'active columns'; } else { $sUnit = strtolower($this->sObject) . 's'; } $sMessage = 'No ' . $sUnit . ' found'; if (!empty($aHiddenSearch)) { $sWhere = ''; foreach ($aHiddenSearch as $sCol => $sValue) { // If the hidden column has "ID" in its name, it is the primary filter column. if (substr($sCol, -3) == ' ID') { $sWhere .= ($sWhere ? ' and ' : ' ') . 'for this ' . strtolower(substr($sCol, 0, -3)); } else { $sWhere .= ($sWhere ? ' and ' : ' where ') . strtolower($sCol) . ' is "' . str_replace('|', '" or "', trim($sValue, '="') . '"'); } } $sMessage .= $sWhere; } lovd_showInfoTable($sMessage . '!', 'stop'); print ' </DIV></FORM>' . "\n\n"; return 0; } } // Now loop through the data and print. But check for $q to be set; if we had a bad search syntax, we end up here as well, but without an $q. while (isset($q) && $nTotal && ($zData = $q->fetchAssoc())) { // If row_id is not given by the database, but it should be created according to some format ($this->sRowID), put the data's ID in this format. $zData = $this->generateRowID($zData); // If row_link is not given by the database, but it should be created according to some format ($this->sRowLink), put the data's ID and the viewList's ID in this format. if (!isset($zData['row_link'])) { if ($this->sRowLink !== '' && $zData['row_id']) { $zData['row_link'] = str_replace(array('{{ID}}', '{{ViewListID}}'), array(rawurlencode($zData['row_id']), $sViewListID), $this->sRowLink); //$zData['row_link'] = preg_replace('/\{\{zData_(\w)+\}\}/', rawurlencode("$1"), $zData['row_link']); //$zData['row_link'] = preg_replace_callback('/\{\{zData_(\w+)\}\}/', create_function('$aRegs', 'global $zData; return rawurlencode($zData[$aRegs[1]]);'), $zData['row_link']); // FIXME; sorry, couldn't figure out how to do this in one line. Suggestions are welcome. foreach ($zData as $key => $val) { // Also allow data from $zData to be put into the row link & row id. // FIXME; This is a temporary ugly solution, so we need to fix this later!!!! $zData['row_link'] = preg_replace('/\\{\\{' . preg_quote($key, '/') . '\\}\\}/', rawurlencode($val), $zData['row_link']); $zData['row_link'] = preg_replace('/\\{\\{zData_' . preg_quote($key, '/') . '\\}\\}/', rawurlencode($val), $zData['row_link']); } } else { $zData['row_link'] = ''; } } $zData = $this->autoExplode($zData); // Only the CustomViewList object has this 3rd argument, but other objects' prepareData() // don't complain when called with this 3 argument they didn't define. $zData = $this->prepareData($zData, 'list', $sViewListID); if (FORMAT == 'text/html') { // FIXME; rawurldecode() in the line below should have a better solution. // IE (who else) refuses to respect the BASE href tag when using JS. So we have no other option than to include the full path here. print "\n" . ' <TR class="' . (empty($zData['class_name']) ? 'data' : $zData['class_name']) . '"' . (!$zData['row_id'] ? '' : ' id="' . $zData['row_id'] . '"') . ' valign="top"' . (!$zData['row_link'] ? '' : ' style="cursor : pointer;"') . (!$zData['row_link'] ? '' : ' onclick="' . (substr($zData['row_link'], 0, 11) == 'javascript:' ? rawurldecode(substr($zData['row_link'], 11)) : 'window.location.href = \'' . lovd_getInstallURL(false) . $zData['row_link'] . '\';') . '"') . '>'; if ($bOptions) { print "\n" . ' <TD align="center" class="checkbox" onclick="cancelParentEvent(event);"><INPUT id="check_' . $zData['row_id'] . '" class="checkbox" type="checkbox" name="check_' . $zData['row_id'] . '" onclick="lovd_recordCheckChanges(this, \'' . $sViewListID . '\');"' . (in_array($zData['row_id'], $aSessionViewList['checked']) ? ' checked' : '') . '></TD>'; } foreach ($this->aColumnsViewList as $sField => $aCol) { if (in_array($sField, $aColsToSkip)) { continue; } print "\n" . ' <TD' . (!empty($aCol['view'][2]) ? ' ' . $aCol['view'][2] : '') . ($aOrder[0] == $sField ? ' class="ordered"' : '') . '>' . ($zData[$sField] === '' ? '-' : $zData[$sField]) . '</TD>'; } print '</TR>'; } elseif (FORMAT == 'text/plain') { // Download format: print contents. if (ACTION == 'downloadSelected' && !in_array($zData['row_id'], $aSessionViewList['checked'])) { // Only selected entries should be downloaded. And this one is not selected. continue; } $i = 0; foreach ($this->aColumnsViewList as $sField => $aCol) { if (in_array($sField, $aColsToSkip)) { continue; } print ($i++ ? "\t" : '') . '"' . str_replace(array("\r\n", "\r", "\n"), array('\\r\\n', '\\r', '\\n'), addslashes(html_entity_decode(strip_tags($zData[$sField])))) . '"'; } print "\r\n"; } } // Only print stuff if we're not just loading one entry right now. if ($nTotal && !$bOnlyRows && FORMAT == 'text/html') { print '</TABLE>' . "\n"; if (!$bHideNav) { print ' <INPUT type="hidden" name="total" value="' . $nTotal . '" disabled>' . "\n" . ' <INPUT type="hidden" name="page_size" value="' . $_GET['page_size'] . '">' . "\n" . ' <INPUT type="hidden" name="page" value="' . $_GET['page'] . '">' . "\n\n"; lovd_pagesplitShowNav($sViewListID, $nTotal, $bTrueCount, $bSortableVL, $bLegend); } if (!$bAjax) { print ' </DIV></FORM><BR>' . "\n"; // These contents will be replaced by Ajax. } } if (!$bAjax && FORMAT == 'text/html') { // If sent using Ajax, the browser is not going to evaluate this code, anyways. print ' <SCRIPT type="text/javascript">' . "\n" . ' // This has to be run when the document has finished loading everything, because only then can it get the proper width from IE7 and lower!' . "\n" . ' $( function () {lovd_stretchInputs(\'' . $sViewListID . '\');});' . "\n"; if ($bOptions) { $sFRMenuOption = ''; if ($bFindReplace) { // Add find & replace menu item to viewlist options menu. $sFRMenuOption = <<<FRITEM ' <LI class="icon">' + ' <A click="lovd_FRColumnSelector(\\'{$sViewListID}\\');">' + ' <SPAN class="icon" style=""></SPAN>' + ' Find and replace text in column' + ' </A>' + ' </LI>' + FRITEM; } print <<<OPMENU // If menu's UL doesn't exist yet, create it. if (\$('#viewlistMenu_{$sViewListID}').attr('id') == undefined) { var oUL = window.document.createElement('ul'); oUL.setAttribute('id', 'viewlistMenu_{$sViewListID}'); oUL.className = 'jeegoocontext jeegooviewlist'; window.document.body.appendChild(oUL); } // Fix the top border that could not be set through jeegoo's style.css. \$('#viewlistMenu_{$sViewListID}').attr('style', 'border-top : 1px solid #000;'); \$('#viewlistMenu_{$sViewListID}').prepend( ' <LI class="icon">' + ' <A click="check_list[\\'{$sViewListID}\\'] = \\'all\\'; lovd_AJAX_viewListSubmit(\\'{$sViewListID}\\');">' + ' <SPAN class="icon" style="background-image: url(gfx/check.png);"></SPAN>' + ' Select all <SPAN>entries</SPAN>' + ' </A>' + ' </LI>' + ' <LI class="icon">' + ' <A click="check_list[\\'{$sViewListID}\\'] = \\'none\\'; lovd_AJAX_viewListSubmit(\\'{$sViewListID}\\');">' + ' <SPAN class="icon" style="background-image: url(gfx/cross.png);"></SPAN>' + ' Unselect all' + ' </A>' + ' </LI>' + {$sFRMenuOption} ' '); \$('#viewlistMenu_{$sViewListID}').append( ' <LI class="icon">' + ' <A click="lovd_AJAX_viewListSubmit(\\'{$sViewListID}\\', function(){lovd_AJAX_viewListDownload(\\'{$sViewListID}\\', true);});">' + ' <SPAN class="icon" style="background-image: url(gfx/menu_save.png);"></SPAN>' + ' Download all entries (summary data)' + ' </A>' + ' </LI>' + ' <LI class="icon">' + ' <A click="lovd_AJAX_viewListSubmit(\\'{$sViewListID}\\', function(){lovd_AJAX_viewListDownload(\\'{$sViewListID}\\', false);});">' + ' <SPAN class="icon" style="background-image: url(gfx/menu_save.png);"></SPAN>' + ' Download selected entries (summary data)' + ' </A>' + ' </LI>'); lovd_activateMenu('{$sViewListID}'); OPMENU; } print ' check_list[\'' . $sViewListID . '\'] = [];' . "\n" . ' </SCRIPT>' . "\n\n"; } return $nTotal; }
} else { // Error during update check. lovd_writeLog('Error', 'CheckUpdate', 'Could not parse upstream server output:' . "\n" . $sUpdates); $_DB->query('UPDATE ' . TABLE_STATUS . ' SET update_checked_date = ?, update_version = "Error", update_level = 0, update_description = "", update_released_date = NULL', array($sNow)); $_STAT['update_checked_date'] = $sNow; $_STAT['update_version'] = 'Error'; $_STAT['update_released_date'] = ''; $_STAT['update_level'] = 0; $_STAT['update_description'] = ''; } } // Process... if ($_STAT['update_version'] == 'Error') { $sType = 'error'; $sMessage = 'An error occured while checking for updates. For more information, see the error log. Please try again later.'; } elseif (lovd_calculateVersion($_STAT['update_version']) > lovd_calculateVersion($_SETT['system']['version'])) { $sType = 'newer'; $sMessage = 'There is an update to LOVD available. More information is below.<BR>' . "\n" . '<B>Latest version</B>: ' . $_STAT['update_version'] . '<BR>' . "\n" . '<B>Release date</B>: ' . $_STAT['update_released_date'] . '<BR>' . "\n" . '<B>Priority level</B>: ' . $_SETT['update_levels'][$_STAT['update_level']] . '<BR>' . "\n" . '<B>Release info</B>: ' . str_replace("\n", '<BR>', $_STAT['update_description']) . '<BR>' . "\n" . '<B>Download</B>: <A href="' . dirname($_SETT['update_URL']) . '/download.php?version=' . $_STAT['update_version'] . '&type=tar.gz">GZIPped TARball</A> or <A href="' . dirname($_SETT['update_URL']) . '/download.php?version=' . $_STAT['update_version'] . '&type=zip">ZIP archive</A><BR>' . "\n" . '<A href="' . $_SETT['upstream_URL'] . $_SETT['system']['tree'] . '/changelog.txt" target="_blank">See the changelog</A>' . "\n"; } else { $sType = 'newest'; $sMessage = 'There are currently no updates. Your LOVD installation is completely up to date.'; } // If we're requested to show the icon, we will do that and quit. Else we will provide some info. if (isset($_GET['icon'])) { // Create icon. header('Content-type: image/png'); readfile('gfx/lovd_update_' . $sType . '_blue.png'); exit; } else { // Print what we know about new versions... $_T->printHeader(false);
$_BAR->setMessage('Other user upgrading the database is still not finished.<BR>' . (isset($_GET['force_lock']) ? 'Forcing upgrade as requested...' : 'This may indicate something went wrong during upgrade.')); if (isset($_GET['force_lock'])) { $bLocked = false; } } else { $_BAR->setMessage('Upgrading database backend...'); } flush(); if (!$bLocked) { // There we go... // This recursive count returns a higher count then we would seem to want at first glance, // because each version's array of queries count as one as well. // However, because we will run one additional query per version, this number will be correct anyway. // 2012-02-02; 3.0-beta-02; But of course we should exclude the older versions... foreach ($aUpdates as $sVersion => $aSQL) { if (lovd_calculateVersion($sVersion) <= $sCalcVersionDB || lovd_calculateVersion($sVersion) > $sCalcVersionFiles) { unset($aUpdates[$sVersion]); } } $nSQL = count($aUpdates, true); // Actually run the SQL... $nSQLDone = 0; $nSQLDonePercentage = 0; $nSQLDonePercentagePrev = 0; $nSQLFailed = 0; $sSQLFailed = ''; foreach ($aUpdates as $sVersion => $aSQL) { $_BAR->setMessage('To ' . $sVersion . '...'); // Also set update_checked_date to NULL, so LOVD will again check for updates as soon as possible. $aSQL[] = 'UPDATE ' . TABLE_STATUS . ' SET version = "' . $sVersion . '", updated_date = NOW(), update_level = 0, update_checked_date = NULL'; // Loop needed queries...
$aDiseasesAlreadyWarnedFor = array(); // To prevent lots and lots of error messages for each phenotype entry created for the same disease that is not yet inserted into the database. $aSectionsAlreadyWarnedFor = array(); // To prevent lots and lots of error messages for a section that cannot be updated in the database. foreach ($aData as $i => $sLine) { $sLine = trim($sLine); if (!$sLine) { if (!lovd_endLine()) { break; } continue; } if (!$sFileVersion) { // Still looking for the LOVD version! We have a line here, so this must be what we're looking for. if (!preg_match('/^###\\s*LOVD-version\\s*([0-9]{4}\\-[0-9]{2}[a-z0-9])\\s*###\\s*([^#]+)\\s*###/', ltrim($sLine, '"'), $aRegs)) { lovd_errorAdd('import', 'File format not recognized; the first line of the imported file should contain the LOVD version header (### LOVD-version ' . lovd_calculateVersion($_SETT['system']['version']) . ' ### ... etc).'); } else { list(, $sFileVersion, $sFileType) = $aRegs; $sFileType = trim($sFileType); if (!isset($aTypes[$sFileType])) { // We did not understand the file type (Full data download, custom columns, genes, etc). lovd_errorAdd('import', 'File type not recognized; type "' . $sFileType . '" unknown.'); } else { if (isset($aExcludedTypes[$sFileType])) { // It is currently not possible to import owned data, see manual for details.. lovd_errorAdd('import', 'File type not allowed; ' . $aExcludedTypes[$sFileType] . ' '); } else { $sFileType = $aTypes[$sFileType]; // Clean $aParsed a bit, depending on the file type. if ($sFileType == 'Col') { $aParsed = array('Columns' => $aParsed['Columns']);
$sHeader = 'Custom column'; $sFilter = 'category'; $ID = $_PE[2]; lovd_requireAuth(LEVEL_MANAGER); } elseif ($_PE[1] == 'genes' && empty($_PE[2])) { // Download all genes. $sFileName = 'genes'; $sHeader = 'Gene data'; lovd_requireAuth(LEVEL_MANAGER); } else { exit; } // If we get here, we can print the header already. header('Content-Disposition: attachment; filename="LOVD_' . $sFileName . '_' . date('Y-m-d_H.i.s') . '.txt"'); header('Pragma: public'); print '### LOVD-version ' . lovd_calculateVersion($_SETT['system']['version']) . ' ### ' . $sHeader . ' download ### To import, do not remove or alter this header ###' . "\r\n"; if ($sFilter == 'owner') { print '## Filter: (created_by = ' . $ID . ' || owned_by = ' . $ID . ')' . "\r\n"; } elseif ($sFilter == 'gene') { print '## Filter: (gene = ' . $ID . ')' . "\r\n"; } elseif ($sFilter == 'category') { print '## Filter: (category = ' . $ID . ')' . "\r\n"; } print '# charset = UTF-8' . "\r\n\r\n"; // Prepare file creation by defining headers, columns and filters. // All data types have same settings: optional ownership filter, no columns hidden. $aDataTypeSettings = array('comments' => array(), 'data' => array(), 'filters' => array(), 'filter_other' => array(), 'hide_columns' => array(), 'prefetch' => false, 'settings' => array()); if ($sFilter == 'owner') { // We need to prefetch the filtered data to be able to filter other objects (genes etc). // Note: This will make all objects to be prefetched, which could be a bit of overkill here. $aDataTypeSettings['prefetch'] = true;