#include #include #include #include #include "global.h" #include "custom.h" /* for date formats */ #include "manhat-lib/shared_util.h" #include "manhat-lib/shared_cs_util.h" #include "manhat-lib/shared_news_util.h" #include "manhat-lib/shared_person_list.h" #include "manhat-lib/shared_lock.h" #include "manhat-lib/shared_hotpot_exam_util.h" #include "manhat-lib/shared_translate_url.h" #include "manhat-lib/shared_access.h" typedef struct student_node { char username[MAX_USERNAME + 1]; char realname[MAX_NAME + 1]; int msgs; /* total msgs to/from this student */ int unread; /* number of unread msgs */ int hidden; /* number of msgs marked teacher hidden */ int flagged; /* number of msgs marked with a flag */ struct student_node *next; } STUDENT_NODE; static void read_parameters (char *course, char *key, long *offset, int *topic) { char astring[80]; cs_get_required_parameter ("crs", course, MAX_PATH); /* shared_cs_util.c */ cs_get_required_parameter ("id", key, MAX_KEY); /* shared_cs_util.c */ cs_get_required_parameter ("loc", astring, 79); /* shared_cs_util.c */ *offset = atol (astring); cs_get_required_parameter ("topic", astring, 79); /* shared_cs_util.c */ *topic = atoi (astring); } static void memory_error () { cs_critical_error (ERR_MALLOC_FAILED, ""); } static void set_command_data (char *course, char *key) { char url[MAX_PATH +1]; snprintf (url, MAX_PATH +1, "%s?crs=%s&id=%s&grp=%d", "news_topic", course, key,ASSIGNMENTS); cs_set_value("back_url", url); snprintf (url, MAX_PATH +1, "%s?crs=%s&id=%s", "main_menu", course, key); cs_set_value("main_url", url); } static void cs_set_form_data (char *course, char *key, long offset, int topic) { char temp[20]; cs_set_value("crs", course); cs_set_value("id", key); snprintf (temp, 20, "%ld", offset); cs_set_value("loc", temp); snprintf (temp, 20, "%d", topic); cs_set_value("topic", temp); } static void free_student_list (STUDENT_NODE *student_head) { STUDENT_NODE *ptr; while (student_head) { ptr = student_head; student_head = student_head->next; free (ptr); } } STUDENT_NODE * build_student_list(CONFIG_STRUCT *conf, P_NODE *person_list_head) { STUDENT_NODE *student_ptr =0; STUDENT_NODE *current_ptr =0; P_NODE *ptr; STUDENT_NODE *student_head = 0; for(ptr = person_list_head; ptr; ptr=ptr->next) { if(ptr->data.group != FACULTY) { student_ptr = (STUDENT_NODE *)malloc(sizeof(STUDENT_NODE )); if(!student_ptr) memory_error(); strcpy(student_ptr->username, ptr->data.username); strcpy(student_ptr->realname, ptr->data.realname); student_ptr->next = 0; student_ptr->msgs = 0; student_ptr->flagged = 0; student_ptr->unread = 0; student_ptr->hidden = 0; if(!student_head) student_head = student_ptr; else current_ptr->next = student_ptr; current_ptr = student_ptr; } } return student_head; } /** A student can be deleted from the course after they have submitted *** an assignment. This function adds these students to the *** student list **/ static void add_additional_students(MSG_TREE_NODE *root, P_NODE *person_head, STUDENT_NODE *student_head, int topic_id) { STUDENT_NODE *s_ptr; P_NODE *p_ptr; int found; if(!root) return; if(root->data.info.reply_to == topic_id) /* only consider responses to the original assignment this will exclude faculty */ { for(p_ptr=person_head, found=0; p_ptr && !found; p_ptr=p_ptr->next) found = !strcmp(root->data.info.sender, p_ptr->data.username); if(!found) { if(!student_head) { student_head = (STUDENT_NODE *) malloc(sizeof(STUDENT_NODE)); if(!student_head) memory_error(); s_ptr = student_head; } else { for(s_ptr = student_head; s_ptr->next; s_ptr = s_ptr->next); s_ptr->next = (STUDENT_NODE *) malloc(sizeof(STUDENT_NODE)); if(!s_ptr->next) memory_error(); s_ptr = s_ptr->next; } strcpy(s_ptr->username, root->data.info.sender); strcpy(s_ptr->realname, root->data.info.sender_realname); s_ptr->msgs = 0; s_ptr->unread = 0; s_ptr->hidden = 0; s_ptr->flagged = 0; s_ptr->next = (STUDENT_NODE *) 0; } /* if !found */ } /* if topic_id matches */ if(root->next) add_additional_students(root->next,person_head, student_head, topic_id); if(root->reply) add_additional_students(root->reply, person_head, student_head, topic_id); } static void add_inbox_data (long offset, MSG_TREE_NODE * assignment, SESSION *user, P_NODE * person_head, STUDENT_NODE *student_head, time_t *current_time) { MSG_TREE_NODE *msg_ptr; MSG_TREE_NODE *topic_ptr; int msg_count; STUDENT_NODE *student_ptr; build_one_topic_tree(user, ASSIGNMENTS, offset, &msg_ptr, &topic_ptr, &msg_count, 0, 1, current_time); /* shared_news_util.c */ if(!msg_ptr || !tree_head || !topic_ptr) cs_critical_error(ERR_CANT_FIND_THIS_OFFSET, ""); *assignment = *topic_ptr; add_additional_students(topic_ptr, person_head, student_head, topic_ptr->data.info.topic_id); for(student_ptr = student_head; student_ptr; student_ptr= student_ptr->next) count_username_msgs(topic_ptr, student_ptr->username, &(student_ptr->msgs), &(student_ptr->unread), &(student_ptr->hidden), &(student_ptr->flagged)); /* shared_news_util.c */ free_msg_tree(tree_head); /*shared_news_util.c */ } static void set_one_student_result( char *realname, char *username, char *viewed_timestring, char *score_string, char *elapsed_timestring, char *start_timestring, char *submit_timestring, char *scorepath, int index) { #define MAX_TMP_NAME 40 char name[MAX_TMP_NAME]; static int j =0; /* realname(username) */ if(*realname) { snprintf(name, MAX_TMP_NAME, "score.%d.realname", index); cs_set_value(name, realname); /* shared_cs_util.c */ snprintf(name, MAX_TMP_NAME, "score.%d.username", index); cs_set_value(name, username); /* shared_cs_util.c */ if(*viewed_timestring) { snprintf(name, MAX_TMP_NAME, "score.%d.viewed_time", index); cs_set_value(name, viewed_timestring); /* shared_cs_util.c */ snprintf(name, MAX_TMP_NAME, "score.%d.scorepath", index); cs_set_value(name, scorepath); /* shared_cs_util.c */ } j = 0; } /* score */ if(*score_string) { snprintf(name, MAX_TMP_NAME, "score.%d.score.%d.num", index, j); cs_set_value(name, score_string); /* shared_cs_util.c */ } /* elapsed_time */ if(*elapsed_timestring) { snprintf(name, MAX_TMP_NAME, "score.%d.score.%d.elapsed_time", index, j); cs_set_value(name, elapsed_timestring); /* shared_cs_util.c */ } /* start time/submit_time */ if(*start_timestring) { snprintf(name, MAX_TMP_NAME, "score.%d.score.%d.start_time", index, j); cs_set_value(name, start_timestring); /* shared_cs_util.c */ if(*submit_timestring) { snprintf(name, MAX_TMP_NAME, "score.%d.score.%d.submit_time", index, j); cs_set_value(name, submit_timestring); /* shared_cs_util.c */ } else { snprintf(name, MAX_TMP_NAME, "score.%d.score.%d.no_submit", index, j); cs_set_int_value(name, 1); /* shared_cs_util.c */ } j++; } snprintf(name, MAX_TMP_NAME, "score.%d.how_many", index); cs_set_int_value(name, j); /* shared_cs_util.c */ #undef MAX_TMP_NAME } static void parse_score_line(char *line, char *elapsed_timestring, char *start_timestring, char *submit_timestring, char *score_string) { #define MAX_STRING 100 char string1[MAX_STRING + 1]; char string2[MAX_STRING + 1]; time_t sometime; /* get number correct */ if(csv_value(line,0,string1, MAX_STRING)) /* shared_hotpot_exam_util.c */ cs_critical_error(ERR_ERROR_PARSING_SCORE, line); /* shared_cs_util.c */ /* get total questions */ if(csv_value(line,1,string2, MAX_STRING)) /* shared_hotpot_exam_util.c */ cs_critical_error(ERR_ERROR_PARSING_SCORE, line); /* shared_cs_util.c */ snprintf(score_string,50,"%s/%s", string1, string2); /* caution- check calling function for correct size of score_string */ /* get elapsed time - it is already saved in correct format */ if(csv_value(line,2,elapsed_timestring, MAX_TIMESTRING)) /* shared_hotpot_exam_util.c */ cs_critical_error(ERR_ERROR_PARSING_SCORE, line); /* shared_cs_util.c */ /* get time exam was started */ if(csv_value(line,3,string1, MAX_STRING)) /* shared_hotpot_exam_util.c */ cs_critical_error(ERR_ERROR_PARSING_SCORE, line); /* shared_cs_util.c */ sometime = (time_t) atol(string1); strftime(start_timestring, MAX_TIMESTRING, DOW_DATE_TIME_FORMAT, localtime(&sometime)); /* get time exam was submitted */ if(csv_value(line,4,string1, MAX_STRING)) /* shared_hotpot_exam_util.c */ cs_critical_error(ERR_ERROR_PARSING_SCORE, line); /* shared_cs_util.c */ sometime = (time_t) atol(string1); strftime(submit_timestring, MAX_TIMESTRING, DOW_DATE_TIME_FORMAT, localtime(&sometime)); #undef MAX_STRING } static void set_student_exam_data( char *score_path, char *realname, char *username, int index) { #define MAX_LINE 1000 FILE *fp; int fd; time_t viewed_time; char viewed_timestring[MAX_TIMESTRING + 1] = ""; char elapsed_timestring[MAX_TIMESTRING + 1] = ""; char start_timestring[MAX_TIMESTRING + 1] = ""; char submit_timestring[MAX_TIMESTRING + 1] = ""; char score_string[50] = ""; char line[MAX_LINE + 1]; int unsubmitted =0; int score_count = 0; fp = fopen(score_path,"r"); if(!fp) { set_one_student_result(realname, username, "", "","","","", score_path, index); return; } fd = fileno(fp); get_shared_lock(fd,1); /* shared_lock.c */ if(fread(&viewed_time, sizeof(time_t), 1, fp) != 1) cs_critical_error(ERR_FREAD_FAILED, score_path); if(viewed_time != 0) strftime(viewed_timestring, MAX_TIMESTRING, DOW_DATE_TIME_FORMAT, localtime(&viewed_time)); else *viewed_timestring = '\0'; /** it's possible the student left immediately after viewing the exam *** in that case the next read will fail **/ if(!fgets(line, MAX_LINE,fp)) { if(viewed_time) set_one_student_result(realname, username, viewed_timestring, "", "", viewed_timestring, "", score_path, index); else set_one_student_result(realname, username, "", "","","","",score_path, index); } else { do { parse_score_line(line, elapsed_timestring, start_timestring, submit_timestring, score_string); if(strcmp(viewed_timestring, start_timestring)) unsubmitted = 1; else unsubmitted = 0; set_one_student_result(score_count?"":realname, score_count?"":username, score_count?"":viewed_timestring, score_string, elapsed_timestring, start_timestring, submit_timestring, score_path, index); score_count++; } while(fgets(line, MAX_LINE, fp)) ; } if(unsubmitted) set_one_student_result("", "", viewed_timestring,"", "", viewed_timestring, "", score_path, index); release_lock(fd); fclose(fp); #undef MAX_LINE } static void set_exam_results_data(const char *course_path, const char *sender, const char *info_fname, STUDENT_NODE *student_head) { STUDENT_NODE *sptr; char score_path[MAX_PATH + 1]; int index = 0; for(sptr=student_head; sptr; sptr=sptr->next) { get_hotpot_score_path(course_path, sptr->username, sender, info_fname, score_path); /* shared_hotpot_exam_util.c */ set_student_exam_data(score_path,sptr->realname,sptr->username, index); index++; } #undef MAX_TMP_NAME } static void set_expand_topic_data (char *course, char *key, int topic, long offset, CONFIG_STRUCT *conf , SESSION *user, P_NODE *p) { #define MAX_TMP_NAME 30 char *new_str = 0; int i; char name[MAX_TMP_NAME]; char url[MAX_PATH +1]; MSG_TREE_NODE assignment; char info_prog[MAX_PATH + 1]; STUDENT_NODE *sptr; char timestring[MAX_TIMESTRING + 1]; int hotpot_exam; /* is this assignment a Hot Potatoes exam? */ int contains_unreleased_msgs = 0; /* for Hot Potatoes exams, is there one or more unreleased exam result? */ char *cptr; char rname[MAX_NAME + 1]; STUDENT_NODE *student_head; time_t current_time; student_head = build_student_list (conf,p); add_inbox_data (offset, &assignment, user, p, student_head, ¤t_time); /* is this a Hot Potatoes Exam? */ hotpot_exam = is_hotpot_exam(&assignment.data, conf->course_path); /* shared_news_util.c */ snprintf (info_prog, MAX_PATH + 1,"%s?crs=%s&id=%s&grp=%d&loc=","news_info", course, key, ASSIGNMENTS); /* set the data for the actual assignment */ if(!assignment.user_data.time_read ) cs_set_int_value("msg.is_unread", 1); if(assignment.data.info.attachments) cs_set_int_value("msg.has_attachments", 1); if(assignment.user_data.flagged) cs_set_int_value("msg.flagged", 1); snprintf(url, MAX_PATH + 1, "%s?crs=%s&id=%s&grp=%d&msg_id=%d&from=assign_fac_expand%ccrs%c%s%cid%c%s%cloc%c%ld%ctopic%c%d", "news_flag", course, key, ASSIGNMENTS, topic, QMARK_SUB,EQUAL_SUB, course, AMP_SUB, EQUAL_SUB, key, AMP_SUB, EQUAL_SUB, offset, AMP_SUB, EQUAL_SUB, topic); cs_set_value("msg.flag_url", url); snprintf (url, MAX_PATH + 1, "%s%ld", info_prog, offset); cs_set_value("msg.info_url", url); /* shared_cs_util.c */ /* AMP_SUB, EQUAL_SUB, etc. are #defined in shared_translate_url.h */ snprintf (url, MAX_PATH +1, "%s?crs=%s&id=%s&loc=%ld&from=%s%ccrs%c%s%cid%c%s%ctopic%c%d%cloc%c%ld", "assign_read", course, key, offset, "assign_fac_expand", QMARK_SUB, EQUAL_SUB, course, AMP_SUB, EQUAL_SUB, key, AMP_SUB, EQUAL_SUB, topic,AMP_SUB,EQUAL_SUB,offset); if(USE_FIXED_FONT(conf->misc)) /* global.h */ strcat(url, "&fixed=1"); cs_set_value("msg.subject_url", url); /* shared_cs_util.c */ if (!strlen (assignment.data.info.subject)) cs_set_int_value("msg.no_subject", 1); /* shared_cs_util.c */ else { /* next makes use of undocmented ClearSilver function */ if(cgi_html_escape_strfunc(assignment.data.info.subject,&new_str) == STATUS_OK) { cs_set_value("msg.subject",new_str); /* shared_cs_util.c */ if(new_str) free(new_str); } else cs_set_value("msg.subject",assignment.data.info.subject); /* shared_cs_util.c */ } cs_set_value("msg.sender_realname", assignment.data.info.sender_realname); /* shared_cs_util.c */ strftime (timestring, MAX_TIMESTRING, DOW_DATE_TIME_FORMAT, localtime (&(assignment.data.info.time_sent))); cs_set_value("msg.sent_time", timestring); /* shared_cs_util.c */ if(hotpot_exam) cs_set_int_value("msg.hotpot_exam", 1); /* shared_cs_util.c */ /* is_teacher_hidden() is in shared_news_util.c ** BASIC_STATUS() is a macro #defined in global.h */ if (is_teacher_hidden(assignment.data.status, assignment.data.release_time, assignment.data.unrelease_time, current_time) ) cs_set_int_value("msg.hidden", 1); /* shared_cs_util.c */ else if(BASIC_STATUS(assignment.data.status) == READ_ONLY) /* shared_cs_util.c */ cs_set_int_value("msg.read_only", 1); for (sptr = student_head, i = 0; sptr; sptr = sptr->next, i++) { if(sptr->unread) { snprintf(name, MAX_TMP_NAME, "std.%d.unread", i); cs_set_int_value(name, 1); /* shared_cs_util.c */ } if(sptr->flagged) { snprintf(name, MAX_TMP_NAME, "std.%d.flagged", i); cs_set_int_value(name, 1); /* shared_cs_util.c */ } snprintf(name, MAX_TMP_NAME, "std.%d.count", i); cs_set_int_value(name, sptr->msgs); /* shared_cs_util.c */ /* replace spaces with underscores in realname */ strncpy (rname, sptr->realname, MAX_NAME + 1); while ((cptr = strchr (rname, ' '))) *cptr = '_'; snprintf(url, MAX_PATH +1, "%s?crs=%s&id=%s&topic=%d&realname=%s&student=%s&loc=%ld", "assign_student_work", course, key, topic, rname, sptr->username, offset); snprintf(name, MAX_TMP_NAME, "std.%d.name_url", i); cs_set_value(name, url); /* shared_cs_util.c */ snprintf(name, MAX_TMP_NAME, "std.%d.realname", i); cs_set_value(name, sptr->realname); /* shared_cs_util.c */ if(hotpot_exam && sptr->hidden) { snprintf(name, MAX_TMP_NAME, "std.%d.unreleased_hotpot", i); cs_set_int_value(name, 1); contains_unreleased_msgs = 1; } } /* for(sptr=student_head...) */ if(hotpot_exam && contains_unreleased_msgs) cs_set_int_value("msg.has_unreleased_hotpot", 1); /* shared_cs_util.c */ if(hotpot_exam) { cs_set_int_value("msg.hotpot_exam", 1); /* shared_cs_util.c */ set_exam_results_data(conf->course_path, assignment.data.info.sender, assignment.data.info_fname, student_head); } free_student_list (student_head ); #undef MAX_TMP_NAME } static void set_assign_data (char *course, char *key, long offset, int topic, CONFIG_STRUCT *conf, SESSION *user, int can_write) { P_NODE *person_list_head; cs_set_course_info(conf); /* shared_cs_util.c */ cs_set_current_time(); /* shared_cs_util.c */ if(can_write) cs_set_int_value("can_write", 1); /* shared_cs_util.c */ set_command_data (course, key); person_list_head = build_option_person_list (conf); /* shared_person_list.c */ if(conf->access != ACCESS_CAPTURE) { cs_set_int_value("not_capture", 1); /* shared_cs_util.c */ set_option_userlist (person_list_head); /* shared_person_list.c */ } cs_set_form_data (course, key, offset,topic); set_expand_topic_data (course, key, topic, offset, conf, user, person_list_head); free_option_person_list (person_list_head); /* shared_person_list.c */ } int main (void) { char course[MAX_PATH + 1]; /* from crs=? command line */ char key[MAX_KEY + 1]; /* from id=? command line */ long offset; /* from loc=? command line */ int topic; /* from topic=? command line */ SESSION user; CONFIG_STRUCT conf; /* the configuration read from config file */ int can_write; /* can this user write to this course? */ cs_cgi_init(); /* shared_cs_util.c */ read_parameters (course, key, &offset, &topic); read_configuration_file (course, &conf); /* shared_util.c */ validate_key (key, &user, &conf); /* shared_util.c */ if(user.group != FACULTY) cs_critical_error (ERR_REQUEST_DENIED, ""); /* shared_cs_util.c */ can_write = has_write_permission(&user, &conf); /* shared_access.c */ set_discussion_names (conf.course_path, user.username, ASSIGNMENTS); /* shared_news_util.c */ set_assign_data (course, key, offset, topic, &conf, &user, can_write); cs_cgi_display("assign_fac_expand", 1); /* shared_cs_util.c */ cs_cgi_destroy(); /* shared_cs_util.c */ return 0; }