viewlist.php 14.1 KB
Newer Older
1 2 3 4 5 6
<?php
/*******************************************************************************
 *
 * LEIDEN OPEN VARIATION DATABASE (LOVD)
 *
 * Created     : 2010-02-18
7
 * Modified    : 2019-10-01
8
 * For LOVD    : 3.0-22
9
 *
10
 * Copyright   : 2004-2019 Leiden University Medical Center; http://www.LUMC.nl/
11 12 13
 * Programmers : Ivo F.A.C. Fokkema <I.F.A.C.Fokkema@LUMC.nl>
 *               Ivar C. Lugtenburg <I.C.Lugtenburg@LUMC.nl>
 *               Daan Asscheman <D.Asscheman@LUMC.nl>
14
 *               M. Kroon <m.kroon@lumc.nl>
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 *
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 *************/

define('ROOT_PATH', '../');
require ROOT_PATH . 'inc-init.php';
36
require_once ROOT_PATH . 'inc-lib-viewlist.php';
37

38
// Get viewlist-identifying arguments from request and check their validity.
Ivo Fokkema's avatar
Ivo Fokkema committed
39 40 41 42
$sViewListID = (isset($_REQUEST['viewlistid'])? $_REQUEST['viewlistid'] : '');
$sObject = (isset($_REQUEST['object'])? $_REQUEST['object'] : '');
$sObjectID = (isset($_REQUEST['object_id'])? $_REQUEST['object_id'] : '');
$nID = (isset($_REQUEST['id'])? $_REQUEST['id'] : '');
43 44

if (empty($sViewListID) || empty($sObject) || !preg_match('/^[A-Z_]+$/i', $sObject)) {
45
    die(AJAX_DATA_ERROR);
46 47
}

48 49 50 51 52
// The required security to load the viewList() depends on the data that is shown.
// To prevent security problems if we forget to set a requirement here, we default to LEVEL_ADMIN.
$aNeededLevel =
         array(
                'Column' => LEVEL_CURATOR,
53
                'Custom_ViewList' => 0,
54
                'Custom_ViewListMOD' => 0, // LOVD+
55 56
                'Disease' => 0,
                'Gene' => 0,
57
                'Gene_Panel' => LEVEL_SUBMITTER, // LOVD+
58
                'Gene_Panel_Gene' => LEVEL_SUBMITTER, // LOVD+
59 60
                'Gene_Panel_Gene_REV' => LEVEL_SUBMITTER, // LOVD+
                'Gene_Statistic' => LEVEL_SUBMITTER, // LOVD+
61 62
                'Genome_Variant' => 0,
                'Individual' => 0,
63
                'IndividualMOD' => 0, // LOVD+
64
                'Link' => LEVEL_MANAGER,
65
                'Log' => (LOVD_plus? LEVEL_SUBMITTER : LEVEL_MANAGER),
66 67
                'Phenotype' => 0,
                'Screening' => 0,
68
                'ScreeningMOD' => 0, // LOVD+
69
                'Shared_Column' => LEVEL_CURATOR,
70
                'Transcript' => 0,
71
                'Transcript_Variant' => 0,
72
                'User' => LEVEL_SUBMITTER, // Certain fields will be forcefully removed, though.
73
              );
74 75
if (isset($aNeededLevel[$sObject])) {
    $nNeededLevel = $aNeededLevel[$sObject];
76 77 78 79
} else {
    $nNeededLevel = LEVEL_ADMIN;
}

Ivo Fokkema's avatar
Ivo Fokkema committed
80 81
// 2013-06-28; 3.0-06; We can't allow just any custom viewlist without actually checking the shown objects. Screenings, for instance, does not have a built-in status check (since it doesn't have a status).
// Building list of allowed combinations of objects for custom viewlists.
82
if ($sObject == 'Custom_ViewList' && (!isset($sObjectID) || !in_array($sObjectID,
Ivo Fokkema's avatar
Ivo Fokkema committed
83 84 85
            array(
                'VariantOnGenome,Scr2Var,VariantOnTranscript', // Variants on I and S VEs.
                'Transcript,VariantOnTranscript,VariantOnGenome', // IN_GENE.
86
                'Transcript,VariantOnTranscript,VariantOnGenome,Screening,Individual', // LOVD-wide full data view (external viewer).
Ivo Fokkema's avatar
Ivo Fokkema committed
87
                'VariantOnTranscript,VariantOnGenome', // Gene-specific variant view.
88
                'VariantOnTranscriptUnique,VariantOnGenome', // Gene-specific unique variant view.
89 90
                'VariantOnTranscript,VariantOnGenome,Screening,Individual', // Gene-specific full data view.
                'Gene,Transcript,DistanceToVar')))) { // Map variant to transcript.
Ivo Fokkema's avatar
Ivo Fokkema committed
91 92 93
    die(AJAX_DATA_ERROR);
}

94 95 96
// We can't authorize Curators and Collaborators without loading their level!
// 2014-03-13; 3.0-10; Collaborators should of course also get their level loaded!
if ($_AUTH['level'] < LEVEL_MANAGER && (!empty($_AUTH['curates']) || !empty($_AUTH['collaborates']))) {
97
    if ($sObject == 'Column') {
98
        lovd_isAuthorized('gene', $_AUTH['curates']); // Any gene will do.
99 100
    } elseif ($sObject == 'Individual' && isset($_REQUEST['search_genes_searched']) && preg_match('/^="([^"]+)"$/', $_REQUEST['search_genes_searched'], $aRegs)) {
        lovd_isAuthorized('gene', $aRegs[1]); // Authorize for the gene currently searched (it currently restricts the view).
101 102
        // Since we're authorizing on $_REQUEST which also contains $_POST data, make sure the $_GET (what we actually filter on) matches what we authorize on!!!
        $_GET['search_genes_searched'] = $_REQUEST['search_genes_searched'];
103
    } elseif ($sObject == 'Transcript' && isset($_REQUEST['search_geneid']) && preg_match('/^="([^"]+)"$/', $_REQUEST['search_geneid'], $aRegs)) {
104
        lovd_isAuthorized('gene', $aRegs[1]); // Authorize for the gene currently searched (it currently restricts the view).
105 106
        // Since we're authorizing on $_REQUEST which also contains $_POST data, make sure the $_GET (what we actually filter on) matches what we authorize on!!!
        $_GET['search_geneid'] = $_REQUEST['search_geneid'];
107 108
    } elseif ($sObject == 'Shared_Column' && isset($_REQUEST['object_id'])) {
        lovd_isAuthorized('gene', $sObjectID); // Authorize for the gene currently loaded.
109 110 111 112 113
    } elseif ($sObject == 'Screening' && $sViewListID == 'Screenings_for_I_VE' && isset($_REQUEST['search_individualid']) && ctype_digit($_REQUEST['search_individualid'])) {
        // Screenings_for_I_VE has no ID but authorizes on search_individualid.
        lovd_isAuthorized('individual', $_REQUEST['search_individualid']); // Authorize for the Screening(s) currently searched (it restricts the view).
        // Since we're authorizing on $_REQUEST which also contains $_POST data, make sure the $_GET (what we actually filter on) matches what we authorize on!!!
        $_GET['search_individualid'] = $_REQUEST['search_individualid'];
114
    } elseif ($sObject == 'Custom_ViewList') {
Ivo Fokkema's avatar
Ivo Fokkema committed
115 116 117
        // 2013-06-28; 3.0-06; We can't just authorize users based on the given ID without actually checking the shown objects and checking if the search results are actually limited or not.
        // CustomVL_IN_GENE has no ID and does not require authorization (only public VOGs loaded).

118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
        if (!empty($_REQUEST['id'])) {
            // CustomVL_VOT_VOG_<<GENE>> is restricted per gene in the object argument, and search_transcriptid should contain a transcript ID that matches.
            // CustomVL_VIEW_<<GENE>> is restricted per gene in the object argument, and search_transcriptid should contain a transcript ID that matches.
            if (in_array($sObjectID,
                    array(
                        'VariantOnTranscript,VariantOnGenome',
                        'VariantOnTranscriptUnique,VariantOnGenome',
                        'VariantOnTranscript,VariantOnGenome,Screening,Individual'
                    )) && (!isset($_REQUEST['search_transcriptid'])
                    || !$_DB->query('SELECT COUNT(*) FROM ' . TABLE_TRANSCRIPTS . ' WHERE id = ? AND geneid = ?', array($_REQUEST['search_transcriptid'], $_REQUEST['id']))->fetchColumn())) {
                die(AJAX_NO_AUTH);
            }
            lovd_isAuthorized('gene', $nID); // Authorize for the gene currently loaded.

        } elseif ($sObjectID == 'VariantOnGenome,Scr2Var,VariantOnTranscript' && isset($_REQUEST['search_screeningid'])) {
            // CustomVL_VOT_for_I_VE has no ID but authorizes on search_screeningid (can contain multiple IDs).
            // CustomVL_VOT_for_S_VE has no ID but authorizes on search_screeningid.

            // When receiving multiple screenings, we intend of course to authorize on its individual.
            // We should not allow to add an "OR screeningid = ?" clause including an owned Screening to force authorization.
            // Check if we have multiple screening IDs. If so, make sure they belong together.
            $aScreeningIDs = explode('|', $_REQUEST['search_screeningid']);
            if (count($aScreeningIDs) > 1) {
                $nIndividuals = $_DB->query('SELECT COUNT(DISTINCT individualid) FROM ' . TABLE_SCREENINGS . ' WHERE id IN (?' . str_repeat(', ?', count($aScreeningIDs) - 1) . ')',
                    $aScreeningIDs)->fetchColumn();
                if ($nIndividuals > 1) {
                    // This custom VL is only loaded for authorization on Screenings, and there's no reason to have multiple Individuals.
                    die(AJAX_NO_AUTH);
                }
            }
            lovd_isAuthorized('screening', $aScreeningIDs); // Authorize for the Screening(s) currently searched (it restricts the view).
            // Since we're authorizing on $_REQUEST which also contains $_POST data, make sure the $_GET (what we actually filter on) matches what we authorize on!!!
            $_GET['search_screeningid'] = $_REQUEST['search_screeningid'];
Ivo Fokkema's avatar
Ivo Fokkema committed
151
        }
152 153 154
    }
}

155

156

157 158 159 160
// Require special clearance?
if ($nNeededLevel && (!$_AUTH || $_AUTH['level'] < $nNeededLevel)) {
    // If not authorized, die with error message.
    die(AJAX_NO_AUTH);
161 162
}

163 164 165 166 167 168 169
// Load the columns to skip from the request. The external viewer uses this.
$aColsToSkip = (!empty($_REQUEST['skip'])? $_REQUEST['skip'] : array());

// Submitters should not be allowed to retrieve more information
//  about users than the info the access sharing page gives them.
if ($sObject == 'User' && $_AUTH['level'] < LEVEL_MANAGER) {
    // Force removal of certain columns, regardless of this has been requested or not.
170 171 172 173 174 175 176 177 178 179 180 181
    // We cannot trust this was set in $_SESSION already since the VL can be loaded independently.
    $aColsToSkip = array_unique(
        array_merge(
            $aColsToSkip,
            array(
                'username',
                'status_',
                'last_login_',
                'created_date_',
                'curates',
                'level_'
            )));
182 183
}

184
// Managers, and sometimes curators, are allowed to download lists...
185
if (in_array(ACTION, array('download', 'downloadSelected'))) {
186 187 188 189 190 191 192
    if ($_AUTH['level'] >= LEVEL_CURATOR) {
        // We need this define() because the Object::viewList() may still throw some error which calls
        // Template::printHeader(), which would then thow a "text/plain not allowed here" error.
        define('FORMAT_ALLOW_TEXTPLAIN', true);
    }
}
if (FORMAT == 'text/plain' && !defined('FORMAT_ALLOW_TEXTPLAIN')) {
193
    die(AJAX_NO_AUTH);
194 195
}

196
$sFile = ROOT_PATH . 'class/object_' . strtolower($sObject) . 's.php';
197 198 199 200 201 202
// For revision tables.
$sFile = str_replace('_revs.php', 's.rev.php', $sFile);
// Exception for LOVD+.
if (LOVD_plus && substr($_GET['object'], -3) == 'MOD') {
    $sFile = str_replace('mods.', 's.mod.', $sFile);
}
203 204 205 206 207 208 209

if (!file_exists($sFile)) {
    header('HTTP/1.0 404 Not Found');
    exit;
}


210 211 212 213
require $sFile;
$sObjectClassname = 'LOVD_' . str_replace('_', '', $sObject);
$_DATA = new $sObjectClassname($sObjectID, $nID);

214 215


216
if (POST && ACTION == 'applyFR') {
Ivo Fokkema's avatar
Ivo Fokkema committed
217
    // Apply find & replace.
218 219 220 221 222 223

    if ($_AUTH['level'] < LEVEL_CURATOR || !isset($_POST['password']) ||
        !lovd_verifyPassword($_POST['password'], $_AUTH['password'])) {
        // Not authorized for find & replace.
        die(AJAX_NO_AUTH);
    }
224 225 226 227
    $aFROptions['sFRMatchType'] = (isset($_POST['FRMatchType_' . $sViewListID])?
        $_POST['FRMatchType_' . $sViewListID] : null);
    $aFROptions['bFRReplaceAll'] = (isset($_POST['FRReplaceAll_' . $sViewListID])?
        $_POST['FRReplaceAll_' . $sViewListID] : null);
228

229 230 231
    if (!isset($_POST['FRFieldname_' . $sViewListID]) ||
        !isset($_POST['FRSearch_' . $sViewListID]) ||
        !isset($_POST['FRReplace_' . $sViewListID])) {
232
        die(AJAX_DATA_ERROR);
233
    }
234 235 236

    // Setup search filters before applying find & replace.
    list($WHERE, $HAVING, $aArguments, $aBadSyntaxColumns, $aColTypes) = $_DATA->processViewListSearchArgs($_POST);
237

Ivo Fokkema's avatar
Ivo Fokkema committed
238
    // Update where/having clauses based on search filters (needed for LOVD_Object->buildSQL()).
239 240 241 242 243 244
    if ($WHERE) {
        $_DATA->aSQLViewList['WHERE'] .= ($_DATA->aSQLViewList['WHERE']? ' AND ' : '') . $WHERE;
    }
    if ($HAVING) {
        $_DATA->aSQLViewList['HAVING'] .= ($_DATA->aSQLViewList['HAVING']? ' AND ' : '') . $HAVING;
    }
245
    $aArgs = array_merge($aArguments['WHERE'], $aArguments['HAVING']);
Ivo Fokkema's avatar
Ivo Fokkema committed
246
    $bResult = $_DATA->applyColumnFindAndReplace($_POST['FRFieldname_' . $sViewListID],
247 248 249 250
                                                $_POST['FRSearch_' . $sViewListID],
                                                $_POST['FRReplace_' . $sViewListID],
                                                $aArgs, $aFROptions);
    // Return AJAX response.
Ivo Fokkema's avatar
Ivo Fokkema committed
251 252
    die($bResult? AJAX_TRUE : AJAX_FALSE);

253 254 255 256
} elseif (POST) {
    // Post request with no action specified. We do not allow normal viewlist
    // views via POST.
    die(AJAX_DATA_ERROR);
257
}
258

259 260 261 262 263
// Restrict the columns of this VL, if given.
if (LOVD_plus && isset($_INSTANCE_CONFIG['viewlists'][$_GET['viewlistid']]['cols_to_show'])) {
    $_DATA->setViewListCols($_INSTANCE_CONFIG['viewlists'][$_GET['viewlistid']]['cols_to_show']);
}

264
// Show the viewlist.
265 266 267 268 269 270
// Parameters could be assumed to be in $_SESSION. However, certain options we send through here.
// only_rows is checked and sent here because of the logs retrieving single rows from the next page.
// cols_to_skip can be overridden for the external viewer.
$aOptions = array(
    'only_rows' => (!empty($_GET['only_rows'])),
);
271
if ($aColsToSkip) {
272 273 274 275 276 277
    // Don't let the requested list of columns overwrite the original one. Only additional columns may be hidden.
    $aOptions['cols_to_skip'] = array_unique(array_merge(
        (!isset($_SESSION['viewlists'][$_GET['viewlistid']]['options']['cols_to_skip'])? array()
            : $_SESSION['viewlists'][$_GET['viewlistid']]['options']['cols_to_skip']),
        $aColsToSkip
    ));
278 279
}
$_DATA->viewList($_GET['viewlistid'], $aOptions);
280
?>