// Gets the raw USFM for the bible and passage given. string search_logic_get_bible_verse_usfm (string bible, int book, int chapter, int verse) { vector <string> texts; string path = search_logic_chapter_file (bible, book, chapter); string index = filter_url_file_get_contents (path); vector <string> lines = filter_string_explode (index, '\n'); int index_verse = 0; bool read_index_verse = false; int index_item = 0; for (auto & line : lines) { if (read_index_verse) { index_verse = convert_to_int (line); read_index_verse = false; } else if (line == search_logic_verse_separator ()) { read_index_verse = true; index_item = 0; } else if (line == search_logic_index_separator ()) { index_item++; } else if (index_item == USFM_RAW) { if (verse == index_verse) { texts.push_back (line); } } } return filter_string_implode (texts, "\n"); }
// This function takes the abbreviations stored for a Bible as $data. // It then sorts them out. // It adds the Bible books not in the $data. // It sorts everything in the standard canonical order. // It presents everything for display to the user for editing and update. string filter_abbreviations_display (string data) { vector <pair <int, string> > v_data = filter_abbreviations_read (data); vector <string> v_output; // Check for books, order them, supply missing ones. vector <int> books = Database_Books::getIDs (); for (int book : books) { bool found = false; for (auto element : v_data) { int bookId = element.first; string abbreviation = element.second; if (book == bookId) { v_output.push_back (Database_Books::getEnglishFromId (book) + " = " + abbreviation); found = true; } } if (!found) { v_output.push_back (Database_Books::getEnglishFromId (book) + " = "); } } // Get data as a string for display. return filter_string_implode (v_output, "\n"); }
// Returns the verse text given a $verse_number and $usfm code. // Handles combined verses. string usfm_get_verse_text (string usfm, int verse_number) { vector <string> result; bool hit = (verse_number == 0); vector <string> lines = filter_string_explode (usfm, '\n'); for (string line : lines) { vector <int> verses = usfm_get_verse_numbers (line); if (verse_number == 0) { if (verses.size () != 1) hit = false; if (hit) result.push_back (line); } else { if (in_array (verse_number, verses)) { // Desired verse found. hit = true; } else if (verses.size () == 1) { // No verse found: No change in situation. } else { // Outside desired verse. hit = false; } if (hit) result.push_back (line); } } // Return the verse text. string verseText = filter_string_implode (result, "\n"); return verseText; }
// Takes the passage in $text, and explodes it into book, chapter, verse. // The book is the numerical identifier, not the string, e.g., // it would not return "Genesis", but identifier 1. Passage filter_passage_explode_passage (string text) { text = filter_passage_clean_passage (text); // Cut the text in its parts. vector <string> bits = filter_string_explode (text, ' '); // Defaults to empty passage. Passage passage; // Take the bits. if (!bits.empty ()) { string verse = bits.back (); if (!verse.empty ()) passage.verse = verse; bits.pop_back (); } if (!bits.empty ()) { string chapter = bits.back (); if (!chapter.empty()) passage.chapter = convert_to_int (chapter); bits.pop_back (); } string book = filter_string_implode (bits, " "); if (!book.empty()) { int bk = filter_passage_interpret_book (book); passage.book = bk; } // Return the result. return passage; }
void client_logic_create_note_decode (string data, string& bible, int& book, int& chapter, int& verse, string& summary, string& contents, bool& raw) { vector <string> lines = filter_string_explode (data, '\n'); if (!lines.empty ()) { bible = lines [0]; lines.erase (lines.begin()); } if (!lines.empty ()) { book = convert_to_int (lines [0]); lines.erase (lines.begin()); } if (!lines.empty ()) { chapter = convert_to_int (lines [0]); lines.erase (lines.begin()); } if (!lines.empty ()) { verse = convert_to_int (lines [0]); lines.erase (lines.begin()); } if (!lines.empty ()) { summary = lines [0]; lines.erase (lines.begin()); } if (!lines.empty ()) { raw = convert_to_bool (lines [0]); lines.erase (lines.begin()); } contents = filter_string_implode (lines, "\n"); }
// Deliver the created CSS. // $path: If given, it saves the CSS to $path. // The function returns the CSS as a string. string Styles_Css::css (string path) { string css = filter_string_implode (code, "\n"); if (path != "") { filter_url_file_put_contents (path, css); } return css; }
string Editor_Html2Usfm::get () { // Generate the USFM as one string. string usfm = filter_string_implode (output, "\n"); usfm = cleanUSFM (usfm); return usfm; }
// Gets the human-readable name of a $line like this: // [CrossWire] *[Shona] (1.1) - Shona Bible string sword_logic_get_name (string line) { vector <string> bits = filter_string_explode (line, '-'); if (bits.size () >= 2) { bits.erase (bits.begin ()); } line = filter_string_implode (bits, "-"); line = filter_string_trim (line); return line; }
void client_logic_usfm_resources_update () { // The Cloud stores the list of USFM resources. // It is stored in the client files area. // Clients can access it from there. string path = client_logic_usfm_resources_path (); Database_UsfmResources database_usfmresources; vector <string> resources = database_usfmresources.getResources (); filter_url_file_put_contents (path, filter_string_implode (resources, "\n")); }
void workspace_delete (void * webserver_request, string workspace) { Webserver_Request * request = (Webserver_Request *) webserver_request; string rawvalue; vector <string> currentlines; vector <string> newlines; rawvalue = request->database_config_user()->getWorkspaceURLs (); currentlines = filter_string_explode (rawvalue, '\n'); newlines.clear (); for (auto & line : currentlines) { if (line.find (workspace + "_") != 0) newlines.push_back (line); } rawvalue = filter_string_implode (newlines, "\n"); request->database_config_user()->setWorkspaceURLs (rawvalue); rawvalue = request->database_config_user()->getWorkspaceWidths (); currentlines = filter_string_explode (rawvalue, '\n'); newlines.clear (); for (auto & line : currentlines) { if (line.find (workspace + "_") != 0) newlines.push_back (line); } rawvalue = filter_string_implode (newlines, "\n"); request->database_config_user()->setWorkspaceWidths (rawvalue); rawvalue = request->database_config_user()->getWorkspaceHeights (); currentlines = filter_string_explode (rawvalue, '\n'); newlines.clear (); for (auto & line : currentlines) { if (line.find (workspace + "_") != 0) newlines.push_back (line); } rawvalue = filter_string_implode (newlines, "\n"); request->database_config_user()->setWorkspaceHeights (rawvalue); request->database_config_user()->setActiveWorkspace (""); // For a client, store the setting for sending to the server. workspace_cache_for_cloud (request, true, true, true); }
string client_logic_create_note_encode (string bible, int book, int chapter, int verse, string summary, string contents, bool raw) { vector <string> data; data.push_back (bible); data.push_back (convert_to_string (book)); data.push_back (convert_to_string (chapter)); data.push_back (convert_to_string (verse)); data.push_back (summary); data.push_back (convert_to_string (raw)); data.push_back (contents); return filter_string_implode (data, "\n"); }
// Export data. string Database_Versifications::output (const string& name) { vector <string> lines; vector <Passage> versification_data = getBooksChaptersVerses (name); for (Passage & passage : versification_data) { string line = Database_Books::getEnglishFromId (passage.book); line.append (" "); line.append (convert_to_string (passage.chapter)); line.append (":"); line.append (passage.verse); lines.push_back (line); } return filter_string_implode (lines, "\n"); }
string index_listing (void * webserver_request, string url) { string page; page = Assets_Page::header ("Bibledit", webserver_request); // No breadcrumbs because the user can arrive here from more than one place. Assets_View view; url = filter_url_urldecode (url); url = filter_url_create_path ("", url); url = filter_string_str_replace ("\\", "/", url); view.set_variable ("url", url); string parent = filter_url_dirname_web (url); if (parent.length () > 1) { view.enable_zone ("parent"); view.set_variable ("parent", parent); } string directory = filter_url_create_root_path (url); if (!file_or_dir_exists (directory) || filter_url_is_dir (directory)) { vector <string> files = filter_url_scandir (directory); for (auto & file : files) { string path = filter_url_create_path (directory, file); string line; line.append ("<tr>"); line.append ("<td>"); line.append ("<a href=\"" + filter_url_create_path (url, file) + "\">"); line.append (file); line.append ("</a>"); line.append ("</td>"); line.append ("<td>"); if (!filter_url_is_dir (path)) { line.append (convert_to_string (filter_url_filesize (path))); } line.append ("</td>"); line.append ("</tr>"); file = line; } string listing = filter_string_implode (files, "\n"); if (listing.empty ()) listing = translate ("No files in this folder"); else { listing.insert (0, "<table>"); listing.append ("</table>"); } view.set_variable ("listing", listing); } else { string filename = filter_url_create_root_path (url); return filter_url_file_get_contents (filename); } page += view.render ("index", "listing"); page += Assets_Page::footer (); return page; }
// Returns the USFM text for a range of verses for the input $usfm code. // It handles combined verses. // It ensures that the $exclude_usfm does not make it to the output of the function. string usfm_get_verse_range_text (string usfm, int verse_from, int verse_to, const string& exclude_usfm) { vector <string> bits; string previous_usfm; for (int vs = verse_from; vs <= verse_to; vs++) { string verse_usfm = usfm_get_verse_text (usfm, vs); // Do not include repeating USFM in the case of combined verse numbers in the input USFM code. if (verse_usfm == previous_usfm) continue; previous_usfm = verse_usfm; // In case of combined verses, the excluded USFM should not be included in the result. if (verse_usfm != exclude_usfm) { bits.push_back (verse_usfm); } } usfm = filter_string_implode (bits, "\n"); return usfm; }
// Queue task $command to run later, with $parameters for that task. void tasks_logic_queue (string command, vector <string> parameters) { // The file on disk will contain the command on the first line, // and any parameters on the following lines, one parameters per line. vector <string> lines; lines.push_back (command); lines.insert (lines.end(), parameters.begin(), parameters.end()); // The filename to write to contains seconds and microseconds. string seconds = convert_to_string (filter_date_seconds_since_epoch ()); string time = seconds + filter_string_fill (convert_to_string (filter_date_numerical_microseconds ()), 8, '0'); string file = filter_url_create_path (tasks_logic_folder (), time); // On Windows the microtime is not fine enough. // This leads to one task overwriting a previous one in case it is queued immediately after. // Deal with that problem here: Ensure the filename is unique. file = filter_url_unique_path (file); // Save it. command = filter_string_implode (lines, "\n"); filter_url_file_put_contents (file, command); }
string Database_Privileges::save (string username) { SqliteDatabase sql (database ()); vector <string> lines; lines.push_back (bibles_start ()); sql.add ("SELECT bible, book, write FROM bibles WHERE username ="******";"); map <string, vector <string> > result = sql.query (); vector <string> bible = result ["bible"]; vector <string> book = result ["book"]; vector <string> write = result ["write"]; for (size_t i = 0; i < bible.size (); i++) { lines.push_back (bible [i]); lines.push_back (book [i]); // It could have just stored 0 or 1 for the boolean values. // But if that were done, then there would be no change in the length of the file // when changing only boolean values. // And then the client would not re-download that file. // To use "on" and "off", that solves the issue. bool b = convert_to_bool (write[i]); if (b) lines.push_back (on ()); else lines.push_back (off ()); } lines.push_back (bibles_end ()); lines.push_back (features_start ()); sql.clear (); sql.add ("SELECT feature FROM features WHERE username ="******";"); result = sql.query (); vector <string> feature = result ["feature"]; for (size_t i = 0; i < feature.size (); i++) { lines.push_back (feature [i]); } lines.push_back (features_end ()); return filter_string_implode (lines, "\n"); }
void workspace_set_values (void * webserver_request, int selector, const map <int, string> & values) { // Store values locally, and for a client, store them also for sending to the server. Webserver_Request * request = (Webserver_Request *) webserver_request; 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> currentlines = filter_string_explode (rawvalue, '\n'); vector <string> newlines; for (auto & line : currentlines) { if (line.find (workspace + "_") != 0) { newlines.push_back (line); } } for (auto & element : values) { string line = workspace + "_" + convert_to_string (element.first) + "_" + element.second; newlines.push_back (line); } rawvalue = filter_string_implode (newlines, "\n"); if (selector == URLS) { request->database_config_user()->setWorkspaceURLs (rawvalue); workspace_cache_for_cloud (request, true, false, false); } if (selector == WIDTHS) { request->database_config_user()->setWorkspaceWidths (rawvalue); workspace_cache_for_cloud (request, false, true, false); } if (selector == HEIGHTS) { request->database_config_user()->setWorkspaceHeights (rawvalue); workspace_cache_for_cloud (request, false, false, true); } if (selector == ENTIREWIDTH) { request->database_config_user()->setEntireWorkspaceWidths (rawvalue); workspace_cache_for_cloud (request, false, true, false); } }
// This orders the workspacees. // It takes the order as in array $workspacees. void workspace_reorder (void * webserver_request, const vector <string> & workspacees) { // The order of the workspacees is taken from the URLs. // Widths and heights are not considered for the order. Webserver_Request * request = (Webserver_Request *) webserver_request; // Retrieve the old order of the workspacees, plus their details. string rawvalue = request->database_config_user()->getWorkspaceURLs (); vector <string> oldlines = filter_string_explode (rawvalue, '\n'); // Create vector with the sorted workspace definitions. vector <string> newlines; for (auto & workspace : workspacees) { for (auto & line : oldlines) { if (line.find (workspace + "_") == 0) { newlines.push_back (line); line.clear (); } } } // Add any extra ones. for (auto & line : oldlines) { if (!line.empty ()) { newlines.push_back (line); } } // Save everything. rawvalue = filter_string_implode (newlines, "\n"); request->database_config_user()->setWorkspaceURLs (rawvalue); // Schedule for sending to Cloud. workspace_cache_for_cloud (request, true, false, false); }
string resource_get (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; vector <string> bits; string s_resource = request->query["resource"]; string s_book = request->query["book"]; string s_chapter = request->query["chapter"]; string s_verse = request->query["verse"]; if (!s_resource.empty () && !s_book.empty () && !s_chapter.empty () && !s_verse.empty ()) { unsigned int resource = convert_to_int (s_resource); int book = convert_to_int (s_book); int chapter = convert_to_int (s_chapter); int verse = convert_to_int (s_verse); // In JavaScript the resource identifier starts at 1 instead of at 0. resource--; vector <string> resources = request->database_config_user()->getActiveResources (); if (resource <= resources.size ()) { s_resource = resources [resource]; // Handle a divider. if (resource_logic_is_divider (s_resource)) return resource_logic_get_divider (s_resource); string bible = request->database_config_user ()->getBible (); string versification = Database_Config_Bible::getVersificationSystem (bible); Database_Versifications database_versifications; vector <int> chapters = database_versifications.getChapters (versification, book); // Whether to add extra verse numbers, for clarity in case of viewing more than one verse. bool add_verse_numbers = false; int context_before = request->database_config_user ()->getResourceVersesBefore (); if (context_before) add_verse_numbers = true; int context_after = request->database_config_user ()->getResourceVersesAfter (); if (context_after) add_verse_numbers = true; // Context before the focused verse. vector <int> chapters_before; vector <int> verses_before; if (context_before > 0) { for (int ch = chapter - 1; ch <= chapter; ch++) { if (in_array (ch, chapters)) { vector <int> verses = database_versifications.getVerses (versification, book, ch); for (size_t vs = 0; vs < verses.size (); vs++) { int vs2 = verses [vs]; if ((ch < chapter) || (vs2 < verse)) { if (vs2 > 0) { chapters_before.push_back (ch); verses_before.push_back (verses[vs]); } } } } } while ((int)chapters_before.size () > context_before) { chapters_before.erase (chapters_before.begin ()); verses_before.erase (verses_before.begin ()); } } for (unsigned int i = 0; i < chapters_before.size (); i++) { bits.push_back (resource_logic_get_html (request, s_resource, book, chapters_before[i], verses_before[i], add_verse_numbers)); } // Focused verse. bits.push_back (resource_logic_get_html (request, s_resource, book, chapter, verse, add_verse_numbers)); // Context after the focused verse. vector <int> chapters_after; vector <int> verses_after; if (context_after > 0) { for (int ch = chapter; ch <= chapter + 1; ch++) { if (in_array (ch, chapters)) { vector <int> verses = database_versifications.getVerses (versification, book, ch); for (size_t vs = 0; vs < verses.size (); vs++) { int vs2 = verses [vs]; if ((ch > chapter) || (vs2 > verse)) { if (vs2 > 0) { chapters_after.push_back (ch); verses_after.push_back (verses[vs]); } } } } } while ((int)chapters_after.size () > context_after) { chapters_after.pop_back (); verses_after.pop_back (); } } for (unsigned int i = 0; i < chapters_after.size (); i++) { bits.push_back (resource_logic_get_html (request, s_resource, book, chapters_after[i], verses_after[i], add_verse_numbers)); } } } string page = filter_string_implode (bits, ""); // <br> return page; }
void Database_Config_General::setList (const char * key, vector <string> values) { string value = filter_string_implode (values, "\n"); setValue (key, value); }
string resource_cache (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; string resource = request->query ["resource"]; string page; Assets_Header header = Assets_Header (menu_logic_resources_text (), request); header.addBreadCrumb (menu_logic_settings_menu (), menu_logic_settings_text ()); page = header.run (); Assets_View view; if (request->query.count ("clear")) { sendreceive_resources_clear_all (); } vector <string> resources = Database_Config_General::getResourcesToCache (); if (!resources.empty ()) { view.enable_zone ("scheduled"); view.set_variable ("scheduled", filter_string_implode (resources, " | ")); } vector <string> listed_resources; string block; // Display the available USFM resources. resources = client_logic_usfm_resources_get (); for (auto & resource : resources) { block.append ("<p>"); block.append ("<a href=\"download?name=" + resource + "\">" + resource + "</a>"); block.append ("</p>\n"); } listed_resources.insert (listed_resources.end (), resources.begin (), resources.end ()); // Display the available external resources. resources = resource_external_names (); for (auto & resource : resources) { block.append ("<p>"); block.append ("<a href=\"download?name=" + resource + "\">" + resource + "</a>"); block.append ("</p>\n"); } listed_resources.insert (listed_resources.end (), resources.begin (), resources.end ()); // Display the available SWORD resources. resources = sword_logic_get_available (); for (auto & resource : resources) { string source = sword_logic_get_source (resource); string module = sword_logic_get_remote_module (resource); string name = "[" + source + "][" + module + "]"; block.append ("<p>"); block.append ("<a href=\"download?name=" + name + "\">" + resource + "</a>"); block.append ("</p>\n"); } listed_resources.insert (listed_resources.end (), resources.begin (), resources.end ()); // Display any old USFM resources still available on the client. Database_UsfmResources database_usfmresources; resources = database_usfmresources.getResources (); for (auto & resource : resources) { if (in_array (resource, listed_resources)) continue; block.append ("<p>"); block.append ("<a href=\"download?name=" + resource + "&old=yes\">" + resource + "</a>"); block.append ("</p>\n"); } // Display any offline resources still available on the client. Database_OfflineResources database_offlineresources; resources = database_offlineresources.names (); for (auto & resource : resources) { if (in_array (resource, listed_resources)) continue; block.append ("<p>"); block.append ("<a href=\"download?name=" + resource + "&old=yes\">" + resource + "</a>"); block.append ("</p>\n"); } // Display the list. view.set_variable ("block", block); page += view.render ("resource", "cache"); page += Assets_Page::footer (); return page; }
string lexicon_definition (void * webserver_request) { // Retrieve the id: It may contain a Strong's number or a lemma. Webserver_Request * request = (Webserver_Request *) webserver_request; string id = request->query["id"]; vector <string> renderings; if (!id.empty ()) { string letter = id.substr (0, 1); if (letter == HEBREW_ETCBC4_PREFIX) { // ETCBC4 database. // When a defintion is clicked for the second time, it gets erased. if (id != request->database_config_user ()->getRequestedEtcbc4Definition ()) { renderings.push_back (lexicon_logic_render_etcbc4_morphology (id)); } else { id.clear (); } request->database_config_user ()->setRequestedEtcbc4Definition (id); } else if (letter == KJV_LEXICON_PREFIX) { // King James Bible with Strong's numbers. if (id != request->database_config_user ()->getRequestedKjvDefinition ()) { Database_Kjv database_kjv; string strong = database_kjv.strong (convert_to_int (id.substr (1))); string rendering = lexicon_logic_render_definition (strong); if (!rendering.empty ()) renderings.push_back (rendering); } else { id.clear (); } request->database_config_user ()->setRequestedKjvDefinition (id); } else if (letter == MORPHHB_PREFIX) { // Open Scriptures Hebrew with Strong's numbers. if (id != request->database_config_user ()->getRequestedMorphHbDefinition ()) { Database_MorphHb database_morphhb; string parsing = database_morphhb.parsing (convert_to_int (id.substr (1))); vector <string> strongs; vector <string> bdbs; lexicon_logic_convert_morphhb_parsing_to_strong (parsing, strongs, bdbs); for (size_t i = 0; i < strongs.size (); i++) { string rendering = lexicon_logic_render_definition (strongs[i]); if (!rendering.empty ()) renderings.push_back (rendering); rendering = "<a href=\"" BDB_PREFIX + bdbs[i] + "\">Brown Driver Briggs</a>"; renderings.push_back (rendering); } } else { id.clear (); } request->database_config_user ()->setRequestedMorphHbDefinition (id); } else if (letter == SBLGNT_PREFIX) { // SBL Greek New Testament plus morphology. if (id != request->database_config_user ()->getRequestedSblGntDefinition ()) { Database_MorphGnt database_morphgnt; Database_Strong database_strong; int rowid = convert_to_int (id.substr (1)); // The part of speech. string pos = database_morphgnt.pos (rowid); string rendering = lexicon_logic_render_morphgnt_part_of_speech (pos); rendering.append (" "); // The parsing. string parsing = database_morphgnt.parsing (rowid); rendering.append (lexicon_logic_render_morphgnt_parsing_code (parsing)); renderings.push_back (rendering); // The lemma. string lemma = database_morphgnt.lemma (rowid); vector <string> strongs = database_strong.strong (lemma); for (auto & id : strongs) { rendering = lexicon_logic_render_definition (id); if (!rendering.empty ()) renderings.push_back (rendering); } } else { id.clear (); } request->database_config_user ()->setRequestedSblGntDefinition (id); } else if (letter == "H") { // Strong's Hebrew. if (id != request->database_config_user ()->getRequestedHDefinition ()) { string rendering = lexicon_logic_render_definition (id); if (!rendering.empty ()) renderings.push_back (rendering); } else { id.clear (); } request->database_config_user ()->setRequestedHDefinition (id); } else if (letter == "G") { // Strong's Greek. if (id != request->database_config_user ()->getRequestedGDefinition ()) { string rendering = lexicon_logic_render_definition (id); if (!rendering.empty ()) renderings.push_back (rendering); } else { id.clear (); } request->database_config_user ()->setRequestedGDefinition (id); } else if (letter == BDB_PREFIX) { // Brown Driver Briggs lexicon. string rendering = lexicon_logic_render_bdb_entry (id.substr (1)); if (!rendering.empty ()) renderings.push_back (rendering); request->database_config_user ()->setRequestedMorphHbDefinition (""); } else { // Unknown definition request. renderings.push_back (id); } } return filter_string_implode (renderings, "<br>"); }
string resource_manage (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; string page; Assets_Header header = Assets_Header (translate("USFM Resources"), request); header.addBreadCrumb (menu_logic_settings_menu (), menu_logic_settings_text ()); page = header.run (); Assets_View view; Database_UsfmResources database_usfmresources = Database_UsfmResources (); // Delete resource. string remove = request->query ["delete"]; if (remove != "") { string confirm = request->query ["confirm"]; if (confirm == "") { Dialog_Yes dialog_yes = Dialog_Yes ("manage", translate("Would you like to delete this resource?")); dialog_yes.add_query ("delete", remove); page += dialog_yes.run (); return page; } if (confirm == "yes") { if (access_bible_write (request, remove)) { database_usfmresources.deleteResource (remove); // The Cloud updates the list of available USFM resources for the clients. tasks_logic_queue (LISTUSFMRESOURCES); } else { view.set_variable ("error", translate("You do not have write access to this resource")); } } } // Convert resource. string convert = request->query ["convert"]; if (convert != "") { string confirm = request->query ["confirm"]; if (confirm == "") { Dialog_Yes dialog_yes = Dialog_Yes ("manage", translate("Would you like to convert this resource to a Bible?")); dialog_yes.add_query ("convert", convert); page += dialog_yes.run (); return page; } if (confirm == "yes") { if (access_bible_write (request, convert)) { tasks_logic_queue (CONVERTRESOURCE2BIBLE, {convert}); redirect_browser (request, journal_index_url ()); return ""; } else { view.set_variable ("error", translate("Insufficient privileges")); } } } vector <string> resources = database_usfmresources.getResources (); vector <string> resourceblock; for (auto & resource : resources) { resourceblock.push_back ("<p>"); resourceblock.push_back ("<a href=\"?delete=" + resource + "\" class=\"deleteresource\" title=\"" + translate("Remove") + "\">"); resourceblock.push_back (emoji_wastebasket ()); resourceblock.push_back ("</a>"); resourceblock.push_back ("<a href=\"?convert=" + resource + "\" class=\"convertresource\" title=\"" + translate("Convert") + "\">"); resourceblock.push_back ("♻"); resourceblock.push_back ("</a>"); resourceblock.push_back (resource); resourceblock.push_back ("</p>"); } view.set_variable ("resourceblock", filter_string_implode (resourceblock, "\n")); page += view.render ("resource", "manage"); page += Assets_Page::footer (); return page; }
// This function runs the sprint burndown history logger for $bible. // If no $bible is passed, it will do all Bibles. // If $mail is true, it will mail the burndown chart to the subscribed users. // If $mail is false, it decides on its own whether to mail the chart to the users. void sprint_burndown (string bible, bool email) { int localseconds = filter_date_local_seconds (filter_date_seconds_since_epoch ()); int year = filter_date_numerical_year (localseconds); int month = filter_date_numerical_month (localseconds); int monthday = filter_date_numerical_month_day (localseconds); // 1 to 31. int weekday = filter_date_numerical_week_day (localseconds); // 0 (for Sunday) through 6 (for Saturday). int hour = filter_date_numerical_hour (localseconds); bool sprintstart = false; bool sprintfinish = false; // Every Friday at 2 PM (14:00h) it sends email about the sprint progress. if ((weekday == 5) && (hour == 14)) email = true; // On the first business day of the month, at 10 AM, send email about the start of the sprint. if (filter_date_is_first_business_day_of_month (monthday, weekday) && (hour == 10)) { email = true; sprintstart = true; } // On the last business day of the month, at 2 PM (14:00h), send email about the end of the sprint. if ((monthday == filter_date_get_last_business_day_of_month (year, month)) && (hour == 14)) { email = true; sprintfinish = true; } // Determine what to do, or to quit. if (!email && !sprintstart && !sprintfinish) { if (hour != 1) return; } Database_Logs::log ("Updating Sprint information", Filter_Roles::manager ()); Webserver_Request request; Database_Mail database_mail = Database_Mail (&request); Database_Sprint database_sprint = Database_Sprint (); // Determine year / month / day of the current sprint. // If this script runs from midnight till early morning, // it applies to the day before. // If the script runs during the day, it applies to today. if (hour <= 6) { localseconds -= (3600 * 6); } year = filter_date_numerical_year (localseconds); month = filter_date_numerical_month (localseconds); monthday = filter_date_numerical_month_day (localseconds); // 1 to 31. vector <string> bibles = {bible}; if (bible == "") { bibles = request.database_bibles()->getBibles (); } for (auto bible : bibles) { // Get the total number of tasks for this sprint, // and the average percentage of completion of them, // and store this information in the sprint history table. vector <int> ids = database_sprint.getTasks (bible, year, month); vector <int> percentages; for (auto id : ids) { percentages.push_back (database_sprint.getComplete (id)); } int tasks = ids.size (); int complete = 0; if (tasks != 0) { for (auto percentage : percentages) complete += percentage; complete = round ((float) complete / (float) tasks); } database_sprint.logHistory (bible, year, month, monthday, tasks, complete); // Send email if requested. if (email) { if (tasks) { // Only mail if the current sprint contains tasks. string scategories = Database_Config_Bible::getSprintTaskCompletionCategories (bible); vector <string> categories = filter_string_explode (scategories, '\n'); int category_count = categories.size(); int category_percentage = round (100 / category_count); vector <string> users = request.database_users ()->getUsers (); for (auto user : users) { if (request.database_config_user()->getUserSprintProgressNotification (user)) { string subject = translate("Team's progress in Sprint"); if (sprintstart) subject = translate("Sprint has started"); if (sprintfinish) subject = translate("Sprint has finished"); subject += " | " + bible; vector <string> body; body.push_back ("<h3>" + translate("Sprint Planning and Team's Progress") + " | " + bible + "</h3>"); body.push_back ("<table class='burndown'>"); vector <int> tasks = database_sprint.getTasks (bible, year, month); for (auto id : tasks) { body.push_back ("<tr>"); string title = database_sprint.getTitle (id); body.push_back ("<td>" + title + "</td>"); int complete = database_sprint.getComplete (id); string text; for (int i = 0; i < round (complete / category_percentage); i++) text.append ("▓"); for (int i = 0; i < category_count - round (complete / category_percentage); i++) text.append ("▁"); body.push_back ("<td>" + text + "</td>"); body.push_back ("</tr>"); } body.push_back ("</table>"); body.push_back ("<h3>" + translate("Sprint Burndown Chart - Remaining Tasks") + "</h3>"); string burndownchart = sprint_create_burndown_chart (bible, year, month); body.push_back ("<p>" + burndownchart + "</p>"); if (!body.empty ()) { string mailbody = filter_string_implode (body, "\n"); if (!client_logic_client_enabled ()) database_mail.send (user, subject, mailbody); } } } } else { // Since there are no tasks, no mail will be sent: Make a logbook entry. Database_Logs::log ("No tasks in this Sprint: No email was sent"); } } } }
// handleEmailNew - 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 created a new Consultation Note. bool Notes_Logic::handleEmailNew (string from, string subject, string body) { // Webserver request. Webserver_Request * request = (Webserver_Request *) webserver_request; // Store the original subject. string originalSubject = subject; // Check that the subject indicates that a new consultation note is to be created. size_t pos = unicode_string_casefold (subject).find ("new note"); if (pos == string::npos) return false; // There is a new note. Remove that bit from the subject. if (pos > 0) subject.erase (0, pos + 8); // Clean the subject line. subject = filter_string_trim (subject); subject = filter_string_str_replace (".", " ", subject); subject = filter_string_str_replace (":", " ", subject); subject = filter_string_str_replace (" ", " ", subject); subject = filter_string_str_replace (" ", " ", subject); // Check that the from address of the email belongs to an existing user. from = filter_string_extract_email (from); if (!request->database_users()->emailExists (from)) return false; string username = request->database_users()->getEmailToUser (from); // Extract book, chapter, verse, and note summary from the subject int book = -1; int chapter = -1; int verse = -1; string summary; vector <string> subjectlines = filter_string_explode (subject, ' '); if (!subjectlines.empty()) { book = filter_passage_interpret_book (subjectlines[0]); subjectlines.erase (subjectlines.begin()); } if (!subjectlines.empty()) { chapter = convert_to_int (subjectlines[0]); subjectlines.erase (subjectlines.begin()); } if (!subjectlines.empty()) { verse = convert_to_int (subjectlines[0]); subjectlines.erase (subjectlines.begin()); } summary = filter_string_implode (subjectlines, " "); // Check book, chapter, verse, and summary. Give feedback if there's anything wrong. string noteCheck; if (book <= 0) noteCheck.append (translate("Unknown book")); if (chapter < 0) { noteCheck.append (" "); noteCheck.append (translate("Unknown chapter")); } if (verse < 0) { noteCheck.append (" "); noteCheck.append (translate("Unknown verse")); } if (summary.empty ()) { noteCheck.append (" "); noteCheck.append (translate("Unknown summary")); } // Mail user if the note could not be posted. Database_Mail database_mail = Database_Mail (webserver_request); if (noteCheck != "") { subject = translate("Your new note could not be posted"); database_mail.send (username, subject + ": " + originalSubject, noteCheck); return false; } // Clean the email's body. body = filter_string_extract_body (body); // Post the note. string sessionuser = request->session_logic()->currentUser (); request->session_logic()->setUsername (username); Database_Notes database_notes = Database_Notes(webserver_request); string bible = request->database_config_user()->getBible (); int identifier = database_notes.storeNewNote (bible, book, chapter, verse, summary, body, false); handlerNewNote (identifier); request->session_logic()->setUsername (sessionuser); // Mail confirmation to the username. if (request->database_config_user()->getUserNotifyMeOfMyPosts (username)) { subject = translate("Your new note was posted"); database_mail.send (username, subject + ": " + originalSubject, body); } // Log operation. Database_Logs::log ("New note posted : " + body); // Job done. return true; }
string sync_changes (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; Sync_Logic sync_logic = Sync_Logic (webserver_request); Database_Modifications database_modifications; // Check on the credentials. if (!sync_logic.credentials_okay ()) return ""; // Bail out if the change notifications are not now available to clients. if (!config_globals_change_notifications_available) { request->response_code = 503; return ""; } // Client makes a prioritized server call: Record the client's IP address. sync_logic.prioritized_ip_address_record (); // Get the relevant parameters the client may have POSTed to us, the server. string user = hex2bin (request->post ["u"]); int action = convert_to_int (request->post ["a"]); int id = convert_to_int (request->post ["i"]); switch (action) { case Sync_Logic::changes_delete_modification: { // The server deletes the change notification. database_modifications.deleteNotification (id); Database_Logs::log ("Client deletes change notification from server: " + convert_to_string (id), Filter_Roles::translator ()); request->database_config_user ()->setChangeNotificationsChecksum (""); return ""; } case Sync_Logic::changes_get_checksum: { // The server responds with the possibly cached total checksum for the user's change notifications. string checksum = request->database_config_user ()->getChangeNotificationsChecksum (); if (checksum.empty ()) { checksum = Sync_Logic::changes_checksum (user); request->database_config_user ()->setChangeNotificationsChecksum (checksum); } return checksum; } case Sync_Logic::changes_get_identifiers: { // The server responds with the identifiers of all the user's change notifications. vector <int> ids = database_modifications.getNotificationIdentifiers (user, false); string response; for (auto & id : ids) { if (!response.empty ()) response.append ("\n"); response.append (convert_to_string (id)); } return response; } case Sync_Logic::changes_get_modification: { // The server responds with the relevant data of the requested modification. vector <string> lines; // category lines.push_back (database_modifications.getNotificationCategory (id)); // bible lines.push_back (database_modifications.getNotificationBible (id)); // book // chapter // verse Passage passage = database_modifications.getNotificationPassage (id); lines.push_back (convert_to_string (passage.book)); lines.push_back (convert_to_string (passage.chapter)); lines.push_back (passage.verse); // oldtext lines.push_back (database_modifications.getNotificationOldText (id)); // modification lines.push_back (database_modifications.getNotificationModification (id)); // newtext lines.push_back (database_modifications.getNotificationNewText (id)); // Result. return filter_string_implode (lines, "\n"); } } // Bad request. // Delay a while to obstruct a flood of bad requests. this_thread::sleep_for (chrono::seconds (1)); request->response_code = 400; return ""; }
string styles_indexm (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; string page; Assets_Header header = Assets_Header (translate("Styles"), webserver_request); header.addBreadCrumb (menu_logic_settings_menu (), menu_logic_settings_text ()); header.addBreadCrumb (menu_logic_settings_styles_menu (), menu_logic_styles_text ()); page = header.run (); Assets_View view; Database_Styles database_styles; string username = request->session_logic ()->currentUser (); int userlevel = request->session_logic ()->currentLevel (); if (request->post.count ("new")) { string name = request->post["entry"]; // Remove spaces at the ends of the name for the new stylesheet. // Because predictive keyboards can add a space to the name, // and the stylesheet system is not built for whitespace at the start / end of the name of the stylesheet. name = filter_string_trim (name); vector <string> existing = database_styles.getSheets (); if (find (existing.begin(), existing.end (), name) != existing.end ()) { page += Assets_Page::error (translate("This stylesheet already exists")); } else { database_styles.createSheet (name); database_styles.grantWriteAccess (username, name); styles_sheets_create_all (); page += Assets_Page::success (translate("The stylesheet has been created")); } } if (request->query.count ("new")) { Dialog_Entry dialog_entry = Dialog_Entry ("indexm", translate("Please enter the name for the new stylesheet"), "", "new", ""); page += dialog_entry.run(); return page; } if (request->query.count ("delete")) { string del = request->query ["delete"]; if (del != "") { string confirm = request->query ["confirm"]; if (confirm == "yes") { bool write = database_styles.hasWriteAccess (username, del); if (userlevel >= Filter_Roles::admin ()) write = true; if (write) { database_styles.deleteSheet (del); database_styles.revokeWriteAccess ("", del); page += Assets_Page::success (translate("The stylesheet has been deleted")); } } if (confirm == "") { Dialog_Yes dialog_yes = Dialog_Yes ("indexm", translate("Would you like to delete this stylesheet?")); dialog_yes.add_query ("delete", del); page += dialog_yes.run (); return page; } } } // Delete empty sheet that may have been there. database_styles.deleteSheet (""); vector <string> sheets = database_styles.getSheets(); vector <string> sheetblock; for (auto & sheet : sheets) { sheetblock.push_back ("<p>"); sheetblock.push_back (sheet); bool editable = database_styles.hasWriteAccess (username, sheet); if (userlevel >= Filter_Roles::admin ()) editable = true; // Cannot edit the Standard stylesheet. if (sheet == styles_logic_standard_sheet ()) editable = false; if (editable) { sheetblock.push_back ("<a href=\"sheetm?name=" + sheet + "\">[" + translate("edit") + "]</a>"); } sheetblock.push_back ("</p>"); } view.set_variable ("sheetblock", filter_string_implode (sheetblock, "\n")); page += view.render ("styles", "indexm"); page += Assets_Page::footer (); return page; }
string user_account (void * webserver_request) { Webserver_Request * request = (Webserver_Request *) webserver_request; string page; Assets_Header header = Assets_Header (translate("Account"), webserver_request); header.addBreadCrumb (menu_logic_settings_menu (), menu_logic_settings_text ()); page = header.run (); Assets_View view; string username = request->session_logic()->currentUser (); string email = request->database_users()->get_email (username); bool actions_taken = false; vector <string> success_messages; // Form submission handler. if (request->post.count ("submit")) { bool form_is_valid = true; string currentpassword = request->post ["currentpassword"]; string newpassword = request->post ["newpassword"]; string newpassword2 = request->post ["newpassword2"]; string newemail = request->post ["newemail"]; if ((newpassword != "") || (newpassword2 != "")) { if (newpassword.length () < 4) { form_is_valid = false; view.set_variable ("new_password_invalid_message", translate("Password should be at least four characters long")); } if (newpassword2.length () < 4) { form_is_valid = false; view.set_variable ("new_password2_invalid_message", translate("Password should be at least four characters long")); } if (newpassword2 != newpassword) { form_is_valid = false; view.set_variable ("new_password2_invalid_message", translate("Passwords do not match")); } if (!request->database_users()->matchUserPassword (username, currentpassword)) { form_is_valid = false; view.set_variable ("current_password_invalid_message", translate("Current password is not valid")); } if (form_is_valid) { request->database_users()->set_password (username, newpassword); actions_taken = true; success_messages.push_back (translate("The new password was saved")); } } if (newemail != "") { if (!filter_url_email_is_valid (newemail)) { form_is_valid = false; view.set_variable ("new_email_invalid_message", translate("Email address is not valid")); } if (!request->database_users()->matchUserPassword (username, currentpassword)) { form_is_valid = false; view.set_variable ("current_password_invalid_message", translate("Current password is not valid")); } if (form_is_valid) { Confirm_Worker confirm_worker = Confirm_Worker (webserver_request); string initial_subject = translate("Email address verification"); string initial_body = translate("Somebody requested to change the email address that belongs to your account."); string query = request->database_users()->updateEmailQuery (username, newemail); string subsequent_subject = translate("Email address change"); string subsequent_body = translate("The email address that belongs to your account has been changed successfully."); confirm_worker.setup (newemail, initial_subject, initial_body, query, subsequent_subject, subsequent_body); actions_taken = true; success_messages.push_back (translate("A verification email was sent to ") + newemail); } } if (!actions_taken) { success_messages.push_back (translate("No changes were made")); } } view.set_variable ("username", filter_string_sanitize_html (username)); view.set_variable ("email", filter_string_sanitize_html (email)); string success_message = filter_string_implode (success_messages, "\n"); view.set_variable ("success_messages", success_message); if (!actions_taken) view.enable_zone ("no_actions_taken"); page += view.render ("user", "account"); page += Assets_Page::footer (); return page; }
void Database_NoteAssignment::assignees (string user, vector <string> assignees) { filter_url_file_put_contents (path (user), filter_string_implode (assignees, "\n")); }
// This function creates a text-based burndown chart for sprint $bible / $year / $month. string sprint_create_burndown_chart (string bible, int year, int month) { // Get the seconds for the first of the month. int seconds = filter_date_seconds_since_epoch (year, month, 1); // The business days in the month for on the X-axis. vector <int> days_in_month; for (unsigned int day = 1; day <= 31; day++) { int mymonth = filter_date_numerical_month (seconds); if (mymonth == month) { if (filter_date_is_business_day (year, month, day)) { days_in_month.push_back (day); } } seconds += 86400; } // Assemble history of this sprint. Database_Sprint database_sprint = Database_Sprint (); vector <Database_Sprint_Item> history = database_sprint.getHistory (bible, year, month); map <int, int> data; for (auto day : days_in_month) { data [day] = 0; for (auto item : history) { if (day == item.day) { int tasks = item.tasks; int complete = item.complete; tasks = round (tasks * (100 - complete) / 100); data [day] = tasks; } } } vector <string> lines; lines.push_back ("<table class='burndown'>"); lines.push_back ("<tr>"); for (auto element : data) { int tasks = element.second; string text; for (int i = 0; i < tasks; i++) text.append ("▓<br>"); lines.push_back ("<td class='day'>" + text + "</td>"); } lines.push_back ("</tr>"); // Write number of days along the x-axis. lines.push_back ("<tr>"); for (auto element : data) { int day = element.first; lines.push_back ("<td class='day'>" + convert_to_string (day) + "</td>"); } lines.push_back ("</tr>"); // Write "days" below the x-axis. lines.push_back ("<tr>"); int columncount = data.size (); string text = translate("days"); lines.push_back ("<td colspan=\"" + convert_to_string (columncount) + "\">" + text + "</td>"); lines.push_back ("</tr>"); lines.push_back ("</table>"); string chart = filter_string_implode (lines, "\n"); return chart; }