Esempio n. 1
0
void Database_Config_Bible::setValue (string bible, const char * key, string value)
{
  if (bible.empty ()) return;
  string filename = file (bible, key);
  string dirname = filter_url_dirname (filename);
  if (!file_or_dir_exists (dirname)) filter_url_mkdir (dirname);
  filter_url_file_put_contents (filename, value);
}
Esempio n. 2
0
// Copies the entire directory $input to a directory named $output.
// It will recursively copy the inner directories also.
void filter_url_dir_cp (const string & input, const string & output)
{
  // Create the output directory.
  filter_url_mkdir (output);
  // Check on all files in the input directory.
  vector <string> files = filter_url_scandir (input);
  for (auto & file : files) {
    string input_path = filter_url_create_path (input, file);
    string output_path = filter_url_create_path (output, file);
    if (filter_url_is_dir (input_path)) {
      // Create output directory.
      filter_url_mkdir (output_path);
      // Handle the new input directory.
      filter_url_dir_cp (input_path, output_path);
    } else {
      // Copy input file to output.
      filter_url_file_cp (input_path, output_path);
    }
  }
}
Esempio n. 3
0
// Imports the file at $path into $resource.
void resource_logic_import_images (string resource, string path)
{
  Database_ImageResources database_imageresources;
  
  Database_Logs::log ("Importing: " + filter_url_basename (path));
  
  // To begin with, add the path to the main file to the list of paths to be processed.
  vector <string> paths = {path};
  
  while (!paths.empty ()) {
  
    // Take and remove the first path from the container.
    path = paths[0];
    paths.erase (paths.begin());
    string basename = filter_url_basename (path);
    string extension = filter_url_get_extension (path);
    extension = unicode_string_casefold (extension);

    if (extension == "pdf") {
      
      Database_Logs::log ("Processing PDF: " + basename);
      
      // Retrieve PDF information.
      filter_shell_run ("", "pdfinfo", {path}, NULL, NULL);

      // Convert the PDF file to separate images.
      string folder = filter_url_tempfile ();
      filter_url_mkdir (folder);
      filter_shell_run (folder, "pdftocairo", {"-jpeg", path}, NULL, NULL);
      // Add the images to the ones to be processed.
      filter_url_recursive_scandir (folder, paths);
      
    } else if (filter_archive_is_archive (path)) {
      
      Database_Logs::log ("Unpacking archive: " + basename);
      string folder = filter_archive_uncompress (path);
      filter_url_recursive_scandir (folder, paths);
      
    } else {

      if (!extension.empty ()) {
        basename = database_imageresources.store (resource, path);
        Database_Logs::log ("Storing image " + basename );
      }
      
    }
  }

  Database_Logs::log ("Ready importing images");
}
Esempio n. 4
0
// Prepares a sample Bible.
// The output of this is supposed to be manually put into the source tree, folder "samples".
// This will be used to quickly create a sample Bible, that is fast, even on mobile devices.
void demo_prepare_sample_bible ()
{
  Database_Bibles database_bibles;
  // Remove the Bible to remove all stuff that might have been in it.
  database_bibles.deleteBible (demo_sample_bible_name ());
  search_logic_delete_bible (demo_sample_bible_name ());
  // Create a new one.
  database_bibles.createBible (demo_sample_bible_name ());
  // Location of the USFM files for the sample Bible.
  string directory = filter_url_create_root_path ("demo");
  vector <string> files = filter_url_scandir (directory);
  for (auto file : files) {
    // Only process the USFM files.
    if (filter_url_get_extension (file) == "usfm") {
      cout << file << endl;
      // Read the USFM.
      file = filter_url_create_path (directory, file);
      string usfm = filter_url_file_get_contents (file);
      usfm = filter_string_str_replace ("  ", " ", usfm);
      // Import the USFM into the Bible.
      vector <BookChapterData> book_chapter_data = usfm_import (usfm, styles_logic_standard_sheet ());
      for (auto data : book_chapter_data) {
        Bible_Logic::storeChapter (demo_sample_bible_name (), data.book, data.chapter, data.data);
      }
    }
  }
  // Clean the destination location for the Bible.
  string destination = sample_bible_bible_path ();
  filter_url_rmdir (destination);
  // Copy the Bible data to the destination.
  string source = database_bibles.bibleFolder (demo_sample_bible_name ());
  filter_url_dir_cp (source, destination);
  // Clean the destination location for the Bible search index.
  destination = sample_bible_index_path ();
  filter_url_rmdir (destination);
  // Create destination location.
  filter_url_mkdir (destination);
  // Copy the index files over to the destination.
  source = search_logic_index_folder ();
  files = filter_url_scandir (source);
  for (auto file : files) {
    if (file.find (demo_sample_bible_name ()) != string::npos) {
      string source_file = filter_url_create_path (source, file);
      string destination_file = filter_url_create_path (destination, file);
      filter_url_file_cp (source_file, destination_file);
    }
  }
}
Esempio n. 5
0
void export_info (string bible)
{
  // Create folders for the information.
  string directory = filter_url_create_path (Export_Logic::bibleDirectory (bible), "info");
  if (!file_exists (directory)) filter_url_mkdir (directory);
  
  
  // Filenames for the various types of OpenDocument files.
  string informationdFilename = filter_url_create_path (directory, "information.html");
  string falloutFilename = filter_url_create_path (directory, "fallout.html");
  
  
  Database_Bibles database_bibles;
  
  
  string stylesheet = Database_Config_Bible::getExportStylesheet (bible);
  
  
  Filter_Text filter_text = Filter_Text (bible);
  
  
  vector <int> books = database_bibles.getBooks (bible);
  for (auto book : books) {
    vector <int> chapters = database_bibles.getChapters (bible, book);
    for (auto chapter : chapters) {
      string usfm = database_bibles.getChapter (bible, book, chapter);
      usfm = filter_string_trim (usfm);
      // Use small chunks of USFM at a time for much better performance.
      filter_text.addUsfmCode (usfm);
    }
  }
  
  
  // Go through USFM to find the info and fallout.
  filter_text.run (stylesheet);
  
  
  // Save files.
  filter_text.produceInfoDocument (informationdFilename);
  filter_text.produceFalloutDocument (falloutFilename);
  
  
  // Clear the flag for this export.
  Database_State::clearExport (bible, 0, Export_Logic::export_info);

  
  Database_Logs::log (translate("Documents with information and fallout were created") + " " + bible, Filter_Roles::translator ());
}
Esempio n. 6
0
// Uncompresses a .tar.gz archive identified by $file.
// Returns the path to the folder it created.
string filter_archive_untar_gzip (string file)
{
  file = filter_url_escape_shell_argument (file);
  string folder = filter_url_tempfile ();
  filter_url_mkdir (folder);
  folder.append (DIRECTORY_SEPARATOR);
  string logfile = filter_url_tempfile () + ".log";
  string command = "cd " + folder + " && tar zxf " + file + " > " + logfile + " 2>&1";
  int return_var = system (command.c_str());
  if (return_var != 0) {
    filter_url_rmdir (folder);
    folder.clear();
    string errors = filter_url_file_get_contents (logfile);
    Database_Logs::log (errors);
  }
  return folder;
}
Esempio n. 7
0
void database_privileges_client_create (const string & user, bool force)
{
  // The path to the file with privileges for the $user.
  string path = database_privileges_client_path (user);
  
  // Without $force, if the file exists, we're done.
  if (!force) {
    if (file_or_dir_exists (path)) return;
  }
  
  // If needed, create the folder.
  string folder = filter_url_dirname (path);
  if (!file_or_dir_exists (folder)) filter_url_mkdir (folder);
  
  // The bits of privileges in human-readable form.
  string privileges = Database_Privileges::save (user);
  
  // Write the privileges to disk.
  filter_url_file_put_contents (path, privileges);
}
Esempio n. 8
0
// Uncompresses a zip archive identified by $file.
// Returns the path to the folder it created.
string filter_archive_unzip (string file)
{
  string folder = filter_url_tempfile ();
  filter_url_mkdir (folder);
  folder.append (DIRECTORY_SEPARATOR);
  string logfile = filter_url_tempfile () + ".log";
  file = filter_url_escape_shell_argument (file);
  string command = "unzip -o -d " + folder + " " + file + " > " + logfile + " 2>&1";
  int return_var = system (command.c_str());
  if (return_var != 0) {
    filter_url_rmdir (folder);
    folder.clear();
    string errors = filter_url_file_get_contents (logfile);
    Database_Logs::log (errors);
  } else {
    // Set free permissions after unzipping.
    command = "chmod -R 0777 " + folder;
    int result = system (command.c_str ());
    (void) result;
  }
  return folder;
}
Esempio n. 9
0
void export_bibledropbox (string user, string bible)
{
  Webserver_Request request;
  Database_Bibles database_bibles;
  Database_Users database_users;
  Database_Mail database_mail = Database_Mail (&request);

  
  string tag = translate ("Submit to the Bible Drop Box") + ": ";
  Database_Logs::log (tag + bible, Filter_Roles::translator ());

  
  // Temporal USFM directory.
  string directory = filter_url_tempfile ();
  filter_url_mkdir (directory);
  

  // Take the USFM from the Bible database.
  // Generate one USFM file per book.
  vector <int> books = database_bibles.getBooks (bible);
  for (auto book : books) {
    
    
    // The USFM data of the current book.
    string bookdata;
    
    
    // Collect the USFM for all chapters in this book.
    vector <int> chapters = database_bibles.getChapters (bible, book);
    for (auto chapter : chapters) {
      // Get the USFM code for the current chapter.
      string usfm = database_bibles.getChapter (bible, book, chapter);
      usfm = filter_string_trim (usfm);
      // Add the chapter USFM code to the book's USFM code.
      bookdata.append (usfm);
      bookdata.append ("\n");
    }
    
    
    // The filename for the USFM for this book.
    string filename = Export_Logic::baseBookFileName (book);
    string path = filter_url_create_path (directory, filename + ".usfm");
    
    
    // Save.
    filter_url_file_put_contents (path, bookdata);
  }
  
  
  // Compress USFM files into one zip file.
  string zipfile = filter_url_create_path (directory, Export_Logic::baseBookFileName (0) + ".zip");
  
  string archive = filter_archive_zip_folder (directory);
  filter_url_rename (archive, zipfile);
  
  /*
  <form action="SubmitAction.phtml" method="post" enctype="multipart/form-data">
  <p>Your name: <input type="text" name="nameLine" size="40" /> </p>
  <p>Your email address: <input type="text" name="emailLine" size="50" /></p>
  <p>Language/Project name: <input type="text" name="projectLine" size="35" /></p>
  <p>I have the authority to submit this data: <input type="checkbox" name="permission" value="Yes">
  What is your main goal here (optional): <input type="text" name="goalLine" size="80" />
  <input type="checkbox" name="photoBible" value="Yes">
  OpenDocument <input type="checkbox" name="odfs" value="Yes">
  PDF <input type="checkbox" name="pdfs" value="Yes">
  Zip file containing your Bible file(s) <input type="file" name="uploadedZipFile" size="50" />
  <input type="hidden" name="MAX_FILE_SIZE" value="5000000" />
  <input type="file" name="uploadedMetadataFile" size="50" />
  <input type="submit" name="submit" value="Submit" />
  </form>
  */
  
  
  // Bible Drop Box submission URL.
  string url = "http://freely-given.org/Software/BibleDropBox/SubmitAction.phtml";
  
  
  // Form values to POST.
  map <string, string> post;
  post ["nameLine"] = user;
  post ["emailLine"] = database_users.getUserToEmail (user);
  post ["projectLine"] = bible;
  post ["permission"] = "Yes";
  post ["goalLine"] = "Bible translation through Bibledit";
  // Just one request: Is it possible to make the Bibledit submission system default to turning off the three check-boxes for the tasks that take heavy processing on The Bible Drop Box: PhotoBible, ODFs using the Python interface to LibreOffice (which is slow compared to the Pathway method of creating the XML files directly), and PDF exports (via TeX). If the user is only after, say a Sword module, it's quite a heavy cost to wastefully produce these other exports.
  //post ["photoBible"] = "Yes";
  //post ["odfs"] = "Yes";
  //post ["pdfs"] = "Yes";
  post ["submit"] = "Submit";
  
  // Submission and response.
  string error;
  string response = filter_url_http_upload (url, post, zipfile, error);
  if (!error.empty ()) {
    Database_Logs::log (tag + error, Filter_Roles::translator ());
    database_mail.send (user, "Error submitting to the Bible Drop Box", error);
  }
  size_t pos = response.find ("<head>");
  if (pos != string::npos) {
    response.insert (pos + 6, "<base href=\"http://freely-given.org/Software/BibleDropBox/\">");
  }
  database_mail.send (user, "Result of your submission to the Bible Drop Box", response);

  
  // Done.
  Database_Logs::log (tag + translate("Ready"), Filter_Roles::translator ());
}
Esempio n. 10
0
void export_quickbible (string bible, bool log)
{
  string directory = filter_url_create_path (Export_Logic::bibleDirectory (bible), "quickbible");
  if (!file_or_dir_exists (directory)) filter_url_mkdir (directory);

  Database_Bibles database_bibles;
  
  string stylesheet = Database_Config_Bible::getExportStylesheet (bible);

  string yet_contents;
  
  yet_contents.append (export_quickbible_tabify ("info", "shortName", bible));
  yet_contents.append (export_quickbible_tabify ("info", "longName", bible));
  yet_contents.append (export_quickbible_tabify ("info", "description", bible));
  yet_contents.append (export_quickbible_tabify ("info", "locale", "en"));
  
  vector <int> books = database_bibles.getBooks (bible);

  for (auto book : books) {
    string bookname = Database_Books::getEnglishFromId (book);
    yet_contents.append (export_quickbible_tabify ("book_name", convert_to_string (book), bookname, ""));
  }
  
  for (auto book : books) {
    // The .yet to .yes converter only handles books > 0.
    if (book) {
      vector <int> chapters = database_bibles.getChapters (bible, book);
      for (auto chapter : chapters) {
        // The .yet to .yes converter only handles chapters > 0.
        if (chapter) {
          Filter_Text filter_text = Filter_Text (bible);
          filter_text.initializeHeadingsAndTextPerVerse (true);
          string usfm = database_bibles.getChapter (bible, book, chapter);
          filter_text.addUsfmCode (usfm);
          filter_text.run (stylesheet);
          map <int, string> text = filter_text.getVersesText ();
          for (auto & element : text) {
            int verse = element.first;
            // The .yet to .yes converter only handles verses > 0.
            if (verse) {
              string bk = convert_to_string (book);
              string ch = convert_to_string (chapter);
              string vs = convert_to_string (element.first);
              string tx = element.second;
              if (tx.empty ()) tx = "empty";
              yet_contents.append (export_quickbible_tabify ("verse", bk, ch, vs, tx));
            }
          }
        }
      }
    }
  }
  
  string bible_yet = filter_url_create_path (directory, "bible.yet");
  filter_url_file_put_contents (bible_yet, yet_contents);
  
  string yet2yes_jar = filter_url_create_root_path ("export", "YetToYes2.jar");
  string bible_yes = filter_url_create_path (directory, "bible.yes");
  
  filter_shell_run ("", "java", { "-jar", yet2yes_jar, bible_yet, bible_yes }, NULL, NULL);
  
  string quickbible_html_source = filter_url_create_root_path ("export", "quickbible.html");
  string quickbible_html_destination = filter_url_create_path (directory, "quickbible.html");
  filter_url_file_cp (quickbible_html_source, quickbible_html_destination);
  
  Database_State::clearExport (bible, 0, Export_Logic::export_quick_bible);
  
  if (log) Database_Logs::log (translate("Exported to Quick Bible") + " " + bible, Filter_Roles::translator ());
}
Esempio n. 11
0
void changes_modifications ()
{
  Database_Logs::log ("Change notifications: Generating", Filter_Roles::translator ());

  
  // Notifications are not available to clients for the duration of processing them.
  config_globals_change_notifications_available = false;
  
  
  // Data objects.
  Webserver_Request request;
  Database_Modifications database_modifications;


  // Check on the health of the modifications database and (re)create it if needed.
  if (!database_modifications.healthy ()) database_modifications.erase ();
  database_modifications.create ();
  
  
  // Create online change notifications for users who made changes in Bibles
  // through the web editor or through a client.
  // It runs before the team changes.
  // This produces the desired order of the notifications in the GUI.
  // At the same time, produce change statistics per user.
  
  // Get the users who will receive the changes entered by the contributors.
  vector <string> recipients;
  {
    vector <string> users = request.database_users ()->getUsers ();
    for (auto & user : users) {
      if (request.database_config_user ()->getContributorChangesNotificationsOnline (user)) {
        recipients.push_back (user);
      }
    }
  }

  // Storage for the statistics.
  map <string, int> user_change_statistics;
  float modification_time_total = 0;
  int modification_time_count = 0;
  
  vector <string> users = database_modifications.getUserUsernames ();
  if (!users.empty ()) Database_Logs::log ("Change notifications: Per user", Filter_Roles::translator ());
  for (auto user : users) {

    // Total changes made by this user.
    int change_count = 0;
    
    // Go through the Bibles changed by the current user.
    vector <string> bibles = database_modifications.getUserBibles (user);
    for (auto bible : bibles) {
      
      // Body of the email to be sent.
      string email = "<p>" + translate("You have entered the changes below in a Bible editor.") + " " + translate ("You may check if it made its way into the Bible text.") + "</p>";
      size_t empty_email_length = email.length ();
      
      // Go through the books in that Bible.
      vector <int> books = database_modifications.getUserBooks (user, bible);
      for (auto book : books) {
        
        // Go through the chapters in that book.
        vector <int> chapters = database_modifications.getUserChapters (user, bible, book);
        for (auto chapter : chapters) {
          
          // Get the sets of identifiers for that chapter, and set some variables.
          vector <Database_Modifications_Id> IdSets = database_modifications.getUserIdentifiers (user, bible, book, chapter);
          //int referenceOldId = 0;
          int referenceNewId = 0;
          int newId = 0;
          int lastNewId = 0;
          bool restart = true;
          
          // Go through the sets of identifiers.
          for (auto IdSet : IdSets) {
            
            int oldId = IdSet.oldid;
            newId = IdSet.newid;
            
            if (restart) {
              changes_process_identifiers (&request, user, recipients, bible, book, chapter, referenceNewId, newId, email, change_count, modification_time_total, modification_time_count);
              //referenceOldId = oldId;
              referenceNewId = newId;
              lastNewId = newId;
              restart = false;
              continue;
            }
            
            if (oldId == lastNewId) {
              lastNewId = newId;
            } else {
              restart = true;
            }
          }
          
          // Process the last set of identifiers.
          changes_process_identifiers (&request, user, recipients, bible, book, chapter, referenceNewId, newId, email, change_count, modification_time_total, modification_time_count);
          
        }
      }

      // Check whether there's any email to be sent.
      if (email.length () != empty_email_length) {
        // Send the user email with the user's personal changes if the user opted to receive it.
        if (request.database_config_user()->getUserUserChangesNotification (user)) {
          string subject = translate("Changes you entered in") + " " + bible;
          if (!client_logic_client_enabled ()) email_schedule (user, subject, email);
        }
      }
    }
    
    // Store change statistics for this user.
    user_change_statistics [user] = change_count;

    // Clear the user's changes in the database.
    database_modifications.clearUserUser (user);
    
    
    // Clear checksum cache.
    request.database_config_user ()->setUserChangeNotificationsChecksum (user, "");
  }
  
  
  // Generate the notifications, online and by email,
  // for the changes in the Bibles entered by anyone since the previous notifications were generated.
  vector <string> bibles = database_modifications.getTeamDiffBibles ();
  for (auto bible : bibles) {
    
    
    string stylesheet = Database_Config_Bible::getExportStylesheet (bible);
    
    
    vector <string> changeNotificationUsers;
    vector <string> users = request.database_users ()->getUsers ();
    for (auto user : users) {
      if (access_bible_read (&request, bible, user)) {
        if (request.database_config_user()->getUserGenerateChangeNotifications (user)) {
          changeNotificationUsers.push_back (user);
        }
      }
    }
    users.clear ();
    
    
    // The number of changes processed so far for this Bible.
    int processedChangesCount = 0;
    
    
    // The files get stored at http://site.org:<port>/revisions/<Bible>/<date>
    int seconds = filter_date_seconds_since_epoch ();
    string timepath;
    timepath.append (convert_to_string (filter_date_numerical_year (seconds)));
    timepath.append ("-");
    timepath.append (filter_string_fill (convert_to_string (filter_date_numerical_month (seconds)), 2, '0'));
    timepath.append ("-");
    timepath.append (filter_string_fill (convert_to_string (filter_date_numerical_month_day (seconds)), 2, '0'));
    timepath.append (" ");
    timepath.append (filter_string_fill (convert_to_string (filter_date_numerical_hour (seconds)), 2, '0'));
    timepath.append (":");
    timepath.append (filter_string_fill (convert_to_string (filter_date_numerical_minute (seconds)), 2, '0'));
    timepath.append (":");
    timepath.append (filter_string_fill (convert_to_string (filter_date_numerical_second (seconds)), 2, '0'));
    string directory = filter_url_create_root_path ("revisions", bible, timepath);
    filter_url_mkdir (directory);
    
    
    // Produce the USFM and html files.
    filter_diff_produce_verse_level (bible, directory);
    
    
    // Create online page with changed verses.
    string versesoutputfile = filter_url_create_path (directory, "changed_verses.html");
    filter_diff_run_file (filter_url_create_path (directory, "verses_old.txt"), filter_url_create_path (directory, "verses_new.txt"), versesoutputfile);
    
    
    // Storage for body of the email with the changes.
    vector <string> email_changes;
    
    
    // Generate the online change notifications.
    vector <int> books = database_modifications.getTeamDiffBooks (bible);
    for (auto book : books) {
      vector <int> chapters = database_modifications.getTeamDiffChapters (bible, book);
      for (auto chapter : chapters) {
        Database_Logs::log ("Change notifications: " + bible + " " + filter_passage_display (book, chapter, ""), Filter_Roles::translator ());
        string old_chapter_usfm = database_modifications.getTeamDiff (bible, book, chapter);
        string new_chapter_usfm = request.database_bibles()->getChapter (bible, book, chapter);
        vector <int> old_verse_numbers = usfm_get_verse_numbers (old_chapter_usfm);
        vector <int> new_verse_numbers = usfm_get_verse_numbers (new_chapter_usfm);
        vector <int> verses = old_verse_numbers;
        verses.insert (verses.end (), new_verse_numbers.begin (), new_verse_numbers.end ());
        verses = array_unique (verses);
        sort (verses.begin (), verses.end());
        for (auto verse : verses) {
          string old_verse_usfm = usfm_get_verse_text (old_chapter_usfm, verse);
          string new_verse_usfm = usfm_get_verse_text (new_chapter_usfm, verse);
          if (old_verse_usfm != new_verse_usfm) {
            processedChangesCount++;
            // In case of too many change notifications, processing them would take too much time, so take a few shortcuts.
            string old_html = "<p>" + old_verse_usfm + "</p>";
            string new_html = "<p>" + new_verse_usfm + "</p>";
            string old_text = old_verse_usfm;
            string new_text = new_verse_usfm;
            if (processedChangesCount < 800) {
              Filter_Text filter_text_old = Filter_Text (bible);
              Filter_Text filter_text_new = Filter_Text (bible);
              filter_text_old.html_text_standard = new Html_Text ("");
              filter_text_new.html_text_standard = new Html_Text ("");
              filter_text_old.text_text = new Text_Text ();
              filter_text_new.text_text = new Text_Text ();
              filter_text_old.addUsfmCode (old_verse_usfm);
              filter_text_new.addUsfmCode (new_verse_usfm);
              filter_text_old.run (stylesheet);
              filter_text_new.run (stylesheet);
              old_html = filter_text_old.html_text_standard->getInnerHtml ();
              new_html = filter_text_new.html_text_standard->getInnerHtml ();
              old_text = filter_text_old.text_text->get ();
              new_text = filter_text_new.text_text->get ();
            }
            string modification = filter_diff_diff (old_text, new_text);
            database_modifications.recordNotification (changeNotificationUsers, changes_bible_category (), bible, book, chapter, verse, old_html, modification, new_html);
            string passage = filter_passage_display (book, chapter, convert_to_string (verse))   + ": ";
            if (old_text != new_text) {
              email_changes.push_back (passage  + modification);
            } else {
              email_changes.push_back (translate ("The following passage has no change in the text.") + " " + translate ("The change is in the formatting only.") + " " + translate ("The USFM code is given for reference."));
              email_changes.push_back (passage);
              email_changes.push_back (translate ("Old code:") + " " + old_verse_usfm);
              email_changes.push_back (translate ("New code:") + " " + new_verse_usfm);
            }
          }
        }
        // Delete the diff data for this chapter, for two reasons:
        // 1. New diffs for this chapter can be stored straightaway.
        // 2. In case of large amounts of diff data, and this function gets killed,
        //    then the next time it runs again, it will continue from where it was killed.
        database_modifications.deleteTeamDiffChapter (bible, book, chapter);
      }
    }
    
    
    // Email the changes to the subscribed users.
    if (!email_changes.empty ()) {
      string body;
      for (auto & line : email_changes) {
        body.append ("<div>");
        body.append (line);
        body.append ("</div>\n");
      }
      string subject = translate("Recent changes:") + " " + bible;
      vector <string> users = request.database_users ()->getUsers ();
      for (auto & user : users) {
        if (request.database_config_user()->getUserBibleChangesNotification (user)) {
          if (access_bible_read (&request, bible, user)) {
            if (!client_logic_client_enabled ()) {
              email_schedule (user, subject, body);
            }
          }
        }
      }
    }
    
    
  }

  
  // Index the data and remove expired notifications.
  Database_Logs::log ("Change notifications: Indexing", Filter_Roles::translator ());
  database_modifications.indexTrimAllNotifications ();

  
  // Remove expired downloadable revisions.
  string directory = filter_url_create_root_path ("revisions");
  int now = filter_date_seconds_since_epoch ();
  bibles = filter_url_scandir (directory);
  for (auto &bible : bibles) {
    string folder = filter_url_create_path (directory, bible);
    int time = filter_url_file_modification_time (folder);
    int days = (now - time) / 86400;
    if (days > 31) {
      filter_url_rmdir (folder);
    } else {
      vector <string> revisions = filter_url_scandir (folder);
      for (auto & revision : revisions) {
        string path = filter_url_create_path (folder, revision);
        int time = filter_url_file_modification_time (path);
        int days = (now - time) / 86400;
        if (days > 31) {
          filter_url_rmdir (path);
          Database_Logs::log ("Removing expired downloadable revision notification: " + bible + " " + revision, Filter_Roles::translator ());
        }
      }
    }
  }
  
  
  // Clear checksum caches.
  users = request.database_users ()->getUsers ();
  for (auto user : users) {
    request.database_config_user ()->setUserChangeNotificationsChecksum (user, "");
  }
  
  
  // Vacuum the modifications index, as it might have been updated.
  database_modifications.vacuum ();
  
  
  // Make the notifications available again to clients.
  config_globals_change_notifications_available = true;

  
  // Store the statistics in the database.
  if (modification_time_count) {
    // Take average timestamp of all timestamps.
    int timestamp = round (modification_time_total / modification_time_count);
    for (auto & element : user_change_statistics) {
      // Store dated change statistics per user.
      string user = element.first;
      int count = element.second;
      Database_Statistics::store_changes (timestamp, user, count);
    }
  }
  

  Database_Logs::log ("Change notifications: Ready", Filter_Roles::translator ());
}
Esempio n. 12
0
void sword_logic_refresh_module_list ()
{
  Database_Logs::log ("Refreshing list of SWORD modules");
  
  string out_err;
  
  // Initialize SWORD directory structure and configuration.
  string sword_path = sword_logic_get_path ();
  filter_url_mkdir (sword_path);
  string swordconf = "[Install]\n"
                     "DataPath=" + sword_path + "/\n";
  filter_url_file_put_contents (filter_url_create_path (sword_path, "sword.conf"), swordconf);
  string config_files_path = filter_url_create_root_path ("sword");
  filter_shell_run ("cp -r " + config_files_path + "/locales.d " + sword_path, out_err);
  sword_logic_log (out_err);
  filter_shell_run ("cp -r " + config_files_path + "/mods.d " + sword_path, out_err);
  sword_logic_log (out_err);
  
  // Initialize basic user configuration.
#ifdef HAVE_SWORD
  sword_logic_installmgr_initialize ();
#else
  filter_shell_run ("echo yes | installmgr -init", out_err);
  sword_logic_log (out_err);
#endif
  
  // Sync the configuration with the online known remote repository list.
#ifdef HAVE_SWORD
  if (!sword_logic_installmgr_synchronize_configuration_with_master ()) {
    Database_Logs::log ("Failed to synchronize SWORD configuration with the master remote source list");
    // Since this could be a network failure, exit from the entire update routine.
    // The advantage of existing already at this stage is that the list of known SWORD resources
    // will be left untouched in case of a network error.
    return;
  }
#else
  filter_shell_run ("echo yes | installmgr -sc", out_err);
  filter_string_replace_between (out_err, "WARNING", "enable? [no]", "");
  sword_logic_log (out_err);
#endif
  
  // List the remote sources.
  vector <string> remote_sources;
#ifdef HAVE_SWORD
  sword_logic_installmgr_list_remote_sources (remote_sources);
#else
  filter_shell_run ("installmgr -s", out_err);
  sword_logic_log (out_err);
  vector <string> lines = filter_string_explode (out_err, '\n');
  for (auto line : lines) {
    line = filter_string_trim (line);
    if (line.find ("[") != string::npos) {
      line.erase (0, 1);
      if (line.find ("]") != string::npos) {
        line.erase (line.length () - 1, 1);
        remote_sources.push_back (line);
        Database_Logs::log (line);
      }
    }
  }
#endif
  
  vector <string> sword_modules;
  
  for (auto remote_source : remote_sources) {
    
#ifdef HAVE_SWORD
    if (!sword_logic_installmgr_refresh_remote_source (remote_source)) {
      Database_Logs::log ("Error refreshing remote source " + remote_source);
    }
#else
    filter_shell_run ("echo yes | installmgr -r \"" + remote_source + "\"", out_err);
    filter_string_replace_between (out_err, "WARNING", "type yes at the prompt", "");
    sword_logic_log (out_err);
#endif

    vector <string> modules;
#ifdef HAVE_SWORD
    sword_logic_installmgr_list_remote_modules (remote_source, modules);
    for (auto & module : modules) {
      sword_modules.push_back ("[" + remote_source + "]" + " " + module);
    }
#else
    filter_shell_run ("installmgr -rl \"" + remote_source + "\"", out_err);
    lines = filter_string_explode (out_err, '\n');
    for (auto line : lines) {
      line = filter_string_trim (line);
      if (line.empty ()) continue;
      if (line.find ("[") == string::npos) continue;
      if (line.find ("]") == string::npos) continue;
      modules.push_back ("[" + remote_source + "]" + " " + line);
    }
    for (auto module : modules) {
      sword_modules.push_back (module);
    }
#endif
    Database_Logs::log (remote_source + ": " + convert_to_string (modules.size ()) + " modules");
  }
  
  // Store the list of remote sources and their modules.
  // It is stored in the client files area.
  // Clients can access it from there too.
  string path = sword_logic_module_list_path ();
  filter_url_file_put_contents (path, filter_string_implode (sword_modules, "\n"));
  
  Database_Logs::log ("Ready refreshing SWORD module list");
}