// The client runs this function to fetch a general resource $name from the Cloud, // or from its local cache, // and to update the local cache with the fetched content, if needed, // and to return the requested content. string resource_logic_client_fetch_cache_from_cloud (string resource, int book, int chapter, int verse) { // Ensure that the cache for this resource exists on the client. if (!Database_Cache::exists (resource, book)) { Database_Cache::create (resource, book); } // If the content exists in the cache, return that content. if (Database_Cache::exists (resource, book, chapter, verse)) { return Database_Cache::retrieve (resource, book, chapter, verse); } // Fetch this resource from Bibledit Cloud or from the cache. string address = Database_Config_General::getServerAddress (); int port = Database_Config_General::getServerPort (); if (!client_logic_client_enabled ()) { // If the client has not been connected to a cloud instance, // fetch the resource from the Bibledit Cloud demo. 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)); string error; string content = filter_url_http_get (url, error); if (error.empty ()) { // No error: Cache content. Database_Cache::cache (resource, book, chapter, verse, content); } else { // Error: Log it, and return it. Database_Logs::log (resource + ": " + error); content.append (error); } // Done. return content; }
// Add "parameter" and "value" to be added in the base query, or base url. // If any $query is passed, if Cancel is clicked in this dialog, it should go go back // to the original caller page with the $query added. // Same for when a selection is made: It adds the $query to the page where to go. void Dialog_Color::add_query (string parameter, string value) { base_url = filter_url_build_http_query (base_url, parameter, value); }
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; }
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); }
map <int, string> workspace_get_values (void * webserver_request, int selector, bool use) { Webserver_Request * request = (Webserver_Request *) webserver_request; map <int, string> values; string workspace = workspace_get_active_name (request); string rawvalue; if (selector == URLS) rawvalue = request->database_config_user()->getWorkspaceURLs (); if (selector == WIDTHS) rawvalue = request->database_config_user()->getWorkspaceWidths (); if (selector == HEIGHTS) rawvalue = request->database_config_user()->getWorkspaceHeights (); if (selector == ENTIREWIDTH) rawvalue = request->database_config_user()->getEntireWorkspaceWidths (); vector <string> lines = filter_string_explode (rawvalue, '\n'); for (auto & line : lines) { if (line.find (workspace + "_") == 0) { vector <string> bits = filter_string_explode (line, '_'); if (bits.size() == 3) { int key = convert_to_int (bits [1]); string value = bits [2]; values [key] = value; } } } if (values.empty ()) { if (selector == URLS) values = workspace_get_default_urls (0); if (selector == WIDTHS) values = workspace_get_default_widths (0); if (selector == HEIGHTS) values = workspace_get_default_heights (0); if (selector == ENTIREWIDTH) values.clear (); } for (auto & element : values) { if ((selector == URLS) && use) { // Add query value for suppressing the topbar as the workspace already has one. if (element.second != "") { element.second = filter_url_build_http_query (element.second, "topbar", "0"); } // Transform the internal URLs to full ones. vector <string> bits = filter_string_explode (element.second, '/'); if (bits.size() == 2) { element.second.insert (0, "/"); } // Encode URL. element.second = filter_string_str_replace (" ", "%20", element.second); } if (selector == WIDTHS) { // Fix the units. element.second = workspace_process_units (element.second); } if (selector == HEIGHTS) { // Fix the units. element.second = workspace_process_units (element.second); } } return values; }
string sword_logic_get_text (string source, string module, int book, int chapter, int verse) { #ifdef HAVE_CLIENT // Client checks for and optionally creates the cache for this SWORD module. if (!Database_Cache::exists (module, book)) { Database_Cache::create (module, book); } // If this module/passage exists in the cache, return it (it updates the access days in the cache). if (Database_Cache::exists (module, book, chapter, verse)) { return Database_Cache::retrieve (module, book, chapter, verse); } // Fetch this SWORD resource from the server. string address = Database_Config_General::getServerAddress (); int port = Database_Config_General::getServerPort (); if (!client_logic_client_enabled ()) { // If the client has not been connected to a cloud instance, // fetch the SWORD content from the Bibledit Cloud demo. address = demo_address (); port = demo_port (); } string url = client_logic_url (address, port, sync_resources_url ()); string resource = "[" + source + "][" + module + "]"; url = filter_url_build_http_query (url, "r", 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)); string error; string html = filter_url_http_get (url, error, true); // In case of an error, don't cache that error, but let the user see it. if (!error.empty ()) return error; // Client caches this info for later. // Except in case of predefined responses from the Cloud. if (html != sword_logic_installing_module_text ()) { if (html != sword_logic_fetch_failure_text ()) { Database_Cache::cache (module, book, chapter, verse, html); } } return html; #else string module_text; bool module_available = false; string osis = Database_Books::getOsisFromId (book); string chapter_verse = convert_to_string (chapter) + ":" + convert_to_string (verse); // See notes on function sword_logic_diatheke // for why it is not currently fetching content via a SWORD library call. // module_text = sword_logic_diatheke (module, osis, chapter, verse, module_available); // Running diatheke only works when it runs in the SWORD installation directory. string sword_path = sword_logic_get_path (); // Running several instances of diatheke simultaneously fails. sword_logic_diatheke_run_mutex.lock (); // The server fetches the module text as follows: // diatheke -b KJV -k Jn 3:16 int result = filter_shell_vfork (module_text, sword_path, "diatheke", "-b", module.c_str(), "-k", osis.c_str(), chapter_verse.c_str()); sword_logic_diatheke_run_mutex.unlock (); if (result != 0) return sword_logic_fetch_failure_text (); // Touch the cache so the server knows that the module has been accessed just now. string url = sword_logic_virtual_url (module, 0, 0, 0); database_filebased_cache_get (url); // If the module has not been installed, the output of "diatheke" will be empty. // If the module was installed, but the requested passage is out of range, // the output of "diatheke" contains the module name, so it won't be empty. module_available = !module_text.empty (); if (!module_available) { // Check whether the SWORD module exists. vector <string> modules = sword_logic_get_available (); string smodules = filter_string_implode (modules, ""); if (smodules.find ("[" + module + "]") != string::npos) { // Schedule SWORD module installation. // (It used to be the case that this function, to get the text, // would wait till the SWORD module was installed, and then after installation, // return the text from that module. // But due to long waiting on Bibledit demo, while it would install multiple modules, // the Bibledit demo would become unresponsive. // So, it's better to return immediately with an informative text.) sword_logic_install_module_schedule (source, module); // Return standard 'installing' information. Client knows not to cache this. return sword_logic_installing_module_text (); } else { return "Cannot find SWORD module " + module; } } // Remove any OSIS elements. filter_string_replace_between (module_text, "<", ">", ""); // Remove the passage name that diatheke adds. // A reliable signature for this is the chapter and verse plus subsequent colon. size_t pos = module_text.find (" " + chapter_verse + ":"); if (pos != string::npos) { pos += 2; pos += chapter_verse.size (); module_text.erase (0, pos); } // Remove the module name that diatheke adds. module_text = filter_string_str_replace ("(" + module + ")", "", module_text); // Clean whitespace away. module_text = filter_string_trim (module_text); return module_text; #endif return ""; }