string notes_assign_n (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; Database_Notes database_notes (webserver_request); Notes_Logic notes_logic = Notes_Logic (webserver_request); Database_NoteAssignment database_noteassignment; string page; Assets_Header header = Assets_Header (translate("Assign notes"), request); page += header.run(); Assets_View view; string user = request->session_logic ()->currentUser (); // Notes can be assigned to the assignees. string userblock; vector <string> assignees = database_noteassignment.assignees (user); for (auto & assignee : assignees) { userblock.append ("<li><a href=\"bulk?assign=" + assignee + "\">" + assignee + "</a></li>\n"); } view.set_variable ("userblock", userblock); page += view.render ("notes", "assign-n"); page += Assets_Page::footer (); return page; }
string public_create (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; Database_Notes database_notes (webserver_request); Notes_Logic notes_logic = Notes_Logic (webserver_request); string page; Assets_Header header = Assets_Header (translate("Create note"), request); page += header.run (); Assets_View view; string bible = request->database_config_user()->getBible (); int book = Ipc_Focus::getBook (webserver_request); int chapter = Ipc_Focus::getChapter (webserver_request); int verse = Ipc_Focus::getVerse (webserver_request); string chapter_usfm = request->database_bibles()->getChapter (bible, book, chapter); string verse_usfm = usfm_get_verse_text (chapter_usfm, verse); string stylesheet = Database_Config_Bible::getExportStylesheet (bible); Filter_Text filter_text = Filter_Text (bible); filter_text.html_text_standard = new Html_Text (bible); filter_text.addUsfmCode (verse_usfm); filter_text.run (stylesheet); string versetext = filter_text.html_text_standard->getInnerHtml (); view.set_variable ("versetext", versetext); if (request->post.count ("submit")) { string summary = filter_string_trim (request->post["summary"]); if (summary.empty ()) summary = translate ("Feedback"); string contents = "<p>" + versetext + "</p>" + filter_string_trim (request->post["contents"]); int identifier = notes_logic.createNote (bible, book, chapter, verse, summary, contents, false); // A note created by a public user is made public to all. database_notes.setPublic (identifier, true); // Subscribe the user to the note. // Then the user receives email about any updates made on this note. database_notes.subscribe (identifier); // Go to the main public notes page. redirect_browser (request, public_index_url ()); return ""; } if (request->post.count ("cancel")) { redirect_browser (request, public_index_url ()); return ""; } string passage = filter_passage_display (book, chapter, convert_to_string (verse)); view.set_variable ("passage", passage); page += view.render ("public", "create"); page += Assets_Page::footer (); return page; }
string notes_bible_n (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; Database_Notes database_notes (webserver_request); Notes_Logic notes_logic = Notes_Logic (webserver_request); string page; Assets_Header header = Assets_Header (translate("Bibles"), request); page += header.run (); Assets_View view; string bibleblock; vector <string> bibles = access_bible_bibles (webserver_request); bibles.push_back (notes_logic.generalBibleName ()); for (auto & bible : bibles) { bibleblock.append ("<li><a href=\"bulk?bible=" + bible + "\">" + bible + "</a></li>\n"); } view.set_variable ("bibleblock", bibleblock); page += view.render ("notes", "bible-n"); page += Assets_Page::footer (); return page; }
void search_reindex_notes () { if (!Database_Config_General::getIndexNotes ()) return; // One simultaneous instance. if (search_reindex_notes_running) { Database_Logs::log ("Still indexing Consultation Notes", Filter_Roles::manager ()); return; } search_reindex_notes_running = true; // This does not run as a result of a webserver request, so create one. Webserver_Request request; // Database. Database_Notes database_notes (&request); // Set a flag indicating that the notes databases are not available to clients and other parties. database_notes.set_availability (false); // Delay shortly to give existing processes a change to complete. // Without this delay, "locked database" errors have been seen. this_thread::sleep_for (chrono::seconds (1)); // Check on health of the databases, and optionally recreate them. bool recreate = database_notes.checkup (); if (recreate) { Database_Logs::log ("The Consultation Notes main index was damaged and is being recreated", Filter_Roles::manager ()); } recreate = database_notes.checkup_checksums (); if (recreate) { Database_Logs::log ("The Consultation Notes checksums database was damaged and is being recreated", Filter_Roles::manager ()); } // Synchronize the notes databases with the notes on the filesystem, // because it may have happened that certain values at certain times // could not be written to the database because of errors. // Then the notes index would not match the notes data. // Syncing ensures the index matches the notes data in the filesystem. Database_Logs::log ("Updating Consultation Notes databases", Filter_Roles::manager ()); database_notes.sync (); // Set the availability flag so that clients and other parties access the notes databases again. database_notes.set_availability (true); Database_Logs::log ("Updating Consultation Notes databases ready", Filter_Roles::manager ()); Database_Config_General::setIndexNotes (false); search_reindex_notes_running = false; }
void Notes_Logic::handlerAddComment (int identifier) { notifyUsers (identifier, notifyNoteComment); // If the note status was Done, and a comment is added, mark it Reopened. Database_Notes database_notes (webserver_request); string status = database_notes.getRawStatus (identifier); if (status == "Done") { database_notes.setStatus (identifier, "Reopened"); } }
// Create sample notes. void demo_create_sample_notes (void * webserver_request) { Database_Notes database_notes (webserver_request); vector <int> identifiers = database_notes.getIdentifiers (); if (identifiers.size () < 10) { for (size_t i = 1; i <= 10; i++) { database_notes.storeNewNote (demo_sample_bible_name (), i, i, i, "Sample Note " + convert_to_string (i), "Sample Contents for note " + convert_to_string (i), false); } } }
// handleEmailComment - handles an email received from from with subject subject and body body. // Returns true if the mail was processed, else false. // The email is considered to have been processed if it comments on an existing Consultation Note. bool Notes_Logic::handleEmailComment (string from, string subject, string body) { // Check whether the Consultation Notes Identifier in the subject exists in the notes database. // The CNID looks like: (CNID123456789) size_t pos = subject.find ("(CNID"); if (pos == string::npos) return false; subject = subject.substr (pos + 5); pos = subject.find (")"); if (pos == string::npos) return false; subject = subject.substr (0, pos); // Webserver request. Webserver_Request * request = (Webserver_Request *) webserver_request; // At this stage, the subject contains an identifier. // Check that the identifier is an existing Consultation Note. int identifier = convert_to_int (subject); Database_Notes database_notes (webserver_request); if (!database_notes.identifierExists (identifier)) return false; // Check that the from address of the email belongs to an existing user. // Or else use the obfuscated email address as the user name. string username; from = filter_string_extract_email (from); if (request->database_users()->emailExists (from)) { username = request->database_users()->getEmailToUser (from); } else { username = from; username = filter_string_str_replace ("@", " ", username); username = filter_string_str_replace (".", " ", username); } // Clean the email's body. string year = convert_to_string (filter_date_numerical_year (filter_date_seconds_since_epoch ())); string sender = Database_Config_General::getSiteMailName(); body = filter_string_extract_body (body, year, sender); // Remove any new lines from the body. This cleans up the email considerably, // because some emails that get posted would otherwise look untidy, // when the many new lines take up a lot of space. body = filter_string_str_replace ("\n", " ", body); // Make comment on the consultation note. string sessionuser = request->session_logic ()->currentUser (); request->session_logic ()->setUsername (username); addComment (identifier, body); request->session_logic ()->setUsername (sessionuser); // Mail confirmation to the username. if (request->database_config_user()->getUserNotifyMeOfMyPosts (username)) { Database_Mail database_mail = Database_Mail (webserver_request); string subject = translate("Your comment was posted"); subject.append (" [CNID"); subject.append (convert_to_string (identifier)); subject.append ("]"); database_mail.send (username, subject, body); } // Log operation. Database_Logs::log ("Comment posted: " + body); // Job done. return true; }
void Notes_Logic::unmarkForDeletion (int identifier) { Database_Notes database_notes (webserver_request); database_notes.unmarkForDeletion (identifier); if (client_logic_client_enabled ()) { // Client: record the action in the database. string user = ((Webserver_Request *) webserver_request)->session_logic ()->currentUser (); Database_NoteActions database_noteactions = Database_NoteActions (); database_noteactions.record (user, identifier, Sync_Logic::notes_put_unmark_delete, ""); } else { // Server: do nothing extra. } }
// Sets the bible for note identifier. void Notes_Logic::setBible (int identifier, const string& bible) { Database_Notes database_notes (webserver_request); database_notes.setBible (identifier, bible); if (client_logic_client_enabled ()) { // Client: record the action in the database. string user = ((Webserver_Request *) webserver_request)->session_logic ()->currentUser (); Database_NoteActions database_noteactions = Database_NoteActions (); database_noteactions.record (user, identifier, Sync_Logic::notes_put_bible, ""); } else { // Server: do nothing extra. } }
void trash_consultation_note (void * webserver_request, int id) { Database_Notes database_notes (webserver_request); vector <Passage> passages = database_notes.getPassages (id); string passageText = filter_passage_display_inline (passages); string summary = database_notes.getSummary (id); string contents = database_notes.getContents (id); contents = filter_string_html2text (contents); Webserver_Request * request = (Webserver_Request *) webserver_request; string username = request->session_logic()->currentUser (); if (username.empty ()) username = "******"; Database_Logs::log (username + " deleted or marked for deletion consultation note " + passageText + " | " + summary + " | " + contents); }
void Notes_Logic::assignUser (int identifier, const string& user) { Database_Notes database_notes (webserver_request); if (client_logic_client_enabled ()) { // Client: record the action in the database. string myuser = ((Webserver_Request *) webserver_request)->session_logic ()->currentUser (); Database_NoteActions database_noteactions; database_noteactions.record (myuser, identifier, Sync_Logic::notes_put_assign, user); } else { // Server: do the notifications. // Assign logic comes before the database action in this particular case. handlerAssignNote (identifier, user); } database_notes.assignUser (identifier, user); }
void Notes_Logic::erase (int identifier) { Database_Notes database_notes (webserver_request); if (client_logic_client_enabled ()) { // Client: record the action in the database. string user = ((Webserver_Request *) webserver_request)->session_logic ()->currentUser (); Database_NoteActions database_noteactions = Database_NoteActions (); database_noteactions.record (user, identifier, Sync_Logic::notes_put_delete, ""); } else { // Server: notification. handlerDeleteNote (identifier); } trash_consultation_note (webserver_request, identifier); database_notes.erase (identifier); }
void Notes_Logic::handlerAssignNote (int identifier, const string& user) { // Take no action in client mode. if (client_logic_client_enabled ()) return; Database_Config_User database_config_user = Database_Config_User (webserver_request); if (database_config_user.getUserAssignedConsultationNoteNotification (user)) { // Only email the user if the user was not yet assigned this note. Database_Notes database_notes (webserver_request); vector <string> assignees = database_notes.getAssignees (identifier); if (find (assignees.begin(), assignees.end(), user) == assignees.end()) { emailUsers (identifier, translate("Assigned"), {user}, false); } } }
string notes_assign_1 (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; Database_Notes database_notes (webserver_request); Notes_Logic notes_logic = Notes_Logic (webserver_request); Database_NoteAssignment database_noteassignment; string page; Assets_Header header = Assets_Header (translate("Assign note"), request); page += header.run(); Assets_View view; string success, error; string user = request->session_logic ()->currentUser (); int id = convert_to_int (request->query ["id"]); view.set_variable ("id", convert_to_string (id)); if (request->query.count ("assign")) { string assign = request->query ["assign"]; if (database_noteassignment.exists (user, assign)) { notes_logic.assignUser (id, assign); } redirect_browser (request, notes_actions_url () + "?id=" + convert_to_string (id)); return ""; } // Note assignees. string userblock; vector <string> assignees = database_noteassignment.assignees (user); for (auto & assignee : assignees) { userblock.append ("<li><a href=\"assign-1?id=" + convert_to_string (id) + "&assign=" + assignee + "\">" + assignee + "</a></li>\n"); } view.set_variable ("userblock", userblock); view.set_variable ("success", success); view.set_variable ("error", error); page += view.render ("notes", "assign-1"); page += Assets_Page::footer (); return page; }
// Add a comment to an exiting note identified by identifier. void Notes_Logic::addComment (int identifier, const string& comment) { // Do nothing if there was no content. if (comment == "") return; Database_Notes database_notes (webserver_request); database_notes.addComment (identifier, comment); if (client_logic_client_enabled ()) { // Client: record the action in the database. string user = ((Webserver_Request *) webserver_request)->session_logic ()->currentUser (); Database_NoteActions database_noteactions = Database_NoteActions (); database_noteactions.record (user, identifier, Sync_Logic::notes_put_comment, comment); } else { // Server: do the notifications. handlerAddComment (identifier); } }
string public_comment (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; Database_Notes database_notes (webserver_request); Notes_Logic notes_logic = Notes_Logic (webserver_request); string page; Assets_Header header = Assets_Header (translate("Feedback"), request); page += header.run(); Assets_View view; int id = convert_to_int (request->query ["id"]); view.set_variable ("id", convert_to_string (id)); if (request->post.count ("submit")) { string comment = filter_string_trim (request->post ["comment"]); notes_logic.addComment (id, comment); redirect_browser (request, public_note_url () + "?id=" + convert_to_string (id)); return ""; } if (request->post.count ("cancel")) { redirect_browser (request, public_note_url () + "?id=" + convert_to_string (id)); return ""; } string summary = database_notes.getSummary (id); view.set_variable ("summary", summary); string content = database_notes.getContents (id); view.set_variable ("content", content); page += view.render ("public", "comment"); page += Assets_Page::footer (); return page; }
string notes_severity_1 (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; Database_Notes database_notes (webserver_request); Notes_Logic notes_logic = Notes_Logic (webserver_request); string page; Assets_Header header = Assets_Header (translate("Severity"), request); page += header.run(); Assets_View view; string success, error; int id = convert_to_int (request->query ["id"]); view.set_variable ("id", convert_to_string (id)); if (request->query.count ("severity")) { int severity = convert_to_int (request->query["severity"]); notes_logic.setRawSeverity (id, severity); redirect_browser (request, notes_actions_url () + "?id=" + convert_to_string (id)); return ""; } string severityblock; vector <Database_Notes_Text> severities = database_notes.getPossibleSeverities (); for (auto & severity : severities) { severityblock.append ("<li><a href=\"severity-1?id=" + convert_to_string (id) + "&severity=" + severity.raw + "\">" + severity.localized + "</a></li>\n"); } view.set_variable ("severityblock", severityblock); view.set_variable ("success", success); view.set_variable ("error", error); page += view.render ("notes", "severity-1"); page += Assets_Page::footer (); return page; }
// Create a consultation note. // $bible: The notes's Bible. // $book, $chapter, $verse: The note's passage. // $summary: The note's summary. // $contents: The note's contents. // $raw: Import $contents as it is. // It returns the $identifier of this new note. int Notes_Logic::createNote (string bible, int book, int chapter, int verse, string summary, string contents, bool raw) { summary = filter_string_str_replace ("\n", "", summary); Database_Notes database_notes (webserver_request); int note_id = database_notes.storeNewNote (bible, book, chapter, verse, summary, contents, raw); if (client_logic_client_enabled ()) { // Client: record the action in the database. Database_NoteActions database_noteactions; Webserver_Request * request = (Webserver_Request *) webserver_request; database_noteactions.record (request->session_logic()->currentUser (), note_id, Sync_Logic::notes_put_create_initiate, ""); database_noteactions.record (request->session_logic()->currentUser (), note_id, Sync_Logic::notes_put_summary, ""); // The contents to submit to the server, take it from the database, as it was updated in the logic above. database_noteactions.record (request->session_logic()->currentUser (), note_id, Sync_Logic::notes_put_contents, database_notes.getContents (note_id)); database_noteactions.record (request->session_logic()->currentUser (), note_id, Sync_Logic::notes_put_bible, ""); database_noteactions.record (request->session_logic()->currentUser (), note_id, Sync_Logic::notes_put_passages, ""); database_noteactions.record (request->session_logic()->currentUser (), note_id, Sync_Logic::notes_put_create_complete, ""); } else { // Server: do the notifications. handlerNewNote (note_id); } return note_id; }
void statistics_statistics () { Webserver_Request request; Database_Mail database_mail = Database_Mail (&request); Database_Modifications database_modifications; Database_Notes database_notes (&request); Database_Logs::log (translate("Sending statistics"), Filter_Roles::manager ()); string siteUrl = config_logic_site_url (); vector <string> bibles = request.database_bibles()->getBibles (); vector <string> users = request.database_users ()->getUsers (); for (auto & user : users) { string subject = "Bibledit " + translate("statistics"); vector <string> body; if (request.database_config_user()->getUserPendingChangesNotification (user)) { vector <int> ids = database_modifications.getNotificationIdentifiers (user); body.push_back ("<p><a href=\"" + siteUrl + changes_changes_url () + "\">" + translate("Number of change notifications") + "</a>: " + convert_to_string (ids.size()) + "</p>\n"); } if (request.database_config_user()->getUserAssignedNotesStatisticsNotification (user)) { vector <int> ids = database_notes.selectNotes ( bibles, // Bibles. 0, // Book 0, // Chapter 0, // Verse 3, // Passage selector. 0, // Edit selector. 0, // Non-edit selector. "", // Status selector. "", // Bible selector. user, // Assignment selector. 0, // Subscription selector. -1, // Severity selector. 0, // Text selector. "", // Search text. -1); // Limit. body.push_back ("<p><a href=\"" + siteUrl + notes_index_url () + "?presetselection=assigned\">" + translate("Number of consultation notes assigned to you awaiting your response") + "</a>: " + convert_to_string (ids.size ()) + "</p>\n"); } if (request.database_config_user()->getUserSubscribedNotesStatisticsNotification (user)) { body.push_back ("<p>" + translate("Number of consultation notes you are subscribed to") + ":</p>\n"); body.push_back ("<ul>\n"); request.session_logic ()->setUsername (user); vector <int> ids = database_notes.selectNotes ( bibles, // Bible. 0, // Book 0, // Chapter 0, // Verse 3, // Passage selector. 0, // Edit selector. 0, // Non-edit selector. "", // Status selector. "", // Bible selector. "", // Assignment selector. 1, // Subscription selector. -1, // Severity selector. 0, // Text selector. "", // Search text. -1); // Limit. body.push_back ("<li><a href=\"" + siteUrl + notes_index_url () + "?presetselection=subscribed\">" + translate("Total") + "</a>: " + convert_to_string (ids.size ()) + "</li>\n"); ids = database_notes.selectNotes ( bibles, // Bible. 0, // Book 0, // Chapter 0, // Verse 3, // Passage selector. 0, // Edit selector. 1, // Non-edit selector. "", // Status selector. "", // Bible selector. "", // Assignment selector. 1, // Subscription selector. -1, // Severity selector. 0, // Text selector. "", // Search text. -1); // Limit. body.push_back ("<li><a href=\"" + siteUrl + notes_index_url () + "?presetselection=subscribeddayidle\">" + translate("Inactive for a day") + "</a>: " + convert_to_string (ids.size ()) + "</li>\n"); ids = database_notes.selectNotes ( bibles, // Bible. 0, // Book 0, // Chapter 0, // Verse 3, // Passage selector. 0, // Edit selector. 3, // Non-edit selector. "", // Status selector. "", // Bible selector. "", // Assignment selector. 1, // Subscription selector. -1, // Severity selector. 0, // Text selector. "", // Search text. -1); // Limit. body.push_back ("<li><a href=\"" + siteUrl + notes_index_url () + "?presetselection=subscribedweekidle\">" + translate("Inactive for a week") + "</a>: " + convert_to_string (ids.size ()) + "</li>\n"); body.push_back ("</ul>\n"); request.session_logic ()->setUsername (""); } if (!body.empty ()) { string mailbody = filter_string_implode (body, "\n"); database_mail.send (user, subject, mailbody); } } }
string notes_index (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; Database_Notes database_notes (webserver_request); string page; Assets_Header header = Assets_Header (translate("Consultation Notes"), request); header.setNavigator (); header.addBreadCrumb (menu_logic_translate_menu (), menu_logic_translate_text ()); page = header.run(); Assets_View view; string error; string success; // Presets for notes selectors. // This is for the daily statistics and the workspace. if (request->query.count ("presetselection")) { request->database_config_user()->setConsultationNotesPassageSelector (3); request->database_config_user()->setConsultationNotesEditSelector (0); request->database_config_user()->setConsultationNotesNonEditSelector (0); request->database_config_user()->setConsultationNotesStatusSelector (""); request->database_config_user()->setConsultationNotesBibleSelector (""); request->database_config_user()->setConsultationNotesAssignmentSelector (""); request->database_config_user()->setConsultationNotesSubscriptionSelector (0); request->database_config_user()->setConsultationNotesSeveritySelector (-1); request->database_config_user()->setConsultationNotesTextSelector (0); string preset_selector = request->query ["presetselection"]; if (preset_selector == "assigned") { request->database_config_user()->setConsultationNotesAssignmentSelector (request->session_logic()->currentUser ()); } if (preset_selector == "subscribed") { request->database_config_user()->setConsultationNotesSubscriptionSelector (1); } if (preset_selector == "subscribeddayidle") { request->database_config_user()->setConsultationNotesSubscriptionSelector (1); request->database_config_user()->setConsultationNotesNonEditSelector (1); } if (preset_selector == "subscribedweekidle") { request->database_config_user()->setConsultationNotesSubscriptionSelector (1); request->database_config_user()->setConsultationNotesNonEditSelector (3); } if (preset_selector == "forverse") { request->database_config_user()->setConsultationNotesPassageSelector (0); } } int level = request->session_logic ()->currentLevel (); // Manager roles and higher can do mass updates on the notes. if (level >= Filter_Roles::manager ()) { // No mass updates in basic mode. if (!config_logic_basic_mode (webserver_request)) { view.enable_zone ("update"); } } // Whether the user can create a new note. if (access_logic_privilege_create_comment_notes (webserver_request)) { view.enable_zone ("create"); } page += view.render ("notes", "index"); page += Assets_Page::footer (); return page; }
// This handles email to users. // identifier: the note that is being handled. // label: prefix to the subject line of the email. // users: array of users to be mailed. // postpone: whether to postpone sending the email till the evening. void Notes_Logic::emailUsers (int identifier, const string& label, const vector <string> & users, bool postpone) { // Databases. Database_Notes database_notes (webserver_request); Database_Mail database_mail = Database_Mail(webserver_request); // Send mail to all users. string summary = database_notes.getSummary (identifier); string passages = filter_passage_display_inline (database_notes.getPassages (identifier)); string contents = database_notes.getContents (identifier); // Include links to the Cloud: One to the note, and one to the active desktop. contents.append ("<br>\n"); contents.append ("<p>"); contents.append ("<a href=\""); string notelink = Database_Config_General::getSiteURL () + notes_note_url () + "?id=" + convert_to_string (identifier); contents.append (notelink); contents.append ("\">"); contents.append (translate ("View or respond online")); contents.append ("</a>"); contents.append (" " + translate ("or") + " "); contents.append ("<a href=\""); string desktoplink = Database_Config_General::getSiteURL () + workbench_index_url () + "?note=" + convert_to_string (identifier); contents.append (desktoplink); contents.append ("\">"); contents.append (translate ("open the desktop online")); contents.append ("</a>"); contents.append ("</p>\n"); string mailto = "mailto:" + Database_Config_General::getSiteMailAddress () + "?subject=(CNID" + convert_to_string (identifier) + ")"; contents.append ("<p><a href=\""); contents.append (mailto); contents.append ("\">Respond by email</a></p>\n"); // Deal with possible postponing email till 9 PM. int timestamp = filter_date_seconds_since_epoch (); if (postpone) { int localseconds = filter_date_local_seconds (timestamp); float localhour = filter_date_numerical_hour (localseconds) + (float) filter_date_numerical_minute (localseconds) / 60; if (localhour < 21) { float difference = 21 - localhour; timestamp += (3600 * difference) - 10; } } // Send (but not in client mode). for (auto & user : users) { if (!client_logic_client_enabled ()) { string subject = label; subject.append (" | "); subject.append (passages); subject.append (" | "); subject.append (summary); subject.append (" | (CNID"); subject.append (convert_to_string (identifier)); subject.append (")"); database_mail.send (user, subject, contents, timestamp); } } }
// This handles notifications for the users // identifier: the note that is being handled. // notification: the type of action on the consultation note. void Notes_Logic::notifyUsers (int identifier, int notification) { // Take no action in client mode. if (client_logic_client_enabled ()) return; // Data objects. Webserver_Request * request = (Webserver_Request *) webserver_request; Database_Notes database_notes (webserver_request); // This note's Bible. string bible = database_notes.getBible (identifier); // Subscription and assignment not to be used for notes marked for deletion, // because marking notes for deletion is nearly the same as deleting them straightaway. if (notification != notifyMarkNoteForDeletion) { // Whether current user gets subscribed to the note. if (request->database_config_user ()->getSubscribeToConsultationNotesEditedByMe ()) { database_notes.subscribe (identifier); } // Users to get subscribed to the note, or to whom the note is to be assigned. vector <string> users = request->database_users ()->getUsers (); for (const string & user : users) { if (access_bible_read (webserver_request, bible, user)) { if (request->database_config_user ()->getNotifyUserOfAnyConsultationNotesEdits (user)) { database_notes.subscribeUser (identifier, user); } if (request->database_config_user ()->getUserAssignedToConsultationNotesChanges (user)) { database_notes.assignUser (identifier, user); } } } } // The recipients who receive a notification by email. vector <string> recipients; // Subscribers who receive email. vector <string> subscribers = database_notes.getSubscribers (identifier); for (const string & subscriber : subscribers) { if (request->database_config_user ()->getUserSubscribedConsultationNoteNotification (subscriber)) { recipients.push_back (subscriber); } } // Assignees who receive email. vector <string> assignees = database_notes.getAssignees (identifier); for (const string & assignee : assignees) { if (request->database_config_user ()->getUserAssignedConsultationNoteNotification (assignee)) { recipients.push_back (assignee); } } // In case the consultation note is deleted or marked for deletion, // notify only the users with this specific notification set. if ((notification == notifyNoteDelete) || (notification == notifyMarkNoteForDeletion)) { recipients.clear (); vector <string> users = request->database_users ()->getUsers (); for (const auto & user : users) { if (request->database_config_user ()->getUserDeletedConsultationNoteNotification (user)) { if (access_bible_read (webserver_request, bible, user)) { recipients.push_back (user); } } } } // Remove duplicates from the recipients. set <string> unique (recipients.begin (), recipients.end ()); recipients.assign (unique.begin (), unique.end()); // Deal with suppressing mail to the user when he made the update himself. string username = request->session_logic ()->currentUser (); if (request->database_config_user ()->getUserSuppressMailFromYourUpdatesNotes (username)) { recipients.erase (remove (recipients.begin(), recipients.end(), username), recipients.end()); } // Generate the label prefixed to the subject line of the email. string label = translate("General"); switch (notification) { case notifyNoteNew : label = translate("New"); break; case notifyNoteComment : label = translate("Comment"); break; case notifyNoteDelete : label = translate("Deleted"); break; case notifyMarkNoteForDeletion : label = translate("Marked for deletion"); break; } // Optional postponing sending email. bool postpone = false; if (notification == notifyNoteNew) { if (request->database_config_user ()->getPostponeNewNotesMails ()) { postpone = true; } } // Send mail to all recipients. emailUsers (identifier, label, recipients, postpone); }
string notes_notes (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; Database_Notes database_notes (webserver_request); string bible = access_bible_clamp (webserver_request, request->database_config_user()->getBible()); int book = Ipc_Focus::getBook (webserver_request); int chapter = Ipc_Focus::getChapter (webserver_request); int verse = Ipc_Focus::getVerse (webserver_request); int passage_selector = request->database_config_user()->getConsultationNotesPassageSelector(); int edit_selector = request->database_config_user()->getConsultationNotesEditSelector(); int non_edit_selector = request->database_config_user()->getConsultationNotesNonEditSelector(); string status_selector = request->database_config_user()->getConsultationNotesStatusSelector(); string bible_selector = request->database_config_user()->getConsultationNotesBibleSelector(); string assignment_selector = request->database_config_user()->getConsultationNotesAssignmentSelector(); bool subscription_selector = request->database_config_user()->getConsultationNotesSubscriptionSelector(); int severity_selector = request->database_config_user()->getConsultationNotesSeveritySelector(); int text_selector = request->database_config_user()->getConsultationNotesTextSelector(); string search_text = request->database_config_user()->getConsultationNotesSearchText(); int passage_inclusion_selector = request->database_config_user()->getConsultationNotesPassageInclusionSelector(); int text_inclusion_selector = request->database_config_user()->getConsultationNotesTextInclusionSelector(); // The Bibles the current user has access to. vector <string> bibles = access_bible_bibles (webserver_request, request->session_logic()->currentUser ()); // The admin disables notes selection on Bibles, // so the admin sees all notes, including notes referring to non-existing Bibles. if (request->session_logic ()->currentLevel () == Filter_Roles::admin ()) bibles.clear (); vector <int> identifiers = database_notes.selectNotes (bibles, book, chapter, verse, passage_selector, edit_selector, non_edit_selector, status_selector, bible_selector, assignment_selector, subscription_selector, severity_selector, text_selector, search_text, -1); // In case there aren't too many notes, there's enough time to sort them in passage order. if (identifiers.size () <= 200) { vector <int> passage_sort_keys; for (auto & identifier : identifiers) { int passage_sort_key = 0; vector <float> numeric_passages; vector <Passage> passages = database_notes.getPassages (identifier); for (auto & passage : passages) { numeric_passages.push_back (filter_passage_to_integer (passage)); } if (!numeric_passages.empty ()) { float average = accumulate (numeric_passages.begin (), numeric_passages.end (), 0) / numeric_passages.size (); passage_sort_key = round (average); } passage_sort_keys.push_back (passage_sort_key); } quick_sort (passage_sort_keys, identifiers, 0, identifiers.size ()); } string notesblock; for (auto & identifier : identifiers) { string summary = database_notes.getSummary (identifier); vector <Passage> passages = database_notes.getPassages (identifier); string verses = filter_passage_display_inline (passages); // A simple way to make it easier to see the individual notes in the list, // when the summaries of some notes are long, is to display the passage first. summary.insert (0, verses + " | "); string verse_text; if (passage_inclusion_selector) { vector <Passage> passages = database_notes.getPassages (identifier); for (auto & passage : passages) { string usfm = request->database_bibles()->getChapter (bible, passage.book, passage.chapter); string text = usfm_get_verse_text (usfm, convert_to_int (passage.verse)); if (!verse_text.empty ()) verse_text.append ("<br>"); verse_text.append (text); } } string content; if (text_inclusion_selector) { content = database_notes.getContents (identifier); } notesblock.append ("<a name=\"note" + convert_to_string (identifier) + "\"></a>\n"); notesblock.append ("<p><a href=\"note?id=" + convert_to_string (identifier) + "\">" + summary + "</a></p>\n"); if (!verse_text.empty ()) notesblock.append ("<p>" + verse_text + "</p>\n"); if (!content.empty ()) notesblock.append ("<p>" + content + "</p>\n"); } if (identifiers.empty ()) { return translate("This selection does not display any notes."); } return notesblock; }
string workbench_index (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; vector <string> desktops = workbench_get_names (request); // Set the requested desktop as the active one. if (request->query.count ("bench")) { unsigned int bench = convert_to_int (request->query ["bench"]); if (bench < desktops.size ()) { string workbench = desktops [bench]; request->database_config_user()->setActiveWorkbench (workbench); } } // Check that the active desktop exists, else set the first available desktop as the active one. { string desktop = request->database_config_user ()->getActiveWorkbench (); if (!in_array (desktop, desktops)) { if (!desktops.empty ()) { request->database_config_user ()->setActiveWorkbench (desktops [0]); } } } // Create default set of desktops if there are none. bool create = desktops.empty (); if (!create) { create = (desktops [0] == workbench_get_default_name ()); } if (create) { workbench_create_defaults (webserver_request); } // In case the desktop is opened from a consultation note email, // read the note, and set the active passage to the passage the note refers to. int noteid = convert_to_int (request->query ["note"]); if (noteid) { Database_Notes database_notes (webserver_request); vector <Passage> passages = database_notes.getPassages (noteid); if (!passages.empty ()) { Ipc_Focus::set (webserver_request, passages[0].book, passages[0].chapter, convert_to_int (passages[0].verse)); Navigation_Passage::recordHistory (webserver_request, passages[0].book, passages[0].chapter, convert_to_int (passages[0].verse)); } } string page; Assets_Header header = Assets_Header (translate("Desktop"), request); header.setNavigator (); header.setFadingMenu (menu_logic_desktop_category (webserver_request)); page = header.run (); Assets_View view; map <int, string> urls = workbench_get_urls (request, true); map <int, string> widths = workbench_get_widths (request); for (unsigned int key = 0; key < 15; key++) { string url = urls [key]; string width = widths [key]; int row = round (key / 5) + 1; int column = key % 5 + 1; string variable = "url" + convert_to_string (row) + convert_to_string (column); view.set_variable (variable, url); variable = "width" + convert_to_string (row) + convert_to_string (column); view.set_variable (variable, width); if (convert_to_int (width) > 0) view.enable_zone (variable); } map <int, string> heights = workbench_get_heights (request); for (unsigned int key = 0; key < 3; key++) { string height = heights [key]; int row = key + 1; string variable = "height" + convert_to_string (row); view.set_variable (variable, height); if (convert_to_int (height) > 0) view.enable_zone (variable); } string workbenchwidth = workbench_get_entire_width (request); if (!workbenchwidth.empty ()) { workbenchwidth.insert (0, "width: "); workbenchwidth.append (";"); } view.set_variable ("workbenchwidth", workbenchwidth); // The rendered template disables framekillers through the "sandbox" attribute on the iframe elements. page += view.render ("workbench", "index"); page += Assets_Page::footer (); return page; }
string notes_actions (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; Database_Notes database_notes (webserver_request); Notes_Logic notes_logic = Notes_Logic (webserver_request); string page; Assets_Header header = Assets_Header (translate("Note actions"), request); header.setNavigator (); page += header.run (); Assets_View view; string success, error; string user = request->session_logic()->currentUser (); int level = request->session_logic()->currentLevel (); int id; if (request->query.count ("id")) id = convert_to_int (request->query ["id"]); else id = convert_to_int (request->post ["id"]); if (request->query.count ("unsubscribe")) { notes_logic.unsubscribe (id); } if (request->query.count ("subscribe")) { notes_logic.subscribe (id); } if (request->query.count ("unassign")) { string unassign = request->query["unassign"]; notes_logic.unassignUser (id, unassign); } if (request->query.count ("done")) { notes_logic.unassignUser (id, user); } if (request->query.count ("markdel")) { notes_logic.markForDeletion (id); success = translate("The note will be deleted after a week.") + " " + translate ("Adding a comment to the note cancels the deletion."); } if (request->query.count ("unmarkdel")) { notes_logic.unmarkForDeletion (id); } if (request->query.count ("delete")) { notes_logic.erase (id); redirect_browser (request, notes_index_url ()); return ""; } if (request->query.count ("publicnote")) { bool state = database_notes.getPublic (id); database_notes.setPublic (id, !state); } view.set_variable ("id", convert_to_string (id)); string summary = database_notes.getSummary (id); view.set_variable ("summary", summary); bool subscribed = database_notes.isSubscribed (id, user); if (subscribed) view.enable_zone ("subscribed"); else view.enable_zone ("subscribe"); vector <string> assignees = database_notes.getAssignees (id); string assigneeblock; for (auto & assignee : assignees) { assigneeblock.append (assignee); if (level >= Filter_Roles::manager ()) { assigneeblock.append ("<a href=\"?id=" + convert_to_string (id) + "&unassign=" + assignee + "\"> [" + translate("unassign") + "]</a>"); assigneeblock.append (" | "); } } view.set_variable ("assigneeblock", assigneeblock); if (level >= Filter_Roles::manager ()) view.enable_zone ("assign"); bool assigned = database_notes.isAssigned (id, user); if (assigned) view.enable_zone ("assigned"); string status = database_notes.getStatus (id); view.set_variable ("status", status); if (Filter_Roles::translator ()) view.enable_zone ("editstatus"); else view.enable_zone ("viewstatus"); string verses = filter_passage_display_inline (database_notes.getPassages (id)); view.set_variable ("verses", verses); string severity = database_notes.getSeverity (id); view.set_variable ("severity", severity); string bible = database_notes.getBible (id); view.set_variable ("bible", bible); if (bible.empty ()) view.enable_zone ("nobible"); if (level >= Filter_Roles::manager ()) view.enable_zone ("rawedit"); if (level >= Filter_Roles::manager ()) view.enable_zone ("marknote"); bool marked = database_notes.isMarkedForDeletion (id); if (marked) view.enable_zone ("marked"); else view.enable_zone ("mark"); #ifndef HAVE_CLIENT view.enable_zone ("cloud"); string on_off = styles_logic_off_on_inherit_toggle_text (database_notes.getPublic (id)); view.set_variable ("publicnote", on_off); #endif // Roles of translator or higher can edit the public visibility of a note. if (level >= Filter_Roles::translator ()) view.enable_zone ("translator"); view.set_variable ("success", success); view.set_variable ("error", error); page += view.render ("notes", "actions"); page += Assets_Page::footer (); return page; }