Exemple #1
0
        /// <summary> Get list of book numbers in PT project. </summary>
        public IReadOnlyList <int> GetBookList(UserSecret userSecret, string ptProjectId)
        {
            ScrText scrText = ScrTextCollection.FindById(GetParatextUsername(userSecret), ptProjectId);

            if (scrText == null)
            {
                return(Array.Empty <int>());
            }
            return(scrText.Settings.BooksPresentSet.SelectedBookNumbers.ToArray());
        }
Exemple #2
0
        /// <summary> Get PT book text in USX, or throw if can't. </summary>
        public string GetBookText(UserSecret userSecret, string ptProjectId, int bookNum)
        {
            ScrText scrText = ScrTextCollection.FindById(GetParatextUsername(userSecret), ptProjectId);

            if (scrText == null)
            {
                throw new DataNotFoundException("Can't get access to cloned project.");
            }
            string usfm = scrText.GetText(bookNum);

            return(UsfmToUsx.ConvertToXmlString(scrText, bookNum, usfm, false));
        }
Exemple #3
0
        /// <summary> Get notes from the Paratext project folder. </summary>
        public string GetNotes(UserSecret userSecret, string projectId, int bookNum)
        {
            // TODO: should return some data structure instead of XML
            ScrText scrText = ScrTextCollection.FindById(GetParatextUsername(userSecret), projectId);

            if (scrText == null)
            {
                return(null);
            }

            CommentManager manager = CommentManager.Get(scrText);
            var            threads = manager.FindThreads((commentThread) => { return(commentThread.VerseRef.BookNum == bookNum); },
                                                         true);

            return(NotesFormatter.FormatNotes(threads));
        }
Exemple #4
0
        /// <summary>
        /// Ensure the target project repository exists on the local SF server, cloning if necessary.
        /// </summary>
        private void EnsureProjectReposExists(UserSecret userSecret, ParatextProject target,
                                              IInternetSharedRepositorySource repositorySource)
        {
            string username          = GetParatextUsername(userSecret);
            bool   targetNeedsCloned =
                ScrTextCollection.FindById(username, target.ParatextId) == null;

            if (target is ParatextResource resource)
            {
                // If the target is a resource, install it
                InstallResource(resource, target.ParatextId, targetNeedsCloned);
            }
            else if (targetNeedsCloned)
            {
                SharedRepository targetRepo = new SharedRepository(target.ShortName, HexId.FromStr(target.ParatextId),
                                                                   RepositoryType.Shared);
                CloneProjectRepo(repositorySource, target.ParatextId, targetRepo);
            }
        }
Exemple #5
0
        /// <summary> Write up-to-date notes from the mongo database to the Paratext project folder </summary>
        public void PutNotes(UserSecret userSecret, string projectId, string notesText)
        {
            // TODO: should accept some data structure instead of XML
            string        username = GetParatextUsername(userSecret);
            List <string> users = new List <string>();
            int           nbrAddedComments = 0, nbrDeletedComments = 0, nbrUpdatedComments = 0;
            ScrText       scrText = ScrTextCollection.FindById(username, projectId);

            if (scrText == null)
            {
                throw new DataNotFoundException("Can't get access to cloned project.");
            }
            CommentManager manager = CommentManager.Get(scrText);
            var            ptUser  = new SFParatextUser(username);
            var            notes   = NotesFormatter.ParseNotes(notesText, ptUser);

            // Algorithm sourced from Paratext DataAccessServer
            foreach (var thread in notes)
            {
                CommentThread existingThread = manager.FindThread(thread[0].Thread);
                foreach (var comment in thread)
                {
                    var existingComment = existingThread?.Comments.FirstOrDefault(c => c.Id == comment.Id);
                    if (existingComment == null)
                    {
                        manager.AddComment(comment);
                        nbrAddedComments++;
                    }
                    else if (comment.Deleted)
                    {
                        existingComment.Deleted = true;
                        nbrDeletedComments++;
                    }
                    else
                    {
                        existingComment.ExternalUser   = comment.ExternalUser;
                        existingComment.Contents       = comment.Contents;
                        existingComment.VersionNumber += 1;
                        nbrUpdatedComments++;
                    }

                    if (!users.Contains(comment.User))
                    {
                        users.Add(comment.User);
                    }
                }
            }

            try
            {
                foreach (string user in users)
                {
                    manager.SaveUser(user, false);
                }
                _paratextDataHelper.CommitVersionedText(scrText, $"{nbrAddedComments} notes added and "
                                                        + $"{nbrDeletedComments + nbrUpdatedComments} notes updated or deleted in synchronize");
                _logger.LogInformation("{0} added {1} notes, updated {2} notes and deleted {3} notes", userSecret.Id,
                                       nbrAddedComments, nbrUpdatedComments, nbrDeletedComments);
            }
            catch (Exception e)
            {
                _logger.LogError(e, "Exception while updating notes: {0}", e.Message);
            }
        }
Exemple #6
0
        /// <summary> Write up-to-date book text from mongo database to Paratext project folder. </summary>
        public async Task PutBookText(UserSecret userSecret, string projectId, int bookNum, string usx,
                                      Dictionary <int, string> chapterAuthors = null)
        {
            string  username = GetParatextUsername(userSecret);
            ScrText scrText  = ScrTextCollection.FindById(username, projectId);
            var     doc      = new XmlDocument
            {
                PreserveWhitespace = true
            };

            doc.LoadXml(usx);
            UsxFragmenter.FindFragments(scrText.ScrStylesheet(bookNum), doc.CreateNavigator(),
                                        XPathExpression.Compile("*[false()]"), out string usfm);
            usfm = UsfmToken.NormalizeUsfm(scrText.ScrStylesheet(bookNum), usfm, false, scrText.RightToLeft, scrText);

            if (chapterAuthors == null || chapterAuthors.Count == 0)
            {
                // If we don't have chapter authors, update book as current user
                if (scrText.Permissions.AmAdministrator)
                {
                    // if the current user is an administrator, then always allow editing the book text even if the user
                    // doesn't have permission. This will ensure that a sync by an administrator never fails.
                    scrText.Permissions.RunWithEditPermision(bookNum,
                                                             () => scrText.PutText(bookNum, 0, false, usfm, null));
                }
                else
                {
                    scrText.PutText(bookNum, 0, false, usfm, null);
                }
                _logger.LogInformation("{0} updated {1} in {2}.", userSecret.Id,
                                       Canon.BookNumberToEnglishName(bookNum), scrText.Name);
            }
            else
            {
                // As we have a list of chapter authors, build a dictionary of ScrTexts for each of them
                Dictionary <string, ScrText> scrTexts = new Dictionary <string, ScrText>();
                foreach (string userId in chapterAuthors.Values.Distinct())
                {
                    if (userId == userSecret.Id)
                    {
                        scrTexts.Add(userId, scrText);
                    }
                    else
                    {
                        // Get their user secret, so we can get their username, and create their ScrText
                        UserSecret authorUserSecret = await _userSecretRepository.GetAsync(userId);

                        string authorUserName = GetParatextUsername(authorUserSecret);
                        scrTexts.Add(userId, ScrTextCollection.FindById(authorUserName, projectId));
                    }
                }

                // If there is only one author, just write the book
                if (scrTexts.Count == 1)
                {
                    scrTexts.Values.First().PutText(bookNum, 0, false, usfm, null);
                    _logger.LogInformation("{0} updated {1} in {2}.", scrTexts.Keys.First(),
                                           Canon.BookNumberToEnglishName(bookNum), scrText.Name);
                }
                else
                {
                    // Split the usfm into chapters
                    List <string> chapters = ScrText.SplitIntoChapters(scrText.Name, bookNum, usfm);

                    // Put the individual chapters
                    foreach ((int chapterNum, string authorUserId) in chapterAuthors)
                    {
                        if ((chapterNum - 1) < chapters.Count)
                        {
                            // The ScrText permissions will be the same as the last sync's permissions, so no need to check
                            scrTexts[authorUserId].PutText(bookNum, chapterNum, false, chapters[chapterNum - 1], null);
                            _logger.LogInformation("{0} updated chapter {1} of {2} in {3}.", authorUserId,
                                                   chapterNum, Canon.BookNumberToEnglishName(bookNum), scrText.Name);
                        }
                    }
                }
            }
        }
Exemple #7
0
        /// <summary> Determine if a specific project is in a right to left language. </summary>
        public bool IsProjectLanguageRightToLeft(UserSecret userSecret, string ptProjectId)
        {
            ScrText scrText = ScrTextCollection.FindById(GetParatextUsername(userSecret), ptProjectId);

            return(scrText == null ? false : scrText.RightToLeft);
        }
Exemple #8
0
        /// <summary>
        /// Gets the permissions for a project or resource.
        /// </summary>
        /// <param name="userSecret">The user secret.</param>
        /// <param name="project">The project - the UserRoles and ParatextId are used.</param>
        /// <param name="ptUsernameMapping">A mapping of user ID to Paratext username.</param>
        /// <param name="book">The book number. Set to zero to check for all books.</param>
        /// <param name="chapter">The chapter number. Set to zero to check for all books.</param>
        /// <returns>
        /// A dictionary of permissions where the key is the user ID and the value is the permission.
        /// </returns>
        /// <remarks>
        /// See <see cref="TextInfoPermission" /> for permission values.
        /// A dictionary is returned, as permissions can be updated.
        /// </remarks>
        public async Task <Dictionary <string, string> > GetPermissionsAsync(UserSecret userSecret, SFProject project,
                                                                             IReadOnlyDictionary <string, string> ptUsernameMapping, int book = 0, int chapter = 0)
        {
            var permissions = new Dictionary <string, string>();

            // See if the source is a resource
            if (project.ParatextId.Length == SFInstallableDblResource.ResourceIdentifierLength)
            {
                foreach (string uid in project.UserRoles.Keys)
                {
                    permissions.Add(uid, await this.GetResourcePermissionAsync(project.ParatextId, uid));
                }
            }
            else
            {
                // Get the scripture text so we can retrieve the permissions from the XML
                ScrText scrText = ScrTextCollection.FindById(GetParatextUsername(userSecret), project.ParatextId);

                // Calculate the project and resource permissions
                foreach (string uid in project.UserRoles.Keys)
                {
                    // See if the user is in the project members list
                    if (!ptUsernameMapping.TryGetValue(uid, out string userName) || string.IsNullOrWhiteSpace(userName) ||
                        scrText.Permissions.GetRole(userName) == Paratext.Data.Users.UserRoles.None)
                    {
                        permissions.Add(uid, TextInfoPermission.None);
                    }
                    else
                    {
                        string textInfoPermission = TextInfoPermission.Read;
                        if (book == 0)
                        {
                            // Project level
                            if (scrText.Permissions.CanEditAllBooks(userName))
                            {
                                textInfoPermission = TextInfoPermission.Write;
                            }
                        }
                        else if (chapter == 0)
                        {
                            // Book level
                            IEnumerable <int> editable = scrText.Permissions.GetEditableBooks(
                                Paratext.Data.Users.PermissionSet.Merged, userName);
                            if (editable == null || !editable.Any())
                            {
                                // If there are no editable book permissions, check if they can edit all books
                                if (scrText.Permissions.CanEditAllBooks(userName))
                                {
                                    textInfoPermission = TextInfoPermission.Write;
                                }
                            }
                            else if (editable.Contains(book))
                            {
                                textInfoPermission = TextInfoPermission.Write;
                            }
                        }
                        else
                        {
                            // Chapter level
                            IEnumerable <int> editable = scrText.Permissions.GetEditableChapters(book,
                                                                                                 scrText.Settings.Versification, userName, Paratext.Data.Users.PermissionSet.Merged);
                            if (editable?.Contains(chapter) ?? false)
                            {
                                textInfoPermission = TextInfoPermission.Write;
                            }
                        }

                        permissions.Add(uid, textInfoPermission);
                    }
                }
            }

            return(permissions);
        }
Exemple #9
0
        /// <summary>
        /// Synchronizes the text and notes data on the SF server with the data on the Paratext server.
        /// </summary>
        public async Task SendReceiveAsync(UserSecret userSecret, string ptTargetId,
                                           IProgress <ProgressState> progress = null)
        {
            if (userSecret == null || ptTargetId == null)
            {
                throw new ArgumentNullException();
            }

            IInternetSharedRepositorySource source = await GetInternetSharedRepositorySource(userSecret.Id);

            IEnumerable <SharedRepository>       repositories        = source.GetRepositories();
            IEnumerable <ProjectMetadata>        projectsMetadata    = source.GetProjectsMetaData();
            IEnumerable <string>                 projectGuids        = projectsMetadata.Select(pmd => pmd.ProjectGuid.Id);
            Dictionary <string, ParatextProject> ptProjectsAvailable =
                GetProjects(userSecret, repositories, projectsMetadata).ToDictionary(ptProject => ptProject.ParatextId);

            if (!projectGuids.Contains(ptTargetId))
            {
                // See if this is a resource
                IReadOnlyList <ParatextResource> resources = await this.GetResourcesInternalAsync(userSecret.Id, true);

                ParatextResource resource = resources.SingleOrDefault(r => r.ParatextId == ptTargetId);
                if (resource != null)
                {
                    ptProjectsAvailable.Add(resource.ParatextId, resource);
                }
                else
                {
                    _logger.LogWarning($"The target project did not have a full name available {ptTargetId}");
                }
            }
            if (!ptProjectsAvailable.TryGetValue(ptTargetId, out ParatextProject targetPtProject))
            {
                throw new ArgumentException(
                          $"PT projects with the following PT ids were requested but without access or they don't exist: {ptTargetId}");
            }

            EnsureProjectReposExists(userSecret, targetPtProject, source);
            StartProgressReporting(progress);
            if (!(targetPtProject is ParatextResource))
            {
                SharedProject targetSharedProj = SharingLogicWrapper.CreateSharedProject(ptTargetId,
                                                                                         targetPtProject.ShortName, source.AsInternetSharedRepositorySource(), repositories);
                string username = GetParatextUsername(userSecret);
                // Specifically set the ScrText property of the SharedProject to indicate the project is available locally
                targetSharedProj.ScrText     = ScrTextCollection.FindById(username, ptTargetId);
                targetSharedProj.Permissions = targetSharedProj.ScrText.Permissions;
                List <SharedProject> sharedPtProjectsToSr = new List <SharedProject> {
                    targetSharedProj
                };

                // TODO report results
                List <SendReceiveResult> results = Enumerable.Empty <SendReceiveResult>().ToList();
                bool success  = false;
                bool noErrors = SharingLogicWrapper.HandleErrors(() => success = SharingLogicWrapper
                                                                                 .ShareChanges(sharedPtProjectsToSr, source.AsInternetSharedRepositorySource(),
                                                                                               out results, sharedPtProjectsToSr));
                if (!noErrors || !success)
                {
                    throw new InvalidOperationException(
                              "Failed: Errors occurred while performing the sync with the Paratext Server.");
                }
            }
        }