1/** 2 * ProgrammingSubmissionService 3 * 4 * Frontend service for the event "Applicants" tab (programming application 5 * submissions) and the "Add to existing session" flow. Loaded as a classic 6 * script; exposes a global class with static methods that POST FormData to the 7 * route file. Session listing reuses the existing schedule endpoint. 8 */ 9class ProgrammingSubmissionService { 10 static get ROUTE() { return '/funcs/dashboard/events/programming/ProgrammingSubmissionRoute.php'; } 11 static get SESSION_ROUTE() { return '/funcs/dashboard/events/programming/SessionRoute.php'; } 12 static get MESSAGE_ROUTE() { return '/funcs/dashboard/events/programming/ProgrammingMessageRoute.php'; } 13 14 /** 15 * A page of the viewer's personal inbox thread with this submission's applicant account (same 16 * conversation they'd see in their own inbox -- not scoped to this event or Programming). 17 * eventId is only used server-side to check the caller's permission on this event, not to 18 * scope the query. Pass beforeId (an id from an earlier page) to load older messages. 19 * Returns { messages, has_more }. 20 */ 21 static async applicantMessages(eventId, submitId, beforeId, limit) { 22 const params = new FormData(); 23 params.append('post_type', 'applicant_messages'); 24 params.append('event_id', eventId); 25 params.append('submit_id', submitId); 26 if (beforeId) params.append('before_id', beforeId); 27 if (limit) params.append('limit', limit); 28 const res = await fetch(this.MESSAGE_ROUTE, { method: 'POST', body: params }); 29 return res.json(); 30 } 31 32 /** Applicants matching the group-message filters (application + status). Returns { recipients, count }. */ 33 static async groupMessageRecipients(eventId, appId, status) { 34 const params = new FormData(); 35 params.append('post_type', 'group_message_recipients'); 36 params.append('event_id', eventId); 37 params.append('app_id', appId || 0); 38 params.append('status', status || ''); 39 const res = await fetch(this.MESSAGE_ROUTE, { method: 'POST', body: params }); 40 return res.json(); 41 } 42 43 /** Send a group message to an explicit set of submissions (the kept recipient list). */ 44 static async sendGroupMessage(eventId, submitIds, message, includeSessions, attachmentFile) { 45 const params = new FormData(); 46 params.append('post_type', 'group_message_send'); 47 params.append('event_id', eventId); 48 params.append('message', message || ''); 49 params.append('include_sessions', includeSessions ? 1 : 0); 50 (submitIds || []).forEach(id => params.append('submit_ids[]', id)); 51 if (attachmentFile) params.append('attachment', attachmentFile); 52 const res = await fetch(this.MESSAGE_ROUTE, { method: 'POST', body: params }); 53 return res.json(); 54 } 55 56 /** Fetch the applicant (submission) list for an event. Returns the parsed response. */ 57 static async fetchSubmissions(eventId, timezone) { 58 const params = new FormData(); 59 params.append('post_type', 'fetch_programming_submissions'); 60 params.append('event_id', eventId); 61 params.append('timezone', timezone || 'UTC'); 62 63 const res = await fetch(this.ROUTE, { method: 'POST', body: params }); 64 return res.json(); 65 } 66 67 /** Add approved applicants (submission ids) to the given session ids. */ 68 static async addApplicantsToSessions(eventId, submitIds, sessionIds) { 69 const params = new FormData(); 70 params.append('post_type', 'add_applicants_to_sessions'); 71 params.append('event_id', eventId); 72 (submitIds || []).forEach(id => params.append('submit_ids[]', id)); 73 (sessionIds || []).forEach(id => params.append('session_ids[]', id)); 74 75 const res = await fetch(this.ROUTE, { method: 'POST', body: params }); 76 return res.json(); 77 } 78 79 /** Apply an organizer decision (approve|reject|waitlist|withdraw) to submissions. */ 80 static async setDecision(eventId, submitIds, decision) { 81 const params = new FormData(); 82 params.append('post_type', 'set_programming_decision'); 83 params.append('event_id', eventId); 84 params.append('decision', decision);
85 (submitIds || []).forEach(id => params.append('submit_ids[]', id)); 86 const res = await fetch(this.ROUTE, { method: 'POST', body: params }); 87 return res.json(); 88 } 89 90 /** Save an organizer (admin) override for one applicant answer. */ 91 static async updateAnswer(eventId, submitId, payload) { 92 const params = new FormData(); 93 params.append('post_type', 'update_programming_answer'); 94 params.append('event_id', eventId); 95 params.append('submit_id', submitId); 96 params.append('question_id', payload.question_id); 97 params.append('answer_type', payload.type); 98 if (payload.type === 'sa' || payload.type === 'pa' || payload.type === 'sg' || payload.type === 'nm') { 99 params.append('text', payload.text || ''); 100 } else if (payload.type === 'mc' || payload.type === 'dd') { 101 params.append('option_id', payload.option_id || 0); 102 } else if (payload.type === 'cb') { 103 (payload.option_ids || []).forEach(id => params.append('option_ids[]', id)); 104 } 105 const res = await fetch(this.ROUTE, { method: 'POST', body: params }); 106 return res.json(); 107 } 108 109 /** Organizer upload for a file-type (fl) question answer. Returns { success, filename, url }. */ 110 static async uploadAnswerFile(eventId, submitId, questionId, file) { 111 const params = new FormData(); 112 params.append('post_type', 'upload_programming_answer_file'); 113 params.append('event_id', eventId); 114 params.append('submit_id', submitId); 115 params.append('question_id', questionId); 116 params.append('upload_file', file); 117 const res = await fetch(this.ROUTE, { method: 'POST', body: params }); 118 return res.json(); 119 } 120 121 /** Create a new session (reuses the schedule update_session_info endpoint). Returns { success, session_id }. */ 122 static async createSession(payload) { 123 const params = new FormData(); 124 params.append('post_type', 'update_session_info'); 125 params.append('session_id', 0); 126 params.append('event_id', payload.event_id); 127 params.append('acct_id', payload.acct_id); 128 params.append('biz_id', payload.biz_id); 129 params.append('session_title', payload.session_title); 130 params.append('status', 'active'); 131 params.append('visibility', payload.visibility); 132 params.append('start_date', payload.start_date); 133 params.append('start_time', payload.start_time); 134 params.append('start_time_am_pm', payload.start_time_am_pm); 135 params.append('end_date', payload.end_date); 136 params.append('end_time', payload.end_time); 137 params.append('end_time_am_pm', payload.end_time_am_pm); 138 params.append('hide_end_time', payload.hide_end_time ? 1 : 0); 139 params.append('location', payload.location || ''); 140 params.append('description', payload.description || ''); 141 params.append('background_color', payload.background_color || '#0073BD'); 142 params.append('text_color', payload.text_color || '#FFFFFF'); 143 if (payload.track_id) params.append('track_id', payload.track_id); 144 if (payload.tag_ids) params.append('tag_ids', payload.tag_ids); 145 if (payload.map_id) params.append('map_id', payload.map_id); 146 if (payload.map_element_type) params.append('map_element_type', payload.map_element_type); 147 if (payload.map_element_id) params.append('map_element_id', payload.map_element_id); 148 const res = await fetch(this.SESSION_ROUTE, { method: 'POST', body: params }); 149 return res.json(); 150 } 151 152 /** Attach media (cover picture, tickets, add-ons, video) to a session. Reuses the schedule update_session_media endpoint. */ 153 static async updateSessionMedia(payload) { 154 // Build from the media form element so the cover_picture file input is always present (matches the schedule modal). 155 const params = payload.form ? new FormData(payload.form) : new FormData(); 156 params.append('post_type', 'update_session_media'); 157 params.append('session_id', payload.session_id); 158 params.append('event_id', payload.event_id); 159 params.append('acct_id', payload.acct_id); 160 params.append('guest_ids', ''); 161 params.append('ticket_ids', payload.ticket_ids || '');
162 params.append('add_on_ids', payload.add_on_ids || ''); 163 const res = await fetch(this.SESSION_ROUTE, { method: 'POST', body: params }); 164 return res.json(); 165 } 166 167 /** Fetch all sessions for an event (reuses the schedule endpoint), for the drawer picker. */ 168 static async fetchSessions(eventId, acctId, search) { 169 const params = new FormData(); 170 params.append('post_type', 'fetch_filtered_list'); 171 params.append('view_group', 'event'); 172 params.append('event_id', eventId); 173 params.append('acct_id', acctId || 0); 174 params.append('time_limit_min', '1'); 175 params.append('time_limit_max', '9999999999'); 176 params.append('search', (search || '').toLowerCase()); 177 params.append('visibility_filter', 'all'); 178 params.append('status_filter', ''); 179 180 const res = await fetch(this.SESSION_ROUTE, { method: 'POST', body: params }); 181 return res.json(); 182 } 183}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.