Beispiel #1
0
        public IActionResult DownloadAudioFile(string projectId, string wordId, string fileName)
        {
            // if we require authorization and authentication for audio files, the frontend cannot just use the api
            // endpoint as the src
            //if (!_permissionService.IsProjectAuthorized("1", HttpContext))
            //{
            //    return Forbid();
            //}

            // Sanitize user input
            if (!Sanitization.SanitizeId(projectId) || !Sanitization.SanitizeId(wordId) ||
                !Sanitization.SanitizeFileName(fileName))
            {
                return(new UnsupportedMediaTypeResult());
            }

            var filePath = FileStorage.GenerateAudioFilePath(projectId, fileName);
            var file     = System.IO.File.OpenRead(filePath);

            if (file is null)
            {
                return(BadRequest("The file does not exist."));
            }

            return(File(file, "application/octet-stream"));
        }
Beispiel #2
0
        // These internal methods are extracted for unit testing
        internal async Task <IActionResult> ExportLiftFile(string projectId, string userId)
        {
            if (!await _permissionService.HasProjectPermission(HttpContext, Permission.ImportExport))
            {
                return(Forbid());
            }

            // Sanitize projectId
            if (!Sanitization.SanitizeId(projectId))
            {
                return(new UnsupportedMediaTypeResult());
            }

            // Ensure project exists
            var proj = await _projRepo.GetProject(projectId);

            if (proj is null)
            {
                return(NotFound(projectId));
            }

            // Check if another export started
            if (_liftService.IsExportInProgress(userId))
            {
                return(Conflict());
            }

            // Store in-progress status for the export
            _liftService.SetExportInProgress(userId, true);

            try
            {
                // Ensure project has words
                var words = await _wordRepo.GetAllWords(projectId);

                if (words.Count == 0)
                {
                    _liftService.SetExportInProgress(userId, false);
                    return(BadRequest("No words to export."));
                }

                // Export the data to a zip, read into memory, and delete zip
                var exportedFilepath = await CreateLiftExport(projectId);

                // Store the temporary path to the exported file for user to download later.
                _liftService.StoreExport(userId, exportedFilepath);
                await _notifyService.Clients.All.SendAsync("DownloadReady", userId);

                return(Ok(projectId));
            }
            catch
            {
                _liftService.SetExportInProgress(userId, false);
                throw;
            }
        }
Beispiel #3
0
        public async Task <IActionResult> UploadAudioFile(string projectId, string wordId,
                                                          [FromForm] FileUpload fileUpload)
        {
            if (!await _permissionService.HasProjectPermission(HttpContext, Permission.WordEntry))
            {
                return(Forbid());
            }

            // sanitize user input
            if (!Sanitization.SanitizeId(projectId) || !Sanitization.SanitizeId(wordId))
            {
                return(new UnsupportedMediaTypeResult());
            }

            var file = fileUpload.File;

            if (file is null)
            {
                return(BadRequest("Null File"));
            }

            // Ensure file is not empty
            if (file.Length == 0)
            {
                return(BadRequest("Empty File"));
            }

            // This path should be unique even though it is only based on the Word ID because currently, a new
            // Word is created each time an audio file is uploaded.
            fileUpload.FilePath = FileStorage.GenerateAudioFilePathForWord(projectId, wordId);

            // Copy the file data to a new local file
            await using (var fs = new FileStream(fileUpload.FilePath, FileMode.Create))
            {
                await file.CopyToAsync(fs);
            }

            // Add the relative path to the audio field
            var word = await _wordRepo.GetWord(projectId, wordId);

            if (word is null)
            {
                return(NotFound(wordId));
            }
            word.Audio.Add(Path.GetFileName(fileUpload.FilePath));

            // Update the word with new audio file
            await _wordService.Update(projectId, wordId, word);

            return(Ok(word.Id));
        }
Beispiel #4
0
        public async Task <IActionResult> CanUploadLift(string projectId)
        {
            if (!await _permissionService.HasProjectPermission(HttpContext, Permission.ImportExport))
            {
                return(Forbid());
            }

            // Sanitize user input
            if (!Sanitization.SanitizeId(projectId))
            {
                return(new UnsupportedMediaTypeResult());
            }

            return(Ok(await _projRepo.CanImportLift(projectId)));
        }
Beispiel #5
0
        public async Task <IActionResult> DeleteAudioFile(string projectId, string wordId, string fileName)
        {
            if (!await _permissionService.HasProjectPermission(HttpContext, Permission.WordEntry))
            {
                return(Forbid());
            }

            // sanitize user input
            if (!Sanitization.SanitizeId(projectId) || !Sanitization.SanitizeId(wordId))
            {
                return(new UnsupportedMediaTypeResult());
            }

            var newWord = await _wordService.Delete(projectId, wordId, fileName);

            if (newWord is not null)
            {
                return(Ok(newWord.Id));
            }
            return(NotFound("The project was found, but the word audio was not deleted"));
        }
 public void TestInvalidIds(string id)
 {
     Assert.False(Sanitization.SanitizeId(id));
 }
 public void TestValidIds(string id)
 {
     Assert.That(Sanitization.SanitizeId(id));
 }
Beispiel #8
0
        [RequestSizeLimit(250_000_000)]  // 250MB.
        public async Task <IActionResult> UploadLiftFile(string projectId, [FromForm] FileUpload fileUpload)
        {
            if (!await _permissionService.HasProjectPermission(HttpContext, Permission.ImportExport))
            {
                return(Forbid());
            }

            // Sanitize projectId
            if (!Sanitization.SanitizeId(projectId))
            {
                return(new UnsupportedMediaTypeResult());
            }

            // Ensure Lift file has not already been imported.
            if (!await _projRepo.CanImportLift(projectId))
            {
                return(BadRequest("A Lift file has already been uploaded."));
            }

            var liftStoragePath = FileStorage.GenerateLiftImportDirPath(projectId);

            // Clear out any files left by a failed import
            RobustIO.DeleteDirectoryAndContents(liftStoragePath);

            var file = fileUpload.File;

            if (file is null)
            {
                return(BadRequest("Null File"));
            }

            // Ensure file is not empty
            if (file.Length == 0)
            {
                return(BadRequest("Empty File"));
            }

            // Copy zip file data to a new temporary file
            fileUpload.FilePath = Path.GetTempFileName();
            await using (var fs = new FileStream(fileUpload.FilePath, FileMode.OpenOrCreate))
            {
                await file.CopyToAsync(fs);
            }

            // Make temporary destination for extracted files
            var extractDir = FileOperations.GetRandomTempDir();

            // Extract the zip to new created directory.
            FileOperations.ExtractZipFile(fileUpload.FilePath, extractDir, true);

            // Check number of directories extracted
            var directoriesExtracted = Directory.GetDirectories(extractDir);
            var extractedDirPath     = "";

            switch (directoriesExtracted.Length)
            {
            // If there was one directory, we're good
            case 1:
            {
                extractedDirPath = directoriesExtracted.First();
                break;
            }

            // If there were two, and there was a __MACOSX directory, ignore it
            case 2:
            {
                var numDirs = 0;
                foreach (var dir in directoriesExtracted)
                {
                    if (dir.EndsWith("__MACOSX"))
                    {
                        Directory.Delete(dir, true);
                    }
                    else         // This directory probably matters
                    {
                        extractedDirPath = dir;
                        numDirs++;
                    }
                }
                // Both directories seemed important
                if (numDirs == 2)
                {
                    return(BadRequest("Your zip file should have one directory."));
                }
                break;
            }

            // There were 0 or more than 2 directories
            default:
            {
                return(BadRequest(
                           "Your zip file structure has the wrong number of directories."));
            }
            }

            // Copy the extracted contents into the persistent storage location for the project.
            FileOperations.CopyDirectory(extractedDirPath, liftStoragePath);
            Directory.Delete(extractDir, true);

            // Search for the lift file within the extracted files
            var extractedLiftNameArr = Directory.GetFiles(liftStoragePath);
            var extractedLiftPath    = Array.FindAll(extractedLiftNameArr, x => x.EndsWith(".lift"));

            if (extractedLiftPath.Length > 1)
            {
                return(BadRequest("More than one .lift file detected."));
            }
            if (extractedLiftPath.Length == 0)
            {
                return(BadRequest("No lift files detected."));
            }

            int liftParseResult;
            // Sets the projectId of our parser to add words to that project
            var liftMerger = _liftService.GetLiftImporterExporter(projectId, _wordRepo);

            try
            {
                // Add character set to project from ldml file
                var proj = await _projRepo.GetProject(projectId);

                if (proj is null)
                {
                    return(NotFound(projectId));
                }

                _liftService.LdmlImport(
                    Path.Combine(liftStoragePath, "WritingSystems"),
                    proj.VernacularWritingSystem.Bcp47, _projRepo, proj);

                var parser = new LiftParser <LiftObject, LiftEntry, LiftSense, LiftExample>(liftMerger);

                // Import words from lift file
                liftParseResult = parser.ReadLiftFile(extractedLiftPath.FirstOrDefault());
                await liftMerger.SaveImportEntries();
            }
            catch (Exception e)
            {
                _logger.LogError(e, $"Error importing lift file {fileUpload.Name} into project {projectId}.");
                return(BadRequest("Error processing the lift data. Contact support for help."));
            }

            // Store that we have imported Lift data already for this project to signal the frontend
            // not to attempt to import again.
            var project = await _projRepo.GetProject(projectId);

            if (project is null)
            {
                return(NotFound(projectId));
            }

            project.LiftImported = true;
            await _projRepo.Update(projectId, project);

            return(Ok(liftParseResult));
        }