/**
 * Generate a list of all projects available on this server.
 */
function project_list_generate()
{
    $api_vid = _project_release_get_api_vid();
    $query = db_query("SELECT n.title, n.nid, n.vid, n.status, p.uri, u.name AS username FROM {node} n INNER JOIN {project_projects} p ON n.nid = p.nid INNER JOIN {users} u ON n.uid = u.uid");
    $xml = '';
    while ($project = db_fetch_object($query)) {
        $xml .= " <project>\n";
        $xml .= '  <title>' . check_plain($project->title) . "</title>\n";
        $xml .= '  <short_name>' . check_plain($project->uri) . "</short_name>\n";
        $xml .= '  <link>' . prch_url("node/{$project->nid}") . "</link>\n";
        $xml .= '  <dc:creator>' . check_plain($project->username) . "</dc:creator>\n";
        $term_query = db_query("SELECT v.name AS vocab_name, v.vid, td.name AS term_name, td.tid FROM {term_node} tn INNER JOIN {term_data} td ON tn.tid = td.tid INNER JOIN {vocabulary} v ON td.vid = v.vid WHERE tn.vid = %d", $project->vid);
        $xml_terms = '';
        while ($term = db_fetch_object($term_query)) {
            $xml_terms .= '   <term><name>' . check_plain($term->vocab_name) . '</name>';
            $xml_terms .= '<value>' . check_plain($term->term_name) . "</value></term>\n";
        }
        if (!empty($xml_terms)) {
            $xml .= "  <terms>\n" . $xml_terms . "  </terms>\n";
        }
        if (!$project->status) {
            // If it's not published, we can skip the rest for this project.
            $xml .= "  <project_status>unpublished</project_status>\n";
        } else {
            $xml .= "  <project_status>published</project_status>\n";
            // Include a list of API terms if available.
            $term_query = db_query("SELECT DISTINCT(td.tid), td.name AS term_name FROM {project_release_nodes} prn INNER JOIN {term_data} td ON prn.version_api_tid = td.tid WHERE prn.pid = %d AND td.vid = %d ORDER BY td.weight ASC", $project->nid, $api_vid);
            $xml_api_terms = '';
            while ($api_term = db_fetch_object($term_query)) {
                $xml_api_terms .= '   <api_version>' . check_plain($api_term->term_name) . "</api_version>\n";
            }
            if (!empty($xml_api_terms)) {
                $xml .= "  <api_versions>\n" . $xml_api_terms . "  </api_versions>\n";
            }
        }
        $xml .= " </project>\n";
    }
    if (empty($xml)) {
        wd_err(array('message' => 'No projects found on this server.'));
        return FALSE;
    }
    project_release_history_write_xml($xml);
}
function package_releases($type, $project_id)
{
    global $wd_err_msg;
    $rel_node_join = '';
    $where_args = array();
    if ($type == 'tag') {
        $where = " AND (label.type = %d) AND (f.filepath IS NULL OR f.filepath = '')";
        $where_args[] = VERSIONCONTROL_OPERATION_TAG;
        // label.type
        $plural = t('tags');
    } elseif ($type == 'branch') {
        $rel_node_join = " INNER JOIN {node} nr ON prn.nid = nr.nid";
        $where = " AND (label.type = %d) AND ((f.filepath IS NULL) OR (f.filepath = '') OR (nr.status = 1))";
        $where_args[] = VERSIONCONTROL_OPERATION_BRANCH;
        // label.type
        $plural = t('branches');
        if (empty($project_id)) {
            wd_msg("Starting to package all snapshot releases.");
        } else {
            wd_msg("Starting to package snapshot releases for project id: @project_id.", array('@project_id' => $project_id), l(t('view'), 'node/' . $project_id));
        }
    } else {
        wd_err("ERROR: package_releases() called with unknown type: %type", array('%type' => $type));
        return;
    }
    $args = array();
    $args[] = (int) _project_release_get_api_vid();
    if (!empty($project_id)) {
        $where .= ' AND prn.pid = %d';
        $where_args[] = $project_id;
    }
    $args = array_merge($args, $where_args);
    $result = db_query("\n    SELECT pp.uri, prn.nid AS release_nid, prn.pid AS project_nid, prn.version,\n      prn.version_major, td.tid, vpp.directory, vpp.repo_id,\n      label.label_id, label.name AS label_name, label.type AS label_type\n    FROM {project_release_nodes} prn\n      {$rel_node_join}\n      LEFT JOIN {project_release_file} prf ON prn.nid = prf.nid\n      LEFT JOIN {files} f ON prf.fid = f.fid\n      INNER JOIN {term_node} tn ON prn.nid = tn.nid\n      INNER JOIN {term_data} td ON tn.tid = td.tid\n      INNER JOIN {project_projects} pp ON prn.pid = pp.nid\n      INNER JOIN {node} np ON prn.pid = np.nid\n      INNER JOIN {project_release_projects} prp ON prp.nid = prn.pid\n      INNER JOIN {versioncontrol_project_projects} vpp ON prn.pid = vpp.nid\n      INNER JOIN {versioncontrol_release_labels} vrl ON prn.nid = vrl.release_nid\n      INNER JOIN {versioncontrol_labels} label ON vrl.label_id = label.label_id\n    WHERE np.status = 1 AND prp.releases = 1 AND td.vid = %d\n      {$where}\n    ORDER BY pp.uri", $args);
    $num_built = 0;
    $num_considered = 0;
    $project_nids = array();
    // Determine the list of sufficient VCS backends, in order to avoid errors
    // in edge cases. (In the common case, unsupported backends don't generate
    // releases with VCS integration, but who knows what might happen.)
    $backends = versioncontrol_get_backends();
    $supported_backends = array();
    foreach ($backends as $vcs => $backend) {
        if (versioncontrol_release_is_supported_backend($vcs)) {
            $supported_backends[] = $vcs;
        }
    }
    // Read everything out of the query immediately so that we don't leave the
    // query object/connection open while doing other queries.
    $releases = array();
    while ($release = db_fetch_object($result)) {
        $releases[] = $release;
    }
    foreach ($releases as $release) {
        // Fetch the repository where the project is located.
        $repositories = versioncontrol_get_repositories(array('repo_ids' => array($release->repo_id), 'vcs' => $supported_backends));
        if (empty($repositories)) {
            $num_considered++;
            continue;
        }
        $repository = reset($repositories);
        // first item
        $wd_err_msg = array();
        $version = $release->version;
        $release_nid = $release->release_nid;
        $tid = $release->tid;
        $project = array('uri' => $release->uri, 'nid' => $release->project_nid, 'repo_id' => $release->directory, 'directory' => $release->directory);
        $label = array('label_id' => $release->label_id, 'name' => $release->label_name, 'type' => $release->label_type);
        $major = $release->version_major;
        $version = escapeshellcmd($version);
        db_query("DELETE FROM {project_release_package_errors} WHERE nid = %d", $release_nid);
        $built = package_release($release_nid, $project, $repository, $version, $label);
        if ($built) {
            $num_built++;
            $project_nids[$project['nid']][$tid][$major] = TRUE;
        }
        $num_considered++;
        if (count($wd_err_msg)) {
            db_query("INSERT INTO {project_release_package_errors} (nid, messages) values (%d, '%s')", $release_nid, serialize($wd_err_msg));
        }
    }
    if (!empty($num_built) || $type == 'branch') {
        if (!empty($project_id)) {
            wd_msg("Done packaging releases for @uri from !plural: !num_built built, !num_considered considered.", array('@uri' => $uri, '!plural' => $plural, '!num_built' => $num_built, '!num_considered' => $num_considered));
        } else {
            wd_msg("Done packaging releases from !plural: !num_built built, !num_considered considered.", array('!plural' => $plural, '!num_built' => $num_built, '!num_considered' => $num_considered));
        }
    }
    // Finally, for each project/tid/major triple we packaged, check to see if
    // the supported/recommended settings are sane now that new tarballs have
    // been generated and release nodes published.
    foreach ($project_nids as $pid => $tids) {
        foreach ($tids as $tid => $majors) {
            foreach ($majors as $major => $value) {
                project_release_check_supported_versions($pid, $tid, $major, FALSE);
            }
        }
    }
}