/*------------------------------------------------------------------------- * Copyright (c) 2000-2003 Kenneth W. Sodemann (stuffle@mac.com) *------------------------------------------------------------------------- * prj_rpts_html * * Synopsis: * Routines used to create HTML based project reports. * * $Id: prj_rpts_html.c,v 1.1 2003/03/09 20:59:11 stuffle Exp $ * * This program 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. * * This program 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 this program; if not, write to * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * *------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include "audit_trail.h" #include "error_chks.h" #include "gstr_utils.h" #include "prq_utils.h" #define HTML_HEAD "\n\n%s\n\n\n" #define BODY_DEF "\n\n" #define LL_ITEM "
  • %s: %s\n" /* * The navagation table that appears at the top and bottom of most of * the pages. */ #define NAV_TABLE "\ \n\ \n\ \n\ \n\ \n\ \n
    \n [%s]\n \n [%s]\n \n [%s]\n
    \n" /* * Lines used to display the general information on the problem report * detail pages. */ #define INFO_LINE1 "

    %s: %s\n" #define INFO_LINE2 "
    %s: %s\n" #define HEAD1 "

    %s

    \n" #define HEAD2 "

    %s

    \n" #define LINK "%s" #define HTML_TAIL "\n" /* * Errors that can be generated by the functions. */ #define SUCCESS 0 #define FILE_OPEN_ERROR -1 #define SQL_ERROR -2 /* * Common static text */ #define NONE "" /* * SQL Statements */ #define PK_POS 0 #define NAME_POS 1 #define DESCR_POS 2 #define LEADER_POS 3 #define LEADER_E_MAIL_POS 4 #define SUB_POS 3 #define SUB_E_MAIL_POS 4 #define RESP_POS 5 #define RESP_E_MAIL_POS 6 #define TYPE_POS 7 #define SEVERITY_POS 8 #define STATUS_POS 9 #define FIX_POS 10 #define PROJECT_VERSION_LIST "\ SELECT version_num, version_text, descr \ FROM project_version \ WHERE project_num = %d " #define PROJECT_VER_ACTIVE "AND active = TRUE " #define PROJECT_VER_ORDER_BY "ORDER BY order_num " #define PROJECT_LIST "\ SELECT prj.project_num, prj.name, prj.descr, \ p.first_name || ' ' || p.last_name, \ p.e_mail \ FROM project prj, person p \ WHERE p.login_id = prj.login_id " #define PROJECT_ACTIVE "AND prj.active = TRUE " #define PROJECT_IN "AND prj.project_num in (" #define PROJECT_ORDER " ORDER BY prj.name, prj.project_num " #define PROBLEM_LIST_BY_STATUS "\ SELECT pr.problem_num, pr.title, pr.descr, \ p1.first_name || ' ' || p1.last_name, p1.e_mail, \ p2.first_name || ' ' || p2.last_name, p2.e_mail, \ pt.name, sevr.name, stat.name, pr.fix, stat.order_num \ FROM problem_report pr, person p1, person p2, problem_type pt, \ severity sevr, status stat \ WHERE p1.login_id = pr.submitter_id \ AND p2.login_id = pr.responsible_id \ AND pt.type_num = pr.type_num \ AND sevr.severity_num = pr.severity_num \ AND stat.status_num = pr.status_num \ AND pr.project_num = %d \ UNION \ SELECT pr.problem_num, pr.title, pr.descr, \ p1.first_name || ' ' || p1.last_name, p1.e_mail, \ NULL, NULL, \ pt.name, sevr.name, stat.name, pr.fix, stat.order_num \ FROM problem_report pr, person p1, problem_type pt, \ severity sevr, status stat \ WHERE p1.login_id = pr.submitter_id \ AND pr.responsible_id is NULL \ AND pt.type_num = pr.type_num \ AND sevr.severity_num = pr.severity_num \ AND stat.status_num = pr.status_num \ AND pr.project_num = %d \ ORDER BY 12, 1 " #define PROBLEM_LIST_BY_OPEN_VERSION "\ SELECT pr.problem_num, pr.title, pr.descr, \ p1.first_name || ' ' || p1.last_name, p1.e_mail, \ p2.first_name || ' ' || p2.last_name, p2.e_mail, \ pt.name, sevr.name, stat.name, pr.fix \ FROM problem_report pr, person p1, person p2, problem_type pt, \ severity sevr, status stat \ WHERE p1.login_id = pr.submitter_id \ AND p2.login_id = pr.responsible_id \ AND pt.type_num = pr.type_num \ AND sevr.severity_num = pr.severity_num \ AND stat.status_num = pr.status_num \ AND pr.project_num = %d \ AND pr.open_version = %d \ UNION \ SELECT pr.problem_num, pr.title, pr.descr, \ p1.first_name || ' ' || p1.last_name, p1.e_mail, \ NULL, NULL, \ pt.name, sevr.name, stat.name, pr.fix \ FROM problem_report pr, person p1, problem_type pt, \ severity sevr, status stat \ WHERE p1.login_id = pr.submitter_id \ AND pr.responsible_id is NULL \ AND pt.type_num = pr.type_num \ AND sevr.severity_num = pr.severity_num \ AND stat.status_num = pr.status_num \ AND pr.project_num = %d \ AND pr.open_version = %d \ ORDER BY 1" #define PROBLEM_LIST_BY_CLOSE_VERSION "\ SELECT pr.problem_num, pr.title, pr.descr, \ p1.first_name || ' ' || p1.last_name, p1.e_mail, \ p2.first_name || ' ' || p2.last_name, p2.e_mail, \ pt.name, sevr.name, stat.name, pr.fix \ FROM problem_report pr, person p1, person p2, problem_type pt, \ severity sevr, status stat \ WHERE p1.login_id = pr.submitter_id \ AND p2.login_id = pr.responsible_id \ AND pt.type_num = pr.type_num \ AND sevr.severity_num = pr.severity_num \ AND stat.status_num = pr.status_num \ AND pr.project_num = %d \ AND pr.close_version = %d \ UNION \ SELECT pr.problem_num, pr.title, pr.descr, \ p1.first_name || ' ' || p1.last_name, p1.e_mail, \ NULL, NULL, \ pt.name, sevr.name, stat.name, pr.fix \ FROM problem_report pr, person p1, problem_type pt, \ severity sevr, status stat \ WHERE p1.login_id = pr.submitter_id \ AND pr.responsible_id is NULL \ AND pt.type_num = pr.type_num \ AND sevr.severity_num = pr.severity_num \ AND stat.status_num = pr.status_num \ AND pr.project_num = %d \ AND pr.close_version = %d \ ORDER BY 1" #define PROBLEM_LIST_NO_VERSION "\ SELECT pr.problem_num, pr.title, pr.descr, \ p1.first_name || ' ' || p1.last_name, p1.e_mail, \ p2.first_name || ' ' || p2.last_name, p2.e_mail, \ pt.name, sevr.name, stat.name, pr.fix \ FROM problem_report pr, person p1, person p2, problem_type pt, \ severity sevr, status stat \ WHERE p1.login_id = pr.submitter_id \ AND p2.login_id = pr.responsible_id \ AND pt.type_num = pr.type_num \ AND sevr.severity_num = pr.severity_num \ AND stat.status_num = pr.status_num \ AND pr.project_num = %d \ AND pr.close_version is NULL \ AND pr.open_version is NULL \ UNION \ SELECT pr.problem_num, pr.title, pr.descr, \ p1.first_name || ' ' || p1.last_name, p1.e_mail, \ NULL, NULL, \ pt.name, sevr.name, stat.name, pr.fix \ FROM problem_report pr, person p1, problem_type pt, \ severity sevr, status stat \ WHERE p1.login_id = pr.submitter_id \ AND pr.responsible_id is NULL \ AND pt.type_num = pr.type_num \ AND sevr.severity_num = pr.severity_num \ AND stat.status_num = pr.status_num \ AND pr.project_num = %d \ AND pr.close_version is NULL \ AND pr.open_version is NULL \ ORDER BY 1" /* * project print information. * * This structure contains the information that will be printed for * each project. */ typedef struct project { gint prj_num; /* pk_num of project */ gchar *name; /* name of the project */ gchar *descr; /* description of the project */ gchar *leader; /* full name of the project leader */ gchar *leader_e_mail; /* e-mail address of project leader, NULL if not available. */ } project; /* * problem print information * * This structure contains the information that will be printed for * each problem report. */ typedef struct problem { gint pr_num; /* pk_num of the problem report */ gchar *title; /* title of the problem report */ project *prj; /* pointer to project associated with PR */ gchar *submitter; /* full name of the PR submitter */ gchar *submitter_e_mail; /* e-mail address of PR submitter, NULL if not available */ gchar *responsible; /* full name of the person responsible for the PR */ gchar *responsible_e_mail; /* e-mail address of the person reponsible for the PR, NULL if not available */ gchar *prob_type; /* pr type code */ gchar *severity; /* severity code */ gchar *status; /* status code */ gchar *descr; /* description of the problem, NULL if not available */ gchar *fix_descr; /* fix description of the problem, NULL if not available */ } problem; static void destroy_project (project *prj) { g_assert (prj != NULL); g_free (prj->name); g_free (prj->descr); g_free (prj->leader); g_free (prj->leader_e_mail); g_free (prj); } /* * The procedure DOES NOT destroy the project pointed to by pr->prj. * Several PR's may point to the same project, so it would not be * clear who would own the project. That will need to be destroyed * explicetly by the owner. */ static void destroy_problem (problem *pr) { g_assert (pr != NULL); g_free (pr->title); g_free (pr->submitter); g_free (pr->submitter_e_mail); g_free (pr->responsible); g_free (pr->responsible_e_mail); g_free (pr->prob_type); g_free (pr->severity); g_free (pr->status); g_free (pr->descr); g_free (pr->fix_descr); g_free (pr); } static gint create_pr_page (PGconn *conn, gchar *path, problem *pr, gint prev_pr_num, gint next_pr_num) { FILE *fp; GString *buffer; GString *url; GString *prev; GString *pr_list; GString *next; GString *nav_table; PGresult *res; gint i, n; buffer = g_string_new (""); /* * Attempt to open the output file for writing. */ g_string_sprintf (buffer, "%s/pr%d.html", path, pr->pr_num); fp = fopen (buffer->str, "w"); if (!fp) { syslog (LOG_ERR, "Failed to open: %s: %m", buffer->str); g_string_free (buffer, TRUE); return FILE_OPEN_ERROR; } /* * Head and start of body */ g_string_sprintf (buffer, "PR #%d - %s", pr->pr_num, pr->title); fprintf (fp, HTML_HEAD, buffer->str); fprintf (fp, BODY_DEF); /* * The nav table appears at the top and at the bottom of the body. * Therefore, create it but don't destroy it until after it is * written at the bottom. */ prev = g_string_new ("Prev"); if (prev_pr_num > 0) { g_string_sprintf (buffer, "pr%d.html", prev_pr_num); g_string_sprintf (prev, LINK, buffer->str, "Prev"); } pr_list = g_string_new (""); g_string_sprintf (buffer, "proj%d.html", pr->prj->prj_num); g_string_sprintf (pr_list, LINK, buffer->str, "PR List"); next = g_string_new ("Next"); if (next_pr_num > 0) { g_string_sprintf (buffer, "pr%d.html", next_pr_num); g_string_sprintf (next, LINK, buffer->str, "Next"); } nav_table = g_string_new (""); g_string_sprintf (nav_table, NAV_TABLE, prev->str, pr_list->str, next->str); g_string_free (prev, TRUE); g_string_free (pr_list, TRUE); g_string_free (next, TRUE); fprintf (fp, nav_table->str); /* * Top nav table done, start the details. Note that we may need to * format more than one URL, so just allocate the string up front. */ url = g_string_new (""); fprintf (fp, "
    \n\n"); g_string_sprintf (buffer, "PR #%d: %s", pr->pr_num, pr->title); fprintf (fp, HEAD1, buffer->str); fprintf (fp, HEAD2, "General Information"); fprintf (fp, "
    \n"); fprintf (fp, INFO_LINE1, "Project", pr->prj->name); if (pr->submitter_e_mail != NULL) { g_string_sprintf (url, "mailto:%s", pr->submitter_e_mail); g_string_sprintf (buffer, LINK, url->str, pr->submitter); } else { buffer = g_string_assign (buffer, pr->submitter); } fprintf (fp, INFO_LINE2, "Submitter", buffer->str); if (pr->responsible_e_mail != NULL) { g_string_sprintf (url, "mailto:%s", pr->responsible_e_mail); g_string_sprintf (buffer, LINK, url->str, pr->responsible); } else { if (pr->responsible != NULL) { buffer = g_string_assign (buffer, pr->responsible); } else { buffer = g_string_assign (buffer, NONE); } g_string_prepare_html (buffer); } fprintf (fp, INFO_LINE2, "Assigned To", buffer->str); fprintf (fp, INFO_LINE2, "Problem Type", pr->prob_type); fprintf (fp, INFO_LINE2, "Problem Severity", pr->severity); fprintf (fp, INFO_LINE2, "Status", pr->status); fprintf (fp, "
    \n\n"); fprintf (fp, HEAD2, "Description"); if (pr->descr != NULL) { buffer = g_string_assign (buffer, pr->descr); } else { buffer = g_string_assign (buffer, NONE); } g_string_prepare_html (buffer); fprintf (fp, "

    %s\n\n", buffer->str); fprintf (fp, HEAD2, "Fix Description"); if (pr->fix_descr != NULL) { buffer = g_string_assign (buffer, pr->fix_descr); } else { buffer = g_string_assign (buffer, NONE); } g_string_prepare_html (buffer); fprintf (fp, "

    %s\n\n", buffer->str); /* * Audit Trail */ fprintf (fp, HEAD2, "Audit Trail"); g_string_sprintf (buffer, PR_SUBMIT_DATE_QUERY, pr->pr_num); res = PQexec (conn, buffer->str); if (chk_sql_error (res, _("get submit date"))) { fprintf (fp, "%s\n", PQgetvalue (res, 0, 0)); } PQclear (res); g_string_sprintf (buffer, SELECT_AUDIT_TRAIL, pr->pr_num, pr->pr_num); res = PQexec (conn, buffer->str); if (chk_sql_error (res, _("get audit trail"))) { n = PQntuples (res); for (i = 0; i < n; i++) { fprintf (fp, "
    %s\n", PQgetvalue (res, 0, 0)); } } PQclear (res); /* * Done with the main details. output the bottom nav table. */ fprintf (fp, "


    \n%s\n%s", nav_table->str, HTML_TAIL); /* * Clean up... */ g_string_free (buffer, TRUE); g_string_free (nav_table, TRUE); g_string_free (url, TRUE); fclose (fp); return SUCCESS; } /* * Given a list of project PK's (or NULL for all projects), create a list * of projects. */ static GList * create_project_list (PGconn *conn, GList *prj_pks, gboolean active_only) { GList *list_iter; GList *list = NULL; GString *sql_buffer; GString *num_buf; PGresult *res; project *prj; gint i, n; sql_buffer = g_string_new (PROJECT_LIST); if (active_only) { sql_buffer = g_string_append (sql_buffer, PROJECT_ACTIVE); } if (prj_pks != NULL) { sql_buffer = g_string_append (sql_buffer, PROJECT_IN); num_buf = g_string_new (""); for (list_iter = g_list_first (prj_pks); list_iter != NULL; list_iter = list_iter->next) { g_string_sprintf (num_buf, "%d,", GPOINTER_TO_INT (list_iter->data)); sql_buffer = g_string_append (sql_buffer, num_buf->str); } sql_buffer->str[sql_buffer->len - 1] = ')'; g_string_free (num_buf, TRUE); } sql_buffer = g_string_append (sql_buffer, PROJECT_ORDER); res = PQexec (conn, sql_buffer->str); if (chk_sql_error (res, _("get projects"))) { n = PQntuples (res); for (i = 0; i < n; i++) { prj = g_malloc (sizeof (project)); prj->prj_num = atoi (PQgetvalue (res, i, PK_POS)); prj->name = g_malloc (strlen (PQgetvalue (res, i, NAME_POS)) + 1); strcpy (prj->name, PQgetvalue (res, i, NAME_POS)); prj->descr = g_malloc (strlen (PQgetvalue (res, i, DESCR_POS)) + 1); strcpy (prj->descr, PQgetvalue (res, i, DESCR_POS)); prj->leader = g_malloc (strlen (PQgetvalue (res, i, LEADER_POS)) + 1); strcpy (prj->leader, PQgetvalue (res, i, LEADER_POS)); if (PQgetisnull (res, i, LEADER_E_MAIL_POS)) { prj->leader_e_mail = NULL; } else { prj->leader_e_mail = g_malloc (strlen (PQgetvalue (res, i, LEADER_E_MAIL_POS)) + 1); strcpy (prj->leader_e_mail, PQgetvalue (res, i, LEADER_E_MAIL_POS)); } list = g_list_append (list, prj); } } g_string_free (sql_buffer, TRUE); PQclear (res); return list; } static gint create_project_list_page (gchar *path, gchar *rpt_title, GList *prj_list) { FILE *fp; GString *buffer; GList *list_iter; project *prj; time_t timestamp; buffer = g_string_new (""); /* * Attempt to open the output file for writing. This is the index of * the report. */ g_string_sprintf (buffer, "%s/index.html", path); fp = fopen (buffer->str, "w"); if (!fp) { syslog (LOG_ERR, "Failed to open: %s: %m", buffer->str); g_string_free (buffer, TRUE); return FILE_OPEN_ERROR; } /* * Head and start of body. */ fprintf (fp, HTML_HEAD, rpt_title); fprintf (fp, BODY_DEF); fprintf (fp, "
    \n"); fprintf (fp, HEAD1, rpt_title); fprintf (fp, "
    \n\n"); fprintf (fp, "
    \n\n"); fprintf (fp, HEAD2, "Project List"); fprintf (fp, "
      \n"); for (list_iter = g_list_first (prj_list); list_iter != NULL; list_iter = list_iter->next) { prj = list_iter->data; g_string_sprintf (buffer, "proj%d.html", prj->prj_num); fprintf (fp, LL_ITEM, buffer->str, prj->name, prj->descr); } fprintf (fp, "
    \n\n
    \n"); timestamp = time (NULL); fprintf (fp, "Generated On: %s\n", ctime (×tamp)); fprintf (fp, HTML_TAIL); fclose (fp); g_string_free (buffer, TRUE); return SUCCESS; } static problem * construct_problem_object (PGresult *res, gint i, project *prj) { problem *pr; pr = g_malloc (sizeof (problem)); pr->pr_num = atoi (PQgetvalue (res, i, PK_POS)); pr->title = g_malloc (strlen (PQgetvalue (res, i, NAME_POS)) + 1); strcpy (pr->title, PQgetvalue (res, i, NAME_POS)); pr->prj = prj; pr->submitter = g_malloc (strlen (PQgetvalue (res, i, SUB_POS)) + 1); strcpy (pr->submitter, PQgetvalue (res, i, SUB_POS)); if (PQgetisnull (res, i, SUB_E_MAIL_POS)) { pr->submitter_e_mail = NULL; } else { pr->submitter_e_mail = g_malloc (strlen (PQgetvalue (res, i, SUB_E_MAIL_POS)) + 1); strcpy (pr->submitter_e_mail, PQgetvalue (res, i, SUB_E_MAIL_POS)); } if (PQgetisnull (res, i, RESP_POS)) { pr->responsible = NULL; pr->responsible_e_mail = NULL; } else { pr->responsible = g_malloc (strlen (PQgetvalue (res, i, RESP_POS)) + 1); strcpy (pr->responsible, PQgetvalue (res, i, RESP_POS)); if (PQgetisnull (res, i, RESP_E_MAIL_POS)) { pr->responsible_e_mail = NULL; } else { pr->responsible_e_mail = g_malloc (strlen (PQgetvalue (res, i, RESP_E_MAIL_POS)) + 1); strcpy (pr->responsible_e_mail, PQgetvalue (res, i, RESP_E_MAIL_POS)); } } pr->prob_type = g_malloc (strlen (PQgetvalue (res, i, TYPE_POS)) + 1); strcpy (pr->prob_type, PQgetvalue (res, i, TYPE_POS)); pr->severity = g_malloc (strlen (PQgetvalue (res, i, SEVERITY_POS)) + 1); strcpy (pr->severity, PQgetvalue (res, i, SEVERITY_POS)); pr->status = g_malloc (strlen (PQgetvalue (res, i, STATUS_POS)) + 1); strcpy (pr->status, PQgetvalue (res, i, STATUS_POS)); pr->descr = g_malloc (strlen (PQgetvalue (res, i, DESCR_POS)) + 1); strcpy (pr->descr, PQgetvalue (res, i, DESCR_POS)); if (PQgetisnull (res, i, FIX_POS)) { pr->fix_descr = NULL; } else { pr->fix_descr = g_malloc (strlen (PQgetvalue (res, i, FIX_POS)) + 1); strcpy (pr->fix_descr, PQgetvalue (res, i, FIX_POS)); } return pr; } static GList * create_problem_by_status_list (PGconn *conn, project *prj) { GList *list = NULL; GString *sql_buffer; PGresult *res; gint i, n; problem *pr; sql_buffer = g_string_new (""); g_string_sprintf (sql_buffer, PROBLEM_LIST_BY_STATUS, prj->prj_num, prj->prj_num); res = PQexec (conn, sql_buffer->str); if (chk_sql_error (res, _("get problem list by status"))) { n = PQntuples (res); for (i = 0; i < n; i++) { pr = construct_problem_object (res, i, prj); list = g_list_append (list, pr); } /* end for loop */ } /* end if valid */ g_string_free (sql_buffer, TRUE); PQclear (res); return list; } static gint create_project_status_page (PGconn *conn, gchar *path, project *prj, gint prev_prj_num, gint next_prj_num) { FILE *fp; GString *buffer; GString *url; GString *prev_str; GString *prj_list_str; GString *next_str; GString *nav_table; GString *curr_stat; GList *pr_list; GList *list_iter; gint prev_pr; gint next_pr; problem *pr; gint rtn = SUCCESS; buffer = g_string_new (""); /* * Attempt to open the output file for writting. */ g_string_sprintf (buffer, "%s/proj%d.html", path, prj->prj_num); fp = fopen (buffer->str, "w"); if (!fp) { syslog (LOG_ERR, "Failed to open: %s: %m", buffer->str); g_string_free (buffer, TRUE); return FILE_OPEN_ERROR; } /* * Head and start of body */ g_string_sprintf (buffer, "%s - Status Report", prj->name); fprintf (fp, HTML_HEAD, buffer->str); fprintf (fp, BODY_DEF); /* * The nav table appears at the top and at the botton of the body. * Therefore, create it, but don't destroy it until after it is * written at the bottom. */ prev_str = g_string_new ("Prev"); if (prev_prj_num > 0) { g_string_sprintf (buffer, "proj%d.html", prev_prj_num); g_string_sprintf (prev_str, LINK, buffer->str, "Prev"); } prj_list_str = g_string_new (""); g_string_sprintf (prj_list_str, LINK, "index.html", "Project List"); next_str = g_string_new ("Next"); if (next_prj_num > 0) { g_string_sprintf (buffer, "proj%d.html", next_prj_num); g_string_sprintf (next_str, LINK, buffer->str, "Next"); } nav_table = g_string_new (""); g_string_sprintf (nav_table, NAV_TABLE, prev_str->str, prj_list_str->str, next_str->str); g_string_free (prev_str, TRUE); g_string_free (prj_list_str, TRUE); g_string_free (next_str, TRUE); fprintf (fp, nav_table->str); /* * Top nav table done, start the deatils. Note that we may need to * format more than one URL, so just allocate the string up front. */ url = g_string_new (""); fprintf (fp, "
    \n\n"); fprintf (fp, HEAD1, prj->name); buffer = g_string_assign (buffer, prj->descr); g_string_prepare_html (buffer); fprintf (fp, "

    \nDescription:
    %s\n", buffer->str); if (prj->leader_e_mail != NULL) { g_string_sprintf (url, "mailto:%s", prj->leader_e_mail); g_string_sprintf (buffer, LINK, url->str, prj->leader); } else { buffer = g_string_assign (buffer, prj->leader); } fprintf (fp, "

    \nProject Leader: %s\n\n", buffer->str); pr_list = create_problem_by_status_list (conn, prj); curr_stat = g_string_new (""); prev_pr = -1; for (list_iter = g_list_first (pr_list); list_iter != NULL; list_iter = list_iter->next) { if (list_iter->next != NULL) { pr = list_iter->next->data; next_pr = pr->pr_num; } else { next_pr = -1; } pr = list_iter->data; /* * If this status is not the same as the last status, create a new * header for it. If the curr_stat->len is zero, we are at the * first status, and thus should not close off the previous

      , * since there isn't one... */ if (strcmp (curr_stat->str, pr->status) != 0) { if (curr_stat->len != 0) { fprintf (fp, "
    \n\n"); } fprintf (fp, HEAD2, pr->status); fprintf (fp, "
      \n"); curr_stat = g_string_assign (curr_stat, pr->status); } /* * draw the detail page for the current problem report. * if an error occurs, stop at this point. This will * allow us to finish the page and clean up. * * save the return value so we can pass it back to the * caller */ rtn = create_pr_page (conn, path, pr, prev_pr, next_pr); if (rtn != SUCCESS) { break; } fprintf (fp, "
    • "); g_string_sprintf (url, "pr%d.html", pr->pr_num); g_string_sprintf (buffer, "PR #%d", pr->pr_num); fprintf (fp, LINK, url->str, buffer->str); buffer = g_string_assign (buffer, pr->title); g_string_prepare_html (buffer); fprintf (fp, ": %s\n", buffer->str); prev_pr = pr->pr_num; } /* end for loop */ /* * If we had a pr_list, we had at least one pr, and ended the for * loop with an open
        , close it. If the pr_list was NULL, we * had no PR's for this project, output that to the user. */ if (pr_list) { fprintf (fp, "
      \n\n"); } else { fprintf (fp, "

      There are no problem reports for this project.\n\n"); } /* * output the final nav bar, and finish the page. */ fprintf (fp, "


      %s\n%s", nav_table->str, HTML_TAIL); /* * clean up */ fclose (fp); g_string_free (url, TRUE); g_string_free (buffer, TRUE); g_string_free (curr_stat, TRUE); g_string_free (nav_table, TRUE); for (list_iter = g_list_first (pr_list); list_iter != NULL; list_iter = list_iter->next) { destroy_problem (list_iter->data); } g_list_free (pr_list); return rtn; } gint create_project_status_report (PGconn *conn, gchar *path, GList *prj_pk_list, gboolean active_only) { GList *prj_list; GList *list_iter; project *prj; gint prev_prj; gint next_prj; gint rtn = SUCCESS; prj_list = create_project_list (conn, prj_pk_list, active_only); if (prj_list) { create_project_list_page (path, "Problem Reports by Project and Status", prj_list); prev_prj = -1; for (list_iter = g_list_first (prj_list); list_iter != NULL; list_iter = list_iter->next) { if (list_iter->next) { prj = list_iter->next->data; next_prj = prj->prj_num; } else { next_prj = -1; } prj = list_iter->data; /* * If we fail to create a project status page, or one * of its child pages, break and return the last error * generated. */ rtn = create_project_status_page (conn, path, prj, prev_prj, next_prj); if (rtn != SUCCESS) { break; } prev_prj = prj->prj_num; } } for (list_iter = g_list_first (prj_list); list_iter != NULL; list_iter = list_iter->next) { destroy_project (list_iter->data); } g_list_free (prj_list); return rtn; } /* * sort by pr_num for sorted insert into pr_list */ static gint compare_problems (const void *p1, const void *p2) { return (((problem *)p1)->pr_num - ((problem *)p2)->pr_num); } static problem * add_to_pr_list (PGresult *res, gint i, project *prj, GList **list) { problem *pr; GList *list_iter; gint pk; pk = atoi (PQgetvalue (res, i, PK_POS)); g_assert (pk > 0); for (list_iter = g_list_first (*list); list_iter != NULL; list_iter = list_iter->next) { pr = list_iter->data; if (pk == pr->pr_num) { return pr; } } /* * If we are here, we did not find it. Create a new project * object, and add it to the list. */ pr = construct_problem_object (res, i, prj); *list = g_list_insert_sorted (*list, pr, compare_problems); return pr; } static gint output_nover_prs (FILE *fp, PGconn *conn, project *prj, GList **list) { GString *buffer; GString *url; PGresult *res; problem *pr; gint rtn = SQL_ERROR; gint prj_num = prj->prj_num; gint i, n; buffer = g_string_new (""); url = g_string_new (""); g_string_sprintf (buffer, PROBLEM_LIST_NO_VERSION, prj_num, prj_num); res = PQexec (conn, buffer->str); if (chk_sql_error (res, _("get problem list no version"))) { rtn = SUCCESS; n = PQntuples (res); if (n != 0) { fprintf (fp, "


      \n"); fprintf (fp, HEAD2, "Problem Reports Without Version Information"); fprintf (fp, "
        \n"); for (i = 0; i < n; i++) { pr = add_to_pr_list (res, i, prj, list); fprintf (fp, "
      • "); g_string_sprintf (url, "pr%d.html", pr->pr_num); g_string_sprintf (buffer, "PR #%d", pr->pr_num); fprintf (fp, LINK, url->str, buffer->str); buffer = g_string_assign (buffer, pr->title); g_string_prepare_html (buffer); fprintf (fp, ": %s\n", buffer->str); } fprintf (fp, "
      \n\n"); } } PQclear (res); g_string_free (buffer, TRUE); g_string_free (url, TRUE); return rtn; } static gint output_version_prs (FILE *fp, PGconn *conn, project *prj, gint ver_num, GList **list) { GString *buffer; GString *url; PGresult *res; problem *pr; gint rtn = SUCCESS; gint i, n; gint prj_num = prj->prj_num; gint total = 0; buffer = g_string_new (""); url = g_string_new (""); fprintf (fp, "
      \n"); g_string_sprintf (buffer, PROBLEM_LIST_BY_OPEN_VERSION, prj_num, ver_num, prj_num, ver_num); res = PQexec (conn, buffer->str); if (!chk_sql_error (res, _("get problem list by opening version"))) { rtn = SQL_ERROR; } else { n = PQntuples (res); total += n; if (n > 0) { fprintf (fp, "
      %s
      \n
      \n
        \n", "PRs Opened"); for (i = 0; i < n; i++) { pr = add_to_pr_list (res, i, prj, list); fprintf (fp, "
      • "); g_string_sprintf (url, "pr%d.html", pr->pr_num); g_string_sprintf (buffer, "PR #%d", pr->pr_num); fprintf (fp, LINK, url->str, buffer->str); buffer = g_string_assign (buffer, pr->title); g_string_prepare_html (buffer); fprintf (fp, ": %s\n", buffer->str); } fprintf (fp, "
      \n
      \n"); } } PQclear (res); g_string_sprintf (buffer, PROBLEM_LIST_BY_CLOSE_VERSION, prj_num, ver_num, prj_num, ver_num); res = PQexec (conn, buffer->str); if (!chk_sql_error (res, _("get probblem list by closing version"))) { rtn = SQL_ERROR; } else { n = PQntuples (res); total += n; if (n > 0) { fprintf (fp, "
      %s
      \n
      \n
        \n", "PRs Closed"); for (i = 0; i < n; i++) { pr = add_to_pr_list (res, i, prj, list); fprintf (fp, "
      • "); g_string_sprintf (url, "pr%d.html", pr->pr_num); g_string_sprintf (buffer, "PR #%d", pr->pr_num); fprintf (fp, LINK, url->str, buffer->str); buffer = g_string_assign (buffer, pr->title); g_string_prepare_html (buffer); fprintf (fp, ": %s\n", buffer->str); } fprintf (fp, "
      \n
      \n"); } } fprintf (fp, "
      \n\n"); if (total == 0) { fprintf (fp, "

      There are no problem reports for this version\n\n"); } PQclear (res); g_string_free (buffer, TRUE); g_string_free (url, TRUE); return rtn; } static gint create_project_version_page (PGconn *conn, gchar *path, project *prj, gint prev_prj_num, gint next_prj_num, gboolean active_vers_only) { FILE *fp; GString *buffer; GString *url; GString *prev_str; GString *prj_list_str; GString *next_str; GString *nav_table; GString *sql_buffer; GList *pr_list = NULL; GList *list_iter; gint prev_pr; gint next_pr; gint nvers; gint i; gint ver_num; problem *pr; gint rtn = SUCCESS; PGresult *ver_res; buffer = g_string_new (""); /* * Attempt to open the output file for writting. */ g_string_sprintf (buffer, "%s/proj%d.html", path, prj->prj_num); fp = fopen (buffer->str, "w"); if (!fp) { syslog (LOG_ERR, "Failed to open: %s: %m", buffer->str); g_string_free (buffer, TRUE); return FILE_OPEN_ERROR; } /* * Head and start of the body. */ g_string_sprintf (buffer, "%s - Version Report", prj->name); fprintf (fp, HTML_HEAD, buffer->str); fprintf (fp, BODY_DEF); /* * The nav table appears at the top and at the bottom of the body. * Therefore, create it, but don't destroy it until after it is * written at the button. */ prev_str = g_string_new ("Prev"); if (prev_prj_num > 0) { g_string_sprintf (buffer, "proj%d.html", prev_prj_num); g_string_sprintf (prev_str, LINK, buffer->str, "Prev"); } prj_list_str = g_string_new (""); g_string_sprintf (prj_list_str, LINK, "index.html", "Project List"); next_str = g_string_new ("Next"); if (next_prj_num > 0) { g_string_sprintf (buffer, "proj%d.html", next_prj_num); g_string_sprintf (next_str, LINK, buffer->str, "Next"); } nav_table = g_string_new (""); g_string_sprintf (nav_table, NAV_TABLE, prev_str->str, prj_list_str->str, next_str->str); g_string_free (prev_str, TRUE); g_string_free (prj_list_str, TRUE); g_string_free (next_str, TRUE); fprintf (fp, nav_table->str); /* * Top nav table done, start the deatils. Note that we may need to * format more than one URL, so just allocate the string up front. */ url = g_string_new (""); fprintf (fp, "


      \n\n"); fprintf (fp, HEAD1, prj->name); buffer = g_string_assign (buffer, prj->descr); g_string_prepare_html (buffer); fprintf (fp, "

      \nDescription:
      %s\n", buffer->str); if (prj->leader_e_mail != NULL) { g_string_sprintf (url, "mailto:%s", prj->leader_e_mail); g_string_sprintf (buffer, LINK, url->str, prj->leader); } else { buffer = g_string_assign (buffer, prj->leader); } fprintf (fp, "

      \nProject Leader: %s\n\n", buffer->str); sql_buffer = g_string_new (""); g_string_sprintf (sql_buffer, PROJECT_VERSION_LIST, prj->prj_num); if (active_vers_only) { sql_buffer = g_string_append (sql_buffer, PROJECT_VER_ACTIVE); } sql_buffer = g_string_append (sql_buffer, PROJECT_VER_ORDER_BY); ver_res = PQexec (conn, sql_buffer->str); if (!chk_sql_error (ver_res, _("get project version list"))) { g_string_free (buffer, TRUE); g_string_free (url, TRUE); g_string_free (sql_buffer, TRUE); PQclear (ver_res); fclose (fp); return SQL_ERROR; } rtn = output_nover_prs (fp, conn, prj, &pr_list); if (rtn != SUCCESS) { for (list_iter = g_list_first (pr_list); list_iter != NULL; list_iter = list_iter->next) { destroy_problem (list_iter->data); } g_list_free (pr_list); g_string_free (buffer, TRUE); g_string_free (url, TRUE); g_string_free (sql_buffer, TRUE); PQclear (ver_res); fclose (fp); return rtn; } nvers = PQntuples (ver_res); for (i = 0; i < nvers; i++) { ver_num = atoi (PQgetvalue (ver_res, i, PK_POS)); g_string_sprintf (buffer, "Version %s : %s", PQgetvalue (ver_res, i, NAME_POS), PQgetvalue (ver_res, i, DESCR_POS)); fprintf (fp, "


      \n"); fprintf (fp, HEAD2, buffer->str); /* * PR's opened against this version */ rtn = output_version_prs (fp, conn, prj, ver_num, &pr_list); if (rtn != SUCCESS) { for (list_iter = g_list_first (pr_list); list_iter != NULL; list_iter = list_iter->next) { destroy_problem (list_iter->data); } g_list_free (pr_list); g_string_free (buffer, TRUE); g_string_free (url, TRUE); g_string_free (sql_buffer, TRUE); PQclear (ver_res); fclose (fp); return rtn; } } /* end for */ /* * If we have nothing for this project, let the user know. */ if (nvers == 0 && pr_list == NULL) { fprintf (fp, "

      There are no problem reports for this project.\n\n"); } /* * output the final nav bar, and finish the page. */ fprintf (fp, "


      %s\n%s", nav_table->str, HTML_TAIL); /* * Create the PR detail files, destroying the problems as we go. */ prev_pr = -1; for (list_iter = g_list_first (pr_list); list_iter != NULL; list_iter = list_iter->next) { if (rtn == SUCCESS) { if (list_iter->next != NULL) { pr = list_iter->next->data; next_pr = pr->pr_num; } else { next_pr = -1; } pr = list_iter->data; rtn = create_pr_page (conn, path, pr, prev_pr, next_pr); prev_pr = pr->pr_num; } destroy_problem (list_iter->data); } g_list_free (pr_list); /* * finish the clean up */ g_string_free (buffer, TRUE); g_string_free (sql_buffer, TRUE); g_string_free (url, TRUE); g_string_free (nav_table, TRUE); PQclear (ver_res); fclose (fp); return SUCCESS; } gint create_project_version_report (PGconn *conn, gchar *path, GList *prj_pk_list, gboolean active_prjs_only, gboolean active_vers_only) { GList *prj_list; GList *list_iter; project *prj; gint prev_prj; gint next_prj; gint rtn = SUCCESS; prj_list = create_project_list (conn, prj_pk_list, active_prjs_only); if (prj_list) { create_project_list_page (path, "Problem Reports by Project and Project Version", prj_list); prev_prj = -1; for (list_iter = g_list_first (prj_list); list_iter != NULL; list_iter = list_iter->next) { if (list_iter->next) { prj = list_iter->next->data; next_prj = prj->prj_num; } else { next_prj = -1; } prj = list_iter->data; rtn = create_project_version_page (conn, path, prj, prev_prj, next_prj, active_vers_only); if (rtn != SUCCESS) { break; } prev_prj = prj->prj_num; } } for (list_iter = g_list_first (prj_list); list_iter != NULL; list_iter = list_iter->next) { destroy_project (list_iter->data); } g_list_free (prj_list); return rtn; }