string filter_passage_link_for_opening_editor_at (int book, int chapter, string verse) { string display = filter_passage_display (book, chapter, verse); Passage passage = Passage ("", book, chapter, verse); string numeric = convert_to_string (filter_passage_to_integer (passage)); string link = "<a class=\"starteditor\" href=\"" + numeric + "\">" + display + "</a> <span></span>"; return link; }
string xrefs_next (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; string bible = request->database_config_user()->getTargetXrefBible (); int currentBook = Ipc_Focus::getBook (webserver_request); int currentChapter = Ipc_Focus::getChapter (webserver_request); Passage currentPassage = Passage ("", currentBook, currentChapter, "1"); int currentLocation = filter_passage_to_integer (currentPassage); vector <int> books = request->database_bibles()->getBooks (bible); for (auto book : books) { vector <int> chapters = request->database_bibles()->getChapters (bible, book); for (auto chapter : chapters) { if (chapter == 0) continue; Passage passage = Passage ("", book, chapter, "1"); int location = filter_passage_to_integer (passage); if (location > currentLocation) { string usfm = request->database_bibles()->getChapter (bible, book, chapter); auto xrefs = usfm_extract_notes (usfm, {"x"}); if (xrefs.empty ()) { Ipc_Focus::set (webserver_request, book, chapter, 1); redirect_browser (request, xrefs_index_url ()); return ""; } } } } redirect_browser (request, xrefs_index_url ()); return ""; }
string resource_img (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; Database_ImageResources database_imageresources; string page; Assets_Header header = Assets_Header (translate("Image resources"), request); page = header.run (); Assets_View view; string error, success; int book1, chapter1, verse1, book2, chapter2, verse2; string name = request->query ["name"]; view.set_variable ("name", name); string image = request->query ["image"]; view.set_variable ("image", image); int userid = filter_string_user_identifier (webserver_request); if (request->post.count ("submit")) { vector <string> errors; string book = request->post ["book1"]; book1 = filter_passage_interpret_book (book); if (book1 == 0) errors.push_back (translate ("Unknown starting book.")); chapter1 = convert_to_int (request->post ["chapter1"]); if (chapter1 < 0) errors.push_back (translate ("Negative starting chapter.")); if (chapter1 > 200) errors.push_back (translate ("High starting chapter.")); verse1 = convert_to_int (request->post ["verse1"]); if (chapter1 < 0) errors.push_back (translate ("Negative starting verse.")); if (chapter1 > 200) errors.push_back (translate ("High starting verse.")); book = request->post ["book2"]; book2 = filter_passage_interpret_book (book); if (book2 == 0) errors.push_back (translate ("Unknown ending book.")); chapter2 = convert_to_int (request->post ["chapter2"]); if (chapter2 < 0) errors.push_back (translate ("Negative ending chapter.")); if (chapter2 > 200) errors.push_back (translate ("High ending chapter.")); verse2 = convert_to_int (request->post ["verse2"]); if (chapter2 < 0) errors.push_back (translate ("Negative ending verse.")); if (chapter2 > 200) errors.push_back (translate ("High ending verse.")); int start = filter_passage_to_integer (Passage ("", book1, chapter1, convert_to_string (verse1))); int end = filter_passage_to_integer (Passage ("", book2, chapter2, convert_to_string (verse2))); if (start > end) { errors.push_back (translate ("The starting passage is beyond the ending passage.")); } database_imageresources.assign (name, image, book1, chapter1, verse1, book2, chapter2, verse2); Database_Volatile::setValue (userid, "imageresources", convert_to_string (end)); error = filter_string_implode (errors, " "); if (errors.empty ()) { redirect_browser (request, filter_url_build_http_query (resource_image_url (), "name", name)); return ""; } } // Retrieve passage range for this image. database_imageresources.get (name, image, book1, chapter1, verse1, book2, chapter2, verse2); if ((book1 == 0) || (book2 == 0)) { string end = Database_Volatile::getValue (userid, "imageresources"); Passage passage = filter_integer_to_passage (convert_to_int (end)); book1 = book2 = passage.book; chapter1 = chapter2 = passage.chapter; verse1 = verse2 = convert_to_int (passage.verse); if (book1 == 0) book1 = 1; if (book2 == 0) book2 = 1; } view.set_variable ("book1", Database_Books::getEnglishFromId (book1)); view.set_variable ("chapter1", convert_to_string (chapter1)); view.set_variable ("verse1", convert_to_string (verse1)); view.set_variable ("book2", Database_Books::getEnglishFromId (book2)); view.set_variable ("chapter2", convert_to_string (chapter2)); view.set_variable ("verse2", convert_to_string (verse2)); view.set_variable ("success", success); view.set_variable ("error", error); page += view.render ("resource", "img"); page += Assets_Page::footer (); return page; }
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 search_strong (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; Database_Kjv database_kjv = Database_Kjv (); string bible = request->database_config_user()->getBible (); if (request->query.count ("b")) { bible = request->query ["b"]; } if (request->query.count ("load")) { int book = Ipc_Focus::getBook (request); int chapter = Ipc_Focus::getChapter (request); int verse = Ipc_Focus::getVerse (request); // Get Strong's numbers, plus English snippets. string html = "<table>\n"; vector <Database_Kjv_Item> details = database_kjv.getVerse (book, chapter, verse); for (auto & detail : details) { string strong = detail.strong; string english = detail.english; html += "<tr><td><a href=\"" + strong + "\">" + strong + "</a></td><td>" + english + "</td></tr>\n"; } html += "</table>\n"; return html; } if (request->query.count ("strong")) { string strong = request->query ["strong"]; strong = filter_string_trim (strong); vector <int> passages; vector <Passage> details = database_kjv.searchStrong (strong); for (auto & passage : details) { int i_passage = filter_passage_to_integer (passage); passages.push_back (i_passage); } passages = array_unique (passages); sort (passages.begin(), passages.end()); string output; for (auto & passage : passages) { if (!output.empty()) output.append ("\n"); output.append (convert_to_string (passage)); } return output; } if (request->query.count ("id")) { int id = convert_to_int (request->query ["id"]); // Get the and passage for this identifier. Passage passage = filter_integer_to_passage (id); int book = passage.book; int chapter = passage.chapter; string verse = passage.verse; // Get the plain text. string text = search_logic_get_bible_verse_text (bible, book, chapter, convert_to_int (verse)); // Format it. string link = filter_passage_link_for_opening_editor_at (book, chapter, verse); string output = "<div>" + link + " " + text + "</div>"; // Output to browser. return output; } string page; Assets_Header header = Assets_Header (translate("Search"), request); header.setNavigator (); header.addBreadCrumb (menu_logic_search_menu (), menu_logic_search_text ()); page = header.run (); Assets_View view; view.set_variable ("bible", bible); string script = "var searchBible = \"" + bible + "\";"; view.set_variable ("script", script); page += view.render ("search", "strong"); page += Assets_Page::footer (); return page; }
void sendreceive_resources () { if (sendreceive_resources_watchdog) { int time = filter_date_seconds_since_epoch (); if (time < (sendreceive_resources_watchdog + 900)) { return; } Database_Logs::log ("Resources: " + translate("Watchdog timeout"), Filter_Roles::translator ()); sendreceive_resources_done (); } // If any of the prioritized synchronization tasks run, postpone the current task and do not start it. if (sendreceive_logic_prioritized_task_is_active ()) { sendreceive_resources_done (); this_thread::sleep_for (chrono::seconds (5)); tasks_logic_queue (SYNCRESOURCES); return; } sendreceive_resources_interrupt = false; // If there's nothing to cache, bail out. vector <string> resources = Database_Config_General::getResourcesToCache (); if (resources.empty ()) return; sendreceive_resources_kick_watchdog (); // Error counter. int error_count = 0; // Resource to cache. string resource = resources [0]; // Erase the two older storage locations that were used to cache resources in earlier versions of Bibledit. { Database_OfflineResources database_offlineresources; Database_UsfmResources database_usfmresources; database_offlineresources.erase (resource); database_usfmresources.deleteResource (resource); } Database_Logs::log ("Starting to install resource:" " " + resource, Filter_Roles::consultant ()); Database_Versifications database_versifications; vector <int> books = database_versifications.getMaximumBooks (); for (auto & book : books) { sendreceive_resources_delay_during_prioritized_tasks (); if (sendreceive_resources_interrupt) continue; // Database layout is per book: Create a database for this book. Database_Cache::create (resource, book); // Last downloaded passage in a previous download operation. int last_downloaded_passage = 0; { pair <int, int> progress = Database_Cache::progress (resource, book); int chapter = progress.first; int verse = progress.second; Passage passage ("", book, chapter, convert_to_string (verse)); last_downloaded_passage = filter_passage_to_integer (passage); } // List of passages recorded in the database that had errors on a previous download operation. vector <int> previous_errors; { vector <pair <int, int> > errors = Database_Cache::errors (resource, book); for (auto & element : errors) { int chapter = element.first; int verse = element.second; Passage passage ("", book, chapter, convert_to_string (verse)); int numeric_error = filter_passage_to_integer (passage); previous_errors.push_back (numeric_error); } } string bookName = Database_Books::getEnglishFromId (book); vector <int> chapters = database_versifications.getMaximumChapters (book); for (auto & chapter : chapters) { sendreceive_resources_delay_during_prioritized_tasks (); if (sendreceive_resources_interrupt) continue; bool downloaded = false; string message = resource + ": " + bookName + " chapter " + convert_to_string (chapter); vector <int> verses = database_versifications.getMaximumVerses (book, chapter); for (auto & verse : verses) { sendreceive_resources_delay_during_prioritized_tasks (); if (sendreceive_resources_interrupt) continue; // Numeric representation of passage to deal with. Passage passage ("", book, chapter, convert_to_string (verse)); int numeric_passage = filter_passage_to_integer (passage); // Conditions to download this verse: // 1. The passage is past the last downloaded passage. bool download_verse_past = numeric_passage > last_downloaded_passage; // 2. The passage was recorded as an error in a previous download operation. bool download_verse_error = in_array (numeric_passage, previous_errors); // Whether to download the verse. if (download_verse_past || download_verse_error) { // Fetch the text for the passage. bool server_is_installing_module = false; int wait_iterations = 0; string html, error; do { // Fetch this resource from the server. string address = Database_Config_General::getServerAddress (); int port = Database_Config_General::getServerPort (); // If the client has not been connected to a cloud instance, // fetch the resource from the Bibledit Cloud demo. if (!client_logic_client_enabled ()) { address = demo_address (); port = demo_port (); } string url = client_logic_url (address, port, sync_resources_url ()); url = filter_url_build_http_query (url, "r", filter_url_urlencode (resource)); url = filter_url_build_http_query (url, "b", convert_to_string (book)); url = filter_url_build_http_query (url, "c", convert_to_string (chapter)); url = filter_url_build_http_query (url, "v", convert_to_string (verse)); error.clear (); html = filter_url_http_get (url, error); server_is_installing_module = (html == sword_logic_installing_module_text ()); if (server_is_installing_module) { Database_Logs::log ("Waiting while Bibledit Cloud installs the requested SWORD module"); this_thread::sleep_for (chrono::seconds (60)); wait_iterations++; } } while (server_is_installing_module && (wait_iterations < 5)); // Record the passage as having been done in case it was a regular download, // rather than one to retry a previous download error. if (download_verse_past) Database_Cache::progress (resource, book, chapter, verse); // Clear the registered error in case the verse download corrects it. if (download_verse_error) Database_Cache::error (resource, book, chapter, verse, false); if (error.empty ()) { // Cache the verse data. if (!Database_Cache::exists (resource, book)) Database_Cache::create (resource, book); Database_Cache::cache (resource, book, chapter, verse, html); } else { // Record an error. Database_Cache::error (resource, book, chapter, verse, true); if (message.find (error) == string::npos) { message.append ("; " + error); } error_count++; this_thread::sleep_for (chrono::seconds (1)); } downloaded = true; } sendreceive_resources_kick_watchdog (); } message += "; done"; if (downloaded) Database_Logs::log (message, Filter_Roles::manager ()); } } // Done. if (error_count) { string msg = "Error count while downloading resource: " + convert_to_string (error_count); Database_Logs::log (msg, Filter_Roles::consultant ()); } Database_Logs::log ("Completed installing resource:" " " + resource, Filter_Roles::consultant ()); // In case of too many errors, schedule the resource download again. bool re_schedule_download = false; if (error_count) { if (!sendreceive_resources_interrupt) { re_schedule_download = true; Database_Logs::log ("Errors: Re-scheduling resource installation", Filter_Roles::consultant ()); } } // Store new download schedule. resources = Database_Config_General::getResourcesToCache (); resources = filter_string_array_diff (resources, {resource}); if (re_schedule_download) { resources.push_back (resource); } Database_Config_General::setResourcesToCache (resources); sendreceive_resources_done (); sendreceive_resources_interrupt = false; // If there's another resource waiting to be cached, schedule it for caching. if (!resources.empty ()) tasks_logic_queue (SYNCRESOURCES); }
string search_similar (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; Database_Volatile database_volatile = Database_Volatile (); int myIdentifier = filter_string_user_identifier (request); string bible = request->database_config_user()->getBible (); if (request->query.count ("b")) { bible = request->query ["b"]; } if (request->query.count ("load")) { int book = Ipc_Focus::getBook (request); int chapter = Ipc_Focus::getChapter (request); int verse = Ipc_Focus::getVerse (request); // Text of the focused verse in the active Bible. // Remove all punctuation from it. string versetext = search_logic_get_bible_verse_text (bible, book, chapter, verse); vector <string> punctuation = filter_string_explode (Database_Config_Bible::getSentenceStructureEndPunctuation (bible), ' '); for (auto & sign : punctuation) { versetext = filter_string_str_replace (sign, "", versetext); } punctuation = filter_string_explode (Database_Config_Bible::getSentenceStructureMiddlePunctuation (bible), ' '); for (auto & sign : punctuation) { versetext = filter_string_str_replace (sign, "", versetext); } versetext = filter_string_trim (versetext); database_volatile.setValue (myIdentifier, "searchsimilar", versetext); return versetext; } if (request->query.count ("words")) { string words = request->query ["words"]; words = filter_string_trim (words); database_volatile.setValue (myIdentifier, "searchsimilar", words); vector <string> vwords = filter_string_explode (words, ' '); // Include items if there are no more search hits than 30% of the total number of verses in the Bible. size_t maxcount = round (0.3 * search_logic_get_verse_count (bible)); // Store how often a verse occurs in an array. // The keys are the identifiers of the search results. // The values are how often the identifiers occur in the entire focused verse. map <int, int> identifiers; for (auto & word : vwords) { // Find out how often this word occurs in the Bible. Skip if too often. vector <Passage> passages = search_logic_search_bible_text (bible, word); if (passages.size () > maxcount) continue; // Store the identifiers and their count. for (auto & passage : passages) { int id = filter_passage_to_integer (passage); if (identifiers.count (id)) identifiers [id]++; else identifiers [id] = 1; } } // Sort on occurrence from high to low. // Skip identifiers that only occur once. vector <int> ids; vector <int> counts; for (auto & element : identifiers) { int id = element.first; int count = element.second; if (count <= 1) continue; ids.push_back (id); counts.push_back (count); } quick_sort (counts, ids, 0, counts.size()); reverse (ids.begin(), ids.end()); // Output the passage identifiers to the browser. string output; for (auto & id : ids) { if (!output.empty ()) output.append ("\n"); output.append (convert_to_string (id)); } return output; } if (request->query.count ("id")) { int id = convert_to_int (request->query ["id"]); // Get the Bible and passage for this identifier. Passage passage = filter_integer_to_passage (id); string bible = request->database_config_user()->getBible (); // string bible = passage.bible; int book = passage.book; int chapter = passage.chapter; string verse = passage.verse; // Get the plain text. string text = search_logic_get_bible_verse_text (bible, book, chapter, convert_to_int (verse)); // Get search words. vector <string> words = filter_string_explode (database_volatile.getValue (myIdentifier, "searchsimilar"), ' '); // Format it. string link = filter_passage_link_for_opening_editor_at (book, chapter, verse); text = filter_string_markup_words (words, text); string output = "<div>" + link + " " + text + "</div>"; // Output to browser. return output; } string page; Assets_Header header = Assets_Header (translate("Search"), request); header.setNavigator (); header.addBreadCrumb (menu_logic_search_menu (), menu_logic_search_text ()); page = header.run (); Assets_View view; view.set_variable ("bible", bible); string script = "var searchBible = \"" + bible + "\";"; view.set_variable ("script", script); page += view.render ("search", "similar"); page += Assets_Page::footer (); return page; }