Pdf Import Rewrite
Pdf Import rewritten to greatly improve QR Code detection, reduce reliance on iTextSharp and improve thumbnails. Fixes #50
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
using Quartz;
|
||||
using Quartz.Impl;
|
||||
using Quartz.Impl.Triggers;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Disco.Services.Documents.AttachmentImport
|
||||
{
|
||||
public class ImportDirectoryMonitor : IDisposable
|
||||
{
|
||||
private FileSystemWatcher watcher;
|
||||
|
||||
public const string WatcherFilter = "*.pdf";
|
||||
public string MonitorLocation { get; private set; }
|
||||
public IScheduler Scheduler { get; private set; }
|
||||
public int ImportDelay { get; private set; }
|
||||
|
||||
public ImportDirectoryMonitor(string MonitorLocation, IScheduler Scheduler, int ImportDelay)
|
||||
{
|
||||
if (MonitorLocation == null)
|
||||
throw new ArgumentNullException(nameof(MonitorLocation));
|
||||
if (Scheduler == null)
|
||||
throw new ArgumentNullException(nameof(Scheduler));
|
||||
|
||||
this.MonitorLocation = MonitorLocation.EndsWith(@"\") ? MonitorLocation : $@"{MonitorLocation}\";
|
||||
this.Scheduler = Scheduler;
|
||||
this.ImportDelay = Math.Max(0, ImportDelay);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (watcher == null)
|
||||
{
|
||||
if (!Directory.Exists(MonitorLocation))
|
||||
{
|
||||
Directory.CreateDirectory(MonitorLocation);
|
||||
}
|
||||
|
||||
watcher = new FileSystemWatcher(MonitorLocation, WatcherFilter);
|
||||
watcher.Created += OnFileCreated;
|
||||
}
|
||||
|
||||
watcher.EnableRaisingEvents = true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (watcher != null)
|
||||
{
|
||||
watcher.EnableRaisingEvents = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFileCreated(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
if (!e.ChangeType.HasFlag(WatcherChangeTypes.Deleted))
|
||||
{
|
||||
ScheduleImport(e.FullPath, ImportDelay);
|
||||
}
|
||||
}
|
||||
|
||||
private void ScheduleImport(string Filename, int ImportDelay)
|
||||
{
|
||||
var startTime = new DateTimeOffset(DateTime.Now.AddMilliseconds(ImportDelay));
|
||||
var jobTrigger = new SimpleTriggerImpl(Guid.NewGuid().ToString(), startTime);
|
||||
|
||||
var jobDetails = new JobDetailImpl(Guid.NewGuid().ToString(), typeof(ImporterJob));
|
||||
jobDetails.JobDataMap.Add("Filename", Filename);
|
||||
jobDetails.JobDataMap.Add("RetryCount", 0);
|
||||
|
||||
Scheduler.ScheduleJob(jobDetails, jobTrigger);
|
||||
}
|
||||
|
||||
public void ScheduleCurrentFiles(int ImportDelay)
|
||||
{
|
||||
foreach (var filename in Directory.GetFiles(this.MonitorLocation, "*.pdf"))
|
||||
{
|
||||
ScheduleImport(filename, ImportDelay);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (watcher != null)
|
||||
{
|
||||
watcher.EnableRaisingEvents = false;
|
||||
watcher.Dispose();
|
||||
watcher = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
using Disco.Data.Repository;
|
||||
using PdfiumViewer;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using ZXing;
|
||||
using ZXing.Common;
|
||||
using ZXing.Multi.QrCode;
|
||||
|
||||
namespace Disco.Services.Documents.AttachmentImport
|
||||
{
|
||||
internal class ImportPage : IDisposable
|
||||
{
|
||||
public DiscoDataContext Database { get; private set; }
|
||||
public string SessionId { get; private set; }
|
||||
public PdfDocument PdfiumDocument { get; private set; }
|
||||
public int PageIndex { get; private set; }
|
||||
|
||||
public DocumentUniqueIdentifier Identifier { get; private set; }
|
||||
|
||||
private Result qrCodeResult;
|
||||
private float qrCodeResultScale;
|
||||
private Image renderedImage;
|
||||
private Bitmap renderedThumbnail;
|
||||
private RotateFlipType detectedRotation;
|
||||
|
||||
public ImportPage(DiscoDataContext Database, string SessionId, PdfDocument PdfiumDocument, int PageIndex)
|
||||
{
|
||||
this.Database = Database;
|
||||
this.SessionId = SessionId;
|
||||
this.PdfiumDocument = PdfiumDocument;
|
||||
this.PageIndex = PageIndex;
|
||||
}
|
||||
|
||||
public bool IsDetected
|
||||
{
|
||||
get
|
||||
{
|
||||
return Identifier != null;
|
||||
}
|
||||
}
|
||||
|
||||
public Image Image
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetRenderedImage();
|
||||
}
|
||||
}
|
||||
|
||||
public Bitmap Thumbnail
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetRenderedThumbnail();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsValidAttachment
|
||||
{
|
||||
get
|
||||
{
|
||||
return Identifier != null &&
|
||||
Identifier.Creator != null &&
|
||||
Identifier.Target != null &&
|
||||
(Identifier.DocumentTemplate != null || Identifier.AttachmentType.HasValue);
|
||||
}
|
||||
}
|
||||
|
||||
public int Rotation
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (detectedRotation)
|
||||
{
|
||||
case RotateFlipType.Rotate90FlipNone:
|
||||
return 90;
|
||||
case RotateFlipType.Rotate180FlipNone:
|
||||
return 180;
|
||||
case RotateFlipType.Rotate270FlipNone:
|
||||
return 270;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteThumbnailSessionCache()
|
||||
{
|
||||
var sessionCacheLocation = DataStore.CreateLocation(Database, "Cache\\DocumentDropBox_SessionPages");
|
||||
var filename = Path.Combine(sessionCacheLocation, $"{SessionId}-{PageIndex + 1}");
|
||||
|
||||
Thumbnail.SavePng(filename);
|
||||
}
|
||||
|
||||
public void WriteUndetectedImages()
|
||||
{
|
||||
var undetectedLocation = DataStore.CreateLocation(Database, "DocumentDropBox_Unassigned");
|
||||
var filename = Path.Combine(undetectedLocation, $"{SessionId}_{PageIndex + 1}_thumbnail.png");
|
||||
Thumbnail.SavePng(filename);
|
||||
|
||||
using (var largePreview = Image.ResizeImage(700, 700))
|
||||
{
|
||||
filename = Path.Combine(undetectedLocation, $"{SessionId}_{PageIndex + 1}.jpg");
|
||||
largePreview.SaveJpg(90, filename);
|
||||
}
|
||||
}
|
||||
|
||||
public bool DetectQRCode()
|
||||
{
|
||||
var qrReader = new QRCodeMultiReader();
|
||||
|
||||
var qrReaderHints = new Dictionary<DecodeHintType, object>() {
|
||||
{ DecodeHintType.TRY_HARDER, true }
|
||||
};
|
||||
|
||||
var qrImageSource = new BitmapLuminanceSource((Bitmap)Image);
|
||||
|
||||
var qrBinarizer = new HybridBinarizer(qrImageSource);
|
||||
var qrBinaryBitmap = new BinaryBitmap(qrBinarizer);
|
||||
|
||||
try
|
||||
{
|
||||
qrCodeResult = qrReader.decodeMultiple(qrBinaryBitmap, qrReaderHints)?.FirstOrDefault();
|
||||
qrCodeResultScale = 1F;
|
||||
}
|
||||
catch (ReaderException)
|
||||
{
|
||||
// QR Detection Failed
|
||||
qrCodeResult = null;
|
||||
}
|
||||
|
||||
if (qrCodeResult == null)
|
||||
{
|
||||
var sizePoints = PdfiumDocument.PageSizes[PageIndex];
|
||||
|
||||
// Try at 175%
|
||||
using (var image = PdfiumDocument.Render(PageIndex, (int)(sizePoints.Width * 1.75), (int)(sizePoints.Height * 1.75), 72F, 72F, false))
|
||||
{
|
||||
qrImageSource = new BitmapLuminanceSource((Bitmap)image);
|
||||
|
||||
// Try Entire Image
|
||||
qrBinarizer = new HybridBinarizer(qrImageSource);
|
||||
qrBinaryBitmap = new BinaryBitmap(qrBinarizer);
|
||||
|
||||
try
|
||||
{
|
||||
qrCodeResult = qrReader.decodeMultiple(qrBinaryBitmap, qrReaderHints)?.FirstOrDefault();
|
||||
qrCodeResultScale = 1.75F;
|
||||
}
|
||||
catch (ReaderException)
|
||||
{
|
||||
// QR Detection Failed
|
||||
qrCodeResult = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (qrCodeResult == null)
|
||||
{
|
||||
// Try at 200%
|
||||
using (var image = PdfiumDocument.Render(PageIndex, (int)(sizePoints.Width * 2), (int)(sizePoints.Height * 2), 72F, 72F, false))
|
||||
{
|
||||
qrImageSource = new BitmapLuminanceSource((Bitmap)image);
|
||||
|
||||
// Try Entire Image
|
||||
qrBinarizer = new HybridBinarizer(qrImageSource);
|
||||
qrBinaryBitmap = new BinaryBitmap(qrBinarizer);
|
||||
|
||||
try
|
||||
{
|
||||
qrCodeResult = qrReader.decodeMultiple(qrBinaryBitmap, qrReaderHints)?.FirstOrDefault();
|
||||
qrCodeResultScale = 2F;
|
||||
}
|
||||
catch (ReaderException)
|
||||
{
|
||||
// QR Detection Failed
|
||||
qrCodeResult = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (qrCodeResult != null)
|
||||
{
|
||||
// Detect Rotation
|
||||
var rotationAngle = Math.Atan2(
|
||||
qrCodeResult.ResultPoints[2].Y - qrCodeResult.ResultPoints[1].Y,
|
||||
qrCodeResult.ResultPoints[2].X - qrCodeResult.ResultPoints[1].X) * 180 / Math.PI;
|
||||
if (rotationAngle <= 45 || rotationAngle > 315)
|
||||
{
|
||||
detectedRotation = RotateFlipType.RotateNoneFlipNone;
|
||||
}
|
||||
else if (rotationAngle <= 135)
|
||||
{
|
||||
detectedRotation = RotateFlipType.Rotate270FlipNone;
|
||||
}
|
||||
else if (rotationAngle <= 225)
|
||||
{
|
||||
detectedRotation = RotateFlipType.Rotate180FlipNone;
|
||||
}
|
||||
else
|
||||
{
|
||||
detectedRotation = RotateFlipType.Rotate90FlipNone;
|
||||
}
|
||||
|
||||
// Reset Thumbnail
|
||||
if (renderedThumbnail != null)
|
||||
{
|
||||
renderedThumbnail.Dispose();
|
||||
renderedThumbnail = null;
|
||||
}
|
||||
|
||||
// Try binary encoding (from v2)
|
||||
if (qrCodeResult.ResultMetadata.ContainsKey(ResultMetadataType.BYTE_SEGMENTS))
|
||||
{
|
||||
var byteSegments = (List<byte[]>)qrCodeResult.ResultMetadata[ResultMetadataType.BYTE_SEGMENTS];
|
||||
var qrBytes = byteSegments[0];
|
||||
if (DocumentUniqueIdentifier.IsDocumentUniqueIdentifier(qrBytes))
|
||||
{
|
||||
Identifier = DocumentUniqueIdentifier.Parse(Database, qrBytes);
|
||||
}
|
||||
}
|
||||
// Fall back to v1
|
||||
if (Identifier == null)
|
||||
{
|
||||
Identifier = DocumentUniqueIdentifier.Parse(Database, qrCodeResult.Text);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Bitmap GetAttachmentThumbnail()
|
||||
{
|
||||
var thumbnail = renderedImage.ResizeImage(48, 48, Brushes.White);
|
||||
|
||||
// Draw Rotation
|
||||
if (detectedRotation != RotateFlipType.RotateNoneFlipNone)
|
||||
{
|
||||
thumbnail.RotateFlip(detectedRotation);
|
||||
}
|
||||
|
||||
// Add PDF Icon overlay
|
||||
using (Image mimeTypeIcon = Disco.Services.Properties.Resources.MimeType_pdf16)
|
||||
{
|
||||
thumbnail.EmbedIconOverlay(mimeTypeIcon);
|
||||
}
|
||||
|
||||
return thumbnail;
|
||||
}
|
||||
|
||||
private Image GetRenderedImage()
|
||||
{
|
||||
if (renderedImage == null)
|
||||
{
|
||||
var pageSize = PdfiumDocument.PageSizes[PageIndex];
|
||||
|
||||
renderedImage = PdfiumDocument.Render(PageIndex, (int)pageSize.Width, (int)pageSize.Height, 72F, 72F, true);
|
||||
}
|
||||
|
||||
return renderedImage;
|
||||
}
|
||||
|
||||
private Bitmap GetRenderedThumbnail()
|
||||
{
|
||||
if (renderedThumbnail == null)
|
||||
{
|
||||
renderedThumbnail = GetRenderedImage().ResizeImage(256, 256, Brushes.White);
|
||||
|
||||
if (qrCodeResult != null && qrCodeResult.ResultPoints.Length == 4)
|
||||
{
|
||||
float thumbnailScale;
|
||||
var thumbnailOffset = renderedImage.CalculateResize(renderedThumbnail.Width, renderedThumbnail.Height, out thumbnailScale);
|
||||
thumbnailScale = thumbnailScale / qrCodeResultScale;
|
||||
|
||||
using (Graphics thumbnailGraphics = Graphics.FromImage(renderedThumbnail))
|
||||
{
|
||||
// Draw Square on QR Code
|
||||
var linePoints = qrCodeResult.ResultPoints.Select(p => new Point((int)(thumbnailOffset.X + (p.X * thumbnailScale)), (int)(thumbnailOffset.Y + (p.Y * thumbnailScale)))).ToList();
|
||||
using (GraphicsPath graphicsPath = new GraphicsPath())
|
||||
{
|
||||
for (int linePointIndex = 0; linePointIndex < (linePoints.Count - 1); linePointIndex++)
|
||||
graphicsPath.AddLine(linePoints[linePointIndex], linePoints[linePointIndex + 1]);
|
||||
|
||||
graphicsPath.AddLine(linePoints[linePoints.Count - 1], linePoints[0]);
|
||||
|
||||
using (SolidBrush graphicsBrush = new SolidBrush(Color.FromArgb(128, 255, 0, 0)))
|
||||
thumbnailGraphics.FillPath(graphicsBrush, graphicsPath);
|
||||
|
||||
using (Pen graphicsPen = new Pen(Color.FromArgb(200, 255, 0, 0), 2))
|
||||
thumbnailGraphics.DrawPath(graphicsPen, graphicsPath);
|
||||
}
|
||||
|
||||
// Draw Rotation
|
||||
if (detectedRotation != RotateFlipType.RotateNoneFlipNone)
|
||||
{
|
||||
renderedThumbnail.RotateFlip(detectedRotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return renderedThumbnail;
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
if (renderedImage != null)
|
||||
renderedImage.Dispose();
|
||||
if (renderedThumbnail != null)
|
||||
renderedThumbnail.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Logging;
|
||||
using Disco.Services.Users;
|
||||
using PdfSharp.Pdf;
|
||||
using PdfSharp.Pdf.IO;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Services.Documents.AttachmentImport
|
||||
{
|
||||
public static class Importer
|
||||
{
|
||||
public static void Import(DiscoDataContext Database, string SessionId, string Filename)
|
||||
{
|
||||
var dataStoreUnassignedLocation = DataStore.CreateLocation(Database, "DocumentDropBox_Unassigned");
|
||||
var dataStoreSessionPagesCacheLocation = DataStore.CreateLocation(Database, "Cache\\DocumentDropBox_SessionPages");
|
||||
var documentTemplates = Database.DocumentTemplates.ToArray();
|
||||
|
||||
if (!File.Exists(Filename))
|
||||
{
|
||||
DocumentsLog.LogImportWarning(SessionId, string.Format("File not found: {0}", Filename));
|
||||
throw new FileNotFoundException("Document Not Found", Filename);
|
||||
}
|
||||
|
||||
DocumentsLog.LogImportProgress(SessionId, 0, "Reading File");
|
||||
|
||||
List<ImportPage> pages = null;
|
||||
List<ImportPage> assignedPages;
|
||||
double progressInterval;
|
||||
|
||||
try
|
||||
{
|
||||
// Use Pdfium to Rasterize and Detect Pages
|
||||
using (var importFileStream = new FileStream(Filename, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
|
||||
{
|
||||
using (var pdfiumDocument = PdfiumViewer.PdfDocument.Load(importFileStream))
|
||||
{
|
||||
progressInterval = 70D / pdfiumDocument.PageCount;
|
||||
pages = new List<ImportPage>(pdfiumDocument.PageCount);
|
||||
assignedPages = new List<ImportPage>(pdfiumDocument.PageCount);
|
||||
|
||||
// Rasterize and Detect Pages
|
||||
for (int pageIndex = 0; pageIndex < pdfiumDocument.PageCount; pageIndex++)
|
||||
{
|
||||
var pageNumber = pageIndex + 1;
|
||||
|
||||
DocumentsLog.LogImportProgress(SessionId, (int)(pageIndex * progressInterval), $"Processing Page {pageNumber} of {pdfiumDocument.PageCount}");
|
||||
DocumentsLog.LogImportPageStarting(SessionId, pageNumber);
|
||||
|
||||
var page = new ImportPage(Database, SessionId, pdfiumDocument, pageIndex);
|
||||
pages.Add(page);
|
||||
|
||||
// Write Session Thumbnail
|
||||
page.WriteThumbnailSessionCache();
|
||||
DocumentsLog.LogImportPageImageUpdate(SessionId, pageNumber);
|
||||
|
||||
// Detect Image
|
||||
if (page.DetectQRCode())
|
||||
{
|
||||
// Write updated session thumbnail
|
||||
page.WriteThumbnailSessionCache();
|
||||
DocumentsLog.LogImportPageImageUpdate(SessionId, pageNumber);
|
||||
var identifier = page.Identifier;
|
||||
DocumentsLog.LogImportPageDetected(SessionId, pageNumber, identifier.DocumentTemplateId, identifier.DocumentTemplate.Description, identifier.DocumentTemplate.Scope, identifier.TargetId, identifier.Target.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
page.WriteUndetectedImages();
|
||||
DocumentsLog.LogImportPageUndetected(SessionId, pageNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Use PdfSharp to Import and Build Documents
|
||||
using (var importFileStream = new FileStream(Filename, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
|
||||
{
|
||||
using (var pdfSharpDocument = PdfReader.Open(importFileStream, PdfDocumentOpenMode.Import))
|
||||
{
|
||||
// Assign Pages
|
||||
var documents = pages
|
||||
.Where(p => p.IsValidAttachment)
|
||||
.GroupBy(p => p.Identifier.DocumentGroupingId)
|
||||
.ToList();
|
||||
|
||||
if (documents.Count > 0)
|
||||
{
|
||||
progressInterval = 20D / documents.Count;
|
||||
|
||||
foreach (var document in documents)
|
||||
{
|
||||
var documentPages = document.OrderBy(p => p.Identifier.PageIndex).ToList();
|
||||
var documentPageFirst = documentPages.First();
|
||||
|
||||
DocumentsLog.LogImportProgress(SessionId, (int)(70D + (documents.IndexOf(document) * progressInterval)), $"Importing Documents {documents.IndexOf(document) + 1} of {documents.Count}");
|
||||
|
||||
using (MemoryStream msBuilder = new MemoryStream())
|
||||
{
|
||||
using (var pdfSharpDocumentOutput = new PdfDocument())
|
||||
{
|
||||
foreach (var documentPage in documentPages)
|
||||
{
|
||||
var pdfSharpImportPage = pdfSharpDocument.Pages[documentPage.PageIndex];
|
||||
var importedPage = pdfSharpDocumentOutput.AddPage(pdfSharpImportPage);
|
||||
|
||||
importedPage.Rotate = documentPage.Rotation;
|
||||
}
|
||||
|
||||
pdfSharpDocumentOutput.Save(msBuilder, false);
|
||||
}
|
||||
|
||||
msBuilder.Position = 0;
|
||||
using (var attachmentThumbnail = documentPageFirst.GetAttachmentThumbnail())
|
||||
{
|
||||
documentPageFirst.Identifier.ImportPdfAttachment(Database, msBuilder, attachmentThumbnail);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write Unassigned Pages
|
||||
var unassignedPages = pages
|
||||
.Where(p => !p.IsValidAttachment)
|
||||
.ToList();
|
||||
|
||||
if (unassignedPages.Count > 0)
|
||||
{
|
||||
progressInterval = 10D / unassignedPages.Count;
|
||||
|
||||
foreach (var documentPage in unassignedPages)
|
||||
{
|
||||
DocumentsLog.LogImportProgress(SessionId, (int)(90 + (unassignedPages.IndexOf(documentPage) * progressInterval)), string.Format("Processing Undetected Pages {0} of {1}", unassignedPages.IndexOf(documentPage) + 1, unassignedPages.Count));
|
||||
|
||||
using (var pdfSharpDocumentOutput = new PdfDocument())
|
||||
{
|
||||
var pdfSharpImportPage = pdfSharpDocument.Pages[documentPage.PageIndex];
|
||||
pdfSharpDocumentOutput.AddPage(pdfSharpImportPage);
|
||||
|
||||
var filename = Path.Combine(dataStoreUnassignedLocation, $"{SessionId}_{documentPage.PageIndex + 1}.pdf");
|
||||
|
||||
pdfSharpDocumentOutput.Save(filename);
|
||||
|
||||
DocumentsLog.LogImportPageUndetectedStored(SessionId, documentPage.PageIndex + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Dispose of pages
|
||||
if (pages != null && pages.Count != 0)
|
||||
{
|
||||
for (int i = 0; i < pages.Count; i++)
|
||||
{
|
||||
pages[i].Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ImportPdfAttachment(this DocumentUniqueIdentifier Identifier, DiscoDataContext Database, string PdfFilename)
|
||||
{
|
||||
return ImportPdfAttachment(Identifier, Database, PdfFilename, null);
|
||||
}
|
||||
|
||||
public static bool ImportPdfAttachment(this DocumentUniqueIdentifier Identifier, DiscoDataContext Database, string PdfFilename, Image Thumbnail)
|
||||
{
|
||||
using (var pdfStream = File.OpenRead(PdfFilename))
|
||||
{
|
||||
return ImportPdfAttachment(Identifier, Database, pdfStream, Thumbnail);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ImportPdfAttachment(this DocumentUniqueIdentifier Identifier, DiscoDataContext Database, Stream PdfContent)
|
||||
{
|
||||
return ImportPdfAttachment(Identifier, Database, PdfContent, null);
|
||||
}
|
||||
|
||||
public static bool ImportPdfAttachment(this DocumentUniqueIdentifier Identifier, DiscoDataContext Database, Stream PdfContent, Image Thumbnail)
|
||||
{
|
||||
string filename;
|
||||
string comments;
|
||||
IAttachment attachment;
|
||||
|
||||
if (Identifier.DocumentTemplate == null)
|
||||
{
|
||||
filename = $"{Identifier.Target.AttachmentReferenceId.Replace('\\', '_')}_{Identifier.TimeStamp:yyyyMMdd-HHmmss}.pdf";
|
||||
comments = $"Uploaded: {Identifier.TimeStamp:s}";
|
||||
}
|
||||
else
|
||||
{
|
||||
filename = $"{Identifier.DocumentTemplateId}_{Identifier.TimeStamp:yyyyMMdd-HHmmss}.pdf";
|
||||
comments = string.Format("Generated: {0:s}", Identifier.TimeStamp);
|
||||
}
|
||||
|
||||
User creatorUser = UserService.GetUser(Identifier.CreatorId, Database);
|
||||
if (creatorUser == null)
|
||||
{
|
||||
// No Creator User (or Username invalid)
|
||||
creatorUser = UserService.CurrentUser;
|
||||
}
|
||||
|
||||
switch (Identifier.AttachmentType)
|
||||
{
|
||||
case AttachmentTypes.Device:
|
||||
Device d = (Device)Identifier.Target;
|
||||
attachment = d.CreateAttachment(Database, creatorUser, filename, DocumentTemplate.PdfMimeType, comments, PdfContent, Identifier.DocumentTemplate, Thumbnail);
|
||||
break;
|
||||
case AttachmentTypes.Job:
|
||||
Job j = (Job)Identifier.Target;
|
||||
attachment = j.CreateAttachment(Database, creatorUser, filename, DocumentTemplate.PdfMimeType, comments, PdfContent, Identifier.DocumentTemplate, Thumbnail);
|
||||
break;
|
||||
case AttachmentTypes.User:
|
||||
User u = (User)Identifier.Target;
|
||||
attachment = u.CreateAttachment(Database, creatorUser, filename, DocumentTemplate.PdfMimeType, comments, PdfContent, Identifier.DocumentTemplate, Thumbnail);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Identifier.DocumentTemplate != null && !string.IsNullOrWhiteSpace(Identifier.DocumentTemplate.OnImportAttachmentExpression))
|
||||
{
|
||||
try
|
||||
{
|
||||
var expressionResult = Identifier.DocumentTemplate.EvaluateOnAttachmentImportExpression(attachment, Database, creatorUser, Identifier.TimeStamp);
|
||||
DocumentsLog.LogImportAttachmentExpressionEvaluated(Identifier.DocumentTemplate, Identifier.Target, attachment, expressionResult);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SystemLog.LogException("Document Importer - OnImportAttachmentExpression", ex);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Services.Logging;
|
||||
using Disco.Services.Tasks;
|
||||
using Quartz;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Disco.Services.Documents.AttachmentImport
|
||||
{
|
||||
public class ImporterCleanCacheJob : ScheduledTask
|
||||
{
|
||||
public override string TaskName { get { return "Document Importer - Clean Cache Task"; } }
|
||||
|
||||
public override bool SingleInstanceTask { get { return true; } }
|
||||
public override bool CancelInitiallySupported { get { return false; } }
|
||||
public override bool LogExceptionsOnly { get { return true; } }
|
||||
|
||||
public override void InitalizeScheduledTask(DiscoDataContext Database)
|
||||
{
|
||||
// Trigger Daily @ 12:30am
|
||||
TriggerBuilder triggerBuilder = TriggerBuilder.Create().WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(0, 30));
|
||||
|
||||
this.ScheduleTask(triggerBuilder);
|
||||
}
|
||||
|
||||
protected override void ExecuteTask()
|
||||
{
|
||||
string dataStoreLocation;
|
||||
using (DiscoDataContext database = new DiscoDataContext())
|
||||
{
|
||||
dataStoreLocation = DataStore.CreateLocation(database, @"Cache\DocumentDropBox_SessionPages");
|
||||
}
|
||||
|
||||
int deleteCount = 0;
|
||||
int errorCount = 0;
|
||||
|
||||
var dataStoreInfo = new DirectoryInfo(dataStoreLocation);
|
||||
|
||||
foreach (var file in dataStoreInfo.GetFiles())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (file.CreationTime < DateTime.Today)
|
||||
{
|
||||
file.Delete();
|
||||
deleteCount++;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
errorCount++;
|
||||
}
|
||||
}
|
||||
|
||||
SystemLog.LogInformation(
|
||||
$"Cleared DocumentDropBox_SessionPages Cache, Deleted {deleteCount} File/s, with {errorCount} Error/s",
|
||||
deleteCount,
|
||||
errorCount
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using Disco.Data.Repository;
|
||||
using Exceptionless;
|
||||
using Quartz;
|
||||
using Quartz.Impl.Triggers;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Disco.Services.Documents.AttachmentImport
|
||||
{
|
||||
[PersistJobDataAfterExecution]
|
||||
public class ImporterJob : IJob
|
||||
{
|
||||
public void Execute(IJobExecutionContext context)
|
||||
{
|
||||
var sessionId = context.JobDetail.JobDataMap["SessionId"] as string;
|
||||
if (sessionId == null)
|
||||
{
|
||||
sessionId = Guid.NewGuid().ToString();
|
||||
context.JobDetail.JobDataMap["SessionId"] = sessionId;
|
||||
}
|
||||
|
||||
var filename = context.JobDetail.JobDataMap["Filename"] as string;
|
||||
var retryCount = (int)context.JobDetail.JobDataMap["RetryCount"];
|
||||
|
||||
using (DiscoDataContext database = new DiscoDataContext())
|
||||
{
|
||||
try
|
||||
{
|
||||
DocumentsLog.LogImportStarting(sessionId, Path.GetFileName(filename));
|
||||
|
||||
// Returns null if unrecoverable error (eg. Not matched document)
|
||||
Importer.Import(database, sessionId, filename);
|
||||
|
||||
// Success - Delete File
|
||||
if (File.Exists(filename))
|
||||
File.Delete(filename);
|
||||
|
||||
DocumentsLog.LogImportFinished(sessionId);
|
||||
|
||||
// All Done - Delete job
|
||||
context.Scheduler.DeleteJob(context.JobDetail.Key);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
// File not found - Delete job and don't reschedule
|
||||
context.Scheduler.DeleteJob(context.JobDetail.Key);
|
||||
DocumentsLog.LogImportFinished(sessionId);
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ex.ToExceptionless().Submit();
|
||||
|
||||
// Retry 18 times (for 3 minutes)
|
||||
if (retryCount < 18)
|
||||
{
|
||||
context.JobDetail.JobDataMap["RetryCount"] = ++retryCount;
|
||||
DocumentsLog.LogImportWarning(sessionId, string.Format("{0}; Will try again in 10 Seconds", ex.Message));
|
||||
// Reschedule Job for 10 seconds
|
||||
var trig = new SimpleTriggerImpl(Guid.NewGuid().ToString(), new DateTimeOffset(DateTime.Now.AddSeconds(10)));
|
||||
context.Scheduler.RescheduleJob(context.Trigger.Key, trig);
|
||||
}
|
||||
else
|
||||
{
|
||||
// To Many Errors
|
||||
DocumentsLog.LogImportError(sessionId, $"To many errors occurred trying to import (SessionId: {sessionId})");
|
||||
// Move to Errors Folder
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
try
|
||||
{
|
||||
var folderError = DataStore.CreateLocation(database, "DocumentDropBox_Errors");
|
||||
var filenameError = Path.Combine(folderError, Path.GetFileName(filename));
|
||||
var filenameErrorCount = 0;
|
||||
while (File.Exists(filenameError))
|
||||
{
|
||||
filenameError = Path.Combine(folderError, $"{Path.GetFileNameWithoutExtension(filename)} ({++filenameErrorCount}){Path.GetExtension(filename)}");
|
||||
}
|
||||
File.Move(filename, filenameError);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore Errors
|
||||
}
|
||||
}
|
||||
DocumentsLog.LogImportFinished(sessionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class DocumentTemplateActionExtensions
|
||||
{
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using System.IO;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class DocumentTemplateDataStoreExtensions
|
||||
{
|
||||
public static string RepositoryFilename(this DocumentTemplate dt, DiscoDataContext Database)
|
||||
{
|
||||
return Path.Combine(DataStore.CreateLocation(Database, "DocumentTemplates"), string.Format("{0}.pdf", dt.Id));
|
||||
}
|
||||
public static string SavePdfTemplate(this DocumentTemplate dt, DiscoDataContext Database, Stream TemplateFile)
|
||||
{
|
||||
string filePath = dt.RepositoryFilename(Database);
|
||||
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
TemplateFile.CopyTo(fs);
|
||||
}
|
||||
Expressions.ExpressionCache.InvalidModule(string.Format(DocumentTemplateExpressionExtensions.CacheTemplate, dt.Id));
|
||||
return filePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Models.Services.Documents;
|
||||
using Disco.Services.Expressions;
|
||||
using System;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class DocumentTemplateExpressionExtensions
|
||||
{
|
||||
internal const string CacheTemplate = "DocumentTemplate_{0}";
|
||||
|
||||
public static Expression FilterExpressionFromCache(this DocumentTemplate dt)
|
||||
{
|
||||
return ExpressionCache.GetValue("DocumentTemplate_FilterExpression", dt.Id, () => { return Expression.TokenizeSingleDynamic(null, dt.FilterExpression, 0); });
|
||||
}
|
||||
|
||||
public static void FilterExpressionInvalidateCache(this DocumentTemplate dt)
|
||||
{
|
||||
ExpressionCache.InvalidateKey("DocumentTemplate_FilterExpression", dt.Id);
|
||||
}
|
||||
|
||||
public static bool FilterExpressionMatches(this DocumentTemplate dt, object Data, DiscoDataContext Database, User User, System.DateTime TimeStamp, DocumentState State)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(dt.FilterExpression))
|
||||
{
|
||||
Expression compiledExpression = dt.FilterExpressionFromCache();
|
||||
System.Collections.IDictionary evaluatorVariables = Expression.StandardVariables(dt, Database, User, TimeStamp, State);
|
||||
try
|
||||
{
|
||||
object er = compiledExpression.EvaluateFirst<object>(Data, evaluatorVariables);
|
||||
if (er is bool)
|
||||
{
|
||||
return (bool)er;
|
||||
}
|
||||
bool erBool;
|
||||
if (bool.TryParse(er.ToString(), out erBool))
|
||||
{
|
||||
return erBool;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Expression OnImportAttachmentExpressionFromCache(this DocumentTemplate dt)
|
||||
{
|
||||
return ExpressionCache.GetValue("DocumentTemplate_OnImportExpression", dt.Id, () => { return Expression.TokenizeSingleDynamic(null, dt.OnImportAttachmentExpression, 0); });
|
||||
}
|
||||
|
||||
public static void OnImportAttachmentExpressionInvalidateCache(this DocumentTemplate dt)
|
||||
{
|
||||
ExpressionCache.InvalidateKey("DocumentTemplate_OnImportExpression", dt.Id);
|
||||
}
|
||||
|
||||
public static string EvaluateOnAttachmentImportExpression(this DocumentTemplate dt, object Data, DiscoDataContext Database, User User, DateTime TimeStamp)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(dt.OnImportAttachmentExpression))
|
||||
{
|
||||
Expression compiledExpression = dt.OnImportAttachmentExpressionFromCache();
|
||||
System.Collections.IDictionary evaluatorVariables = Expression.StandardVariables(dt, Database, User, TimeStamp, null);
|
||||
try
|
||||
{
|
||||
object result = compiledExpression.EvaluateFirst<object>(Data, evaluatorVariables);
|
||||
if (result == null)
|
||||
return null;
|
||||
else
|
||||
return result.ToString();
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Expression OnGenerateExpressionFromCache(this DocumentTemplate dt)
|
||||
{
|
||||
return ExpressionCache.GetValue("DocumentTemplate_OnGenerateExpression", dt.Id, () => { return Expression.TokenizeSingleDynamic(null, dt.OnGenerateExpression, 0); });
|
||||
}
|
||||
|
||||
public static void OnGenerateExpressionInvalidateCache(this DocumentTemplate dt)
|
||||
{
|
||||
ExpressionCache.InvalidateKey("DocumentTemplate_OnGenerateExpression", dt.Id);
|
||||
}
|
||||
|
||||
public static string EvaluateOnGenerateExpression(this DocumentTemplate dt, object Data, DiscoDataContext Database, User User, DateTime TimeStamp, DocumentState State)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(dt.OnGenerateExpression))
|
||||
{
|
||||
Expression compiledExpression = dt.OnGenerateExpressionFromCache();
|
||||
System.Collections.IDictionary evaluatorVariables = Expression.StandardVariables(dt, Database, User, TimeStamp, State);
|
||||
try
|
||||
{
|
||||
object result = compiledExpression.EvaluateFirst<object>(Data, evaluatorVariables);
|
||||
return result.ToString();
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,425 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Disco.Services.Documents
|
||||
{
|
||||
public class DocumentUniqueIdentifier
|
||||
{
|
||||
private const int CurrentVersion = 2;
|
||||
private const byte MagicNumber = 0xC4;
|
||||
private DiscoDataContext database;
|
||||
|
||||
public int Version { get; private set; }
|
||||
public short DeploymentChecksum { get; private set; }
|
||||
public string DocumentTemplateId { get; private set; }
|
||||
public string TargetId { get; private set; }
|
||||
public string CreatorId { get; private set; }
|
||||
public DateTime TimeStamp { get; private set; }
|
||||
public int PageIndex { get; private set; }
|
||||
|
||||
private DocumentTemplate documentTemplate;
|
||||
private AttachmentTypes? attachmentType;
|
||||
private IAttachmentTarget target;
|
||||
private User creator;
|
||||
|
||||
public DocumentTemplate DocumentTemplate
|
||||
{
|
||||
get
|
||||
{
|
||||
if (documentTemplate == null && !string.IsNullOrWhiteSpace(DocumentTemplateId) && !DocumentTemplateId.StartsWith("--", StringComparison.Ordinal))
|
||||
{
|
||||
documentTemplate = database.DocumentTemplates.Find(DocumentTemplateId);
|
||||
if (documentTemplate != null)
|
||||
{
|
||||
attachmentType = documentTemplate.AttachmentType;
|
||||
}
|
||||
}
|
||||
|
||||
return documentTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
public string DocumentGroupingId
|
||||
{
|
||||
get
|
||||
{
|
||||
// Unique identifier to distinguish this document from others (but keep pages together)
|
||||
return $"{TimeStamp.Ticks}|{TargetId}|{DocumentTemplateId}|{CreatorId}|{Version}|{DeploymentChecksum}";
|
||||
}
|
||||
}
|
||||
|
||||
public AttachmentTypes? AttachmentType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!attachmentType.HasValue)
|
||||
{
|
||||
if (DocumentTemplateId.StartsWith("--", StringComparison.Ordinal))
|
||||
{
|
||||
if (DocumentTemplateId.Equals("--DEVICE", StringComparison.Ordinal))
|
||||
{
|
||||
attachmentType = AttachmentTypes.Device;
|
||||
}
|
||||
else if (DocumentTemplateId.Equals("--JOB", StringComparison.Ordinal))
|
||||
{
|
||||
attachmentType = AttachmentTypes.Job;
|
||||
}
|
||||
else if (DocumentTemplateId.Equals("--USER", StringComparison.Ordinal))
|
||||
{
|
||||
attachmentType = AttachmentTypes.User;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var dt = DocumentTemplate;
|
||||
if (dt != null)
|
||||
{
|
||||
attachmentType = dt.AttachmentType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return attachmentType;
|
||||
}
|
||||
}
|
||||
|
||||
public IAttachmentTarget Target
|
||||
{
|
||||
get
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
switch (AttachmentType)
|
||||
{
|
||||
case AttachmentTypes.Device:
|
||||
target = database.Devices.Find(TargetId);
|
||||
break;
|
||||
case AttachmentTypes.Job:
|
||||
target = database.Jobs.Find(int.Parse(TargetId));
|
||||
break;
|
||||
case AttachmentTypes.User:
|
||||
target = database.Users.Find(ActiveDirectory.ParseDomainAccountId(TargetId));
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("Unexpected Attachment Type", nameof(AttachmentType));
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
public User Creator
|
||||
{
|
||||
get
|
||||
{
|
||||
if (creator == null)
|
||||
{
|
||||
creator = database.Users.Find(ActiveDirectory.ParseDomainAccountId(CreatorId));
|
||||
}
|
||||
return creator;
|
||||
}
|
||||
}
|
||||
|
||||
private DocumentUniqueIdentifier(DiscoDataContext Database, int Version, short DeploymentChecksum, string DocumentTemplateId, string TargetId, string CreatorId, DateTime TimeStamp, int PageIndex, AttachmentTypes? AttachmentType)
|
||||
{
|
||||
this.database = Database;
|
||||
this.Version = Version;
|
||||
this.DeploymentChecksum = DeploymentChecksum;
|
||||
this.DocumentTemplateId = DocumentTemplateId;
|
||||
this.attachmentType = AttachmentType;
|
||||
this.TargetId = TargetId;
|
||||
this.CreatorId = ActiveDirectory.ParseDomainAccountId(CreatorId);
|
||||
this.TimeStamp = TimeStamp;
|
||||
this.PageIndex = PageIndex;
|
||||
}
|
||||
|
||||
public string ToQRCodeString()
|
||||
{
|
||||
return $"Disco|1|{DocumentTemplate.Id}|{TargetId}|{CreatorId}|{TimeStamp:s}|{PageIndex}";
|
||||
}
|
||||
|
||||
public string ToJson()
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
using (var stringWriter = new StringWriter(builder))
|
||||
{
|
||||
using (var writer = new JsonTextWriter(stringWriter))
|
||||
{
|
||||
writer.Formatting = Formatting.Indented;
|
||||
writer.WriteStartObject();
|
||||
writer.WritePropertyName("version");
|
||||
writer.WriteValue(CurrentVersion);
|
||||
writer.WritePropertyName("attachmentType");
|
||||
writer.WriteValue(AttachmentType.Value.ToString());
|
||||
writer.WritePropertyName("documentTemplateId");
|
||||
writer.WriteValue(DocumentTemplateId);
|
||||
writer.WritePropertyName("targetId");
|
||||
writer.WriteValue(TargetId);
|
||||
writer.WritePropertyName("creatorId");
|
||||
writer.WriteValue(CreatorId);
|
||||
writer.WritePropertyName("timestamp");
|
||||
writer.WriteValue(TimeStamp);
|
||||
writer.WritePropertyName("pageIndex");
|
||||
writer.WriteValue(PageIndex);
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public byte[] ToQRCodeBytes()
|
||||
{
|
||||
// Byte | Meaning
|
||||
// 0 | magic number = 0x0D
|
||||
// 1 | bits 0-3 = version; 4-7 = flags (1 = has document template id, 2 = device attachment,
|
||||
// | 4 = job attachment, 8 = user attachment)
|
||||
// 2-3 | deployment checksum (int16)
|
||||
// 4-7 | timestamp (uint32 unix epoch)
|
||||
// 8-9 | page index (uint16)
|
||||
// 10 | creator id length
|
||||
// 11-? | creator id (UTF8)
|
||||
// ? | target id length
|
||||
// ?-? | target id
|
||||
// ? | document template id length (optional based on flag)
|
||||
// ?-? | document template id (UTF8, optional based on flag)
|
||||
|
||||
var encoding = Encoding.UTF8;
|
||||
byte flags = 0;
|
||||
|
||||
switch (AttachmentType)
|
||||
{
|
||||
case AttachmentTypes.Device:
|
||||
flags = 2;
|
||||
break;
|
||||
case AttachmentTypes.Job:
|
||||
flags = 4;
|
||||
break;
|
||||
case AttachmentTypes.User:
|
||||
flags = 8;
|
||||
break;
|
||||
}
|
||||
|
||||
var deploymentChecksumBytes = BitConverter.GetBytes(DeploymentChecksum);
|
||||
var timeStampEpochBytes = BitConverter.GetBytes((uint)(TimeStamp.ToUniversalTime().Subtract(DateTime.FromFileTimeUtc(116444736000000000L)).Ticks / TimeSpan.TicksPerSecond));
|
||||
var pageIndexBytes = BitConverter.GetBytes((ushort)PageIndex);
|
||||
|
||||
|
||||
// magic number (1) + version/flags (1) + deployment checksum (2) + timestamp (4) +
|
||||
// page index (2) + creator id length (1) + target id length (1)
|
||||
var requiredBytes = 12;
|
||||
var creatorIdLength = encoding.GetByteCount(CreatorId);
|
||||
var targetIdLength = encoding.GetByteCount(TargetId);
|
||||
var documentTemplateIdLength = 0;
|
||||
|
||||
if (DocumentTemplateId != null)
|
||||
{
|
||||
flags |= 1;
|
||||
requiredBytes++;
|
||||
documentTemplateIdLength = encoding.GetByteCount(DocumentTemplateId);
|
||||
}
|
||||
|
||||
int position = 0;
|
||||
var result = new byte[requiredBytes];
|
||||
|
||||
// magic number
|
||||
result[position++] = MagicNumber;
|
||||
// version & flags
|
||||
result[position++] = (byte)(2 | (flags << 4));
|
||||
// deployment checksum
|
||||
deploymentChecksumBytes.CopyTo(result, position);
|
||||
position += 2;
|
||||
// timestamp
|
||||
timeStampEpochBytes.CopyTo(result, position);
|
||||
position += 4;
|
||||
// page index
|
||||
pageIndexBytes.CopyTo(result, position);
|
||||
position += 2;
|
||||
// creator id length
|
||||
result[position++] = (byte)creatorIdLength;
|
||||
// creator id
|
||||
position += encoding.GetBytes(CreatorId, 0, CreatorId.Length, result, position);
|
||||
// target id length
|
||||
result[position++] = (byte)targetIdLength;
|
||||
// target id
|
||||
position += encoding.GetBytes(TargetId, 0, TargetId.Length, result, position);
|
||||
if (documentTemplateIdLength > 0)
|
||||
{
|
||||
// document template id length
|
||||
result[position++] = (byte)documentTemplateIdLength;
|
||||
// document template id
|
||||
position += encoding.GetBytes(DocumentTemplateId, 0, DocumentTemplateId.Length, result, position);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static DocumentUniqueIdentifier Create(DiscoDataContext Database, DocumentTemplate DocumentTemplate, IAttachmentTarget Target, User Creator, DateTime TimeStamp, int PageIndex)
|
||||
{
|
||||
var deploymentChecksum = Database.DiscoConfiguration.DeploymentChecksum;
|
||||
var documentTemplateId = DocumentTemplate.Id;
|
||||
var targetId = Target.AttachmentReferenceId;
|
||||
var creatorId = Creator.UserId;
|
||||
var attachmentType = DocumentTemplate.AttachmentType;
|
||||
|
||||
var identifier = new DocumentUniqueIdentifier(Database, CurrentVersion, deploymentChecksum, documentTemplateId, targetId, creatorId, TimeStamp, PageIndex, attachmentType);
|
||||
|
||||
identifier.documentTemplate = DocumentTemplate;
|
||||
identifier.target = Target;
|
||||
identifier.creator = Creator;
|
||||
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public static DocumentUniqueIdentifier Create(DiscoDataContext Database, string DocumentTemplateId, string TargetId, string CreatorId, DateTime TimeStamp, int PageIndex)
|
||||
{
|
||||
var deploymentChecksum = Database.DiscoConfiguration.DeploymentChecksum;
|
||||
|
||||
return new DocumentUniqueIdentifier(Database, CurrentVersion, deploymentChecksum, DocumentTemplateId, TargetId, CreatorId, TimeStamp, PageIndex, null);
|
||||
}
|
||||
|
||||
public static DocumentUniqueIdentifier Parse(DiscoDataContext Database, byte[] UniqueIdentifier)
|
||||
{
|
||||
DocumentUniqueIdentifier identifier;
|
||||
if (TryParse(Database, UniqueIdentifier, out identifier))
|
||||
{
|
||||
return identifier;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FormatException("Invalid Document Unique Identifier");
|
||||
}
|
||||
}
|
||||
|
||||
public static DocumentUniqueIdentifier Parse(DiscoDataContext Database, string UniqueIdentifier)
|
||||
{
|
||||
DocumentUniqueIdentifier identifier;
|
||||
if (TryParse(Database, UniqueIdentifier, out identifier))
|
||||
{
|
||||
return identifier;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new FormatException("Invalid Document Unique Identifier");
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryParse(DiscoDataContext Database, string UniqueIdentifier, out DocumentUniqueIdentifier Identifier)
|
||||
{
|
||||
if (IsDocumentUniqueIdentifier(UniqueIdentifier))
|
||||
{
|
||||
var components = UniqueIdentifier.Split('|');
|
||||
|
||||
// Version 0, Version 1 Handling
|
||||
if ((components.Length == 7 || components.Length == 6) &&
|
||||
(components[1].Equals("1", StringComparison.Ordinal) || components[1].Equals("AT", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
var documentTemplateId = components[2];
|
||||
var targetId = components[3];
|
||||
var creatorId = components[4];
|
||||
var timeStamp = DateTime.Parse(components[5]);
|
||||
var page = 0;
|
||||
if (components.Length == 7)
|
||||
{
|
||||
page = int.Parse(components[6]);
|
||||
}
|
||||
|
||||
Identifier = new DocumentUniqueIdentifier(Database, 1, -1, documentTemplateId, targetId, creatorId, timeStamp, page, null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Identifier = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryParse(DiscoDataContext Database, byte[] UniqueIdentifier, out DocumentUniqueIdentifier Identifier)
|
||||
{
|
||||
if (IsDocumentUniqueIdentifier(UniqueIdentifier))
|
||||
{
|
||||
// first 4 bit indicate version
|
||||
var version = UniqueIdentifier[2] & 0x0F;
|
||||
|
||||
// Version 2
|
||||
if (version == 2)
|
||||
{
|
||||
// Byte | Meaning
|
||||
// 0 | magic number = 0x0D
|
||||
// 1 | bits 0-3 = version; 4-7 = flags (1 = has document template id, 2 = device attachment,
|
||||
// | 4 = job attachment, 8 = user attachment)
|
||||
// 2-3 | deployment checksum (int16)
|
||||
// 4-7 | timestamp (uint32 unix epoch)
|
||||
// 8-9 | page index (uint16)
|
||||
// 10 | creator id length
|
||||
// 11-? | creator id (UTF8)
|
||||
// ? | target id length
|
||||
// ?-? | target id
|
||||
// ? | document template id length (optional based on flag)
|
||||
// ?-? | document template id (UTF8, optional based on flag)
|
||||
var encoding = Encoding.UTF8;
|
||||
var position = 2;
|
||||
|
||||
// next 4 bits are flags
|
||||
var flags = UniqueIdentifier[position++] >> 4;
|
||||
|
||||
var deploymentChecksum = BitConverter.ToInt16(UniqueIdentifier, position);
|
||||
position += 2;
|
||||
|
||||
var timeStampEpoch = BitConverter.ToUInt32(UniqueIdentifier, position);
|
||||
position += 4;
|
||||
|
||||
var pageIndex = BitConverter.ToUInt16(UniqueIdentifier, position);
|
||||
position += 2;
|
||||
|
||||
var creatorIdLength = UniqueIdentifier[position++];
|
||||
var creatorId = encoding.GetString(UniqueIdentifier, position, creatorIdLength);
|
||||
position += creatorIdLength;
|
||||
|
||||
var targetIdLength = UniqueIdentifier[position++];
|
||||
var targetId = encoding.GetString(UniqueIdentifier, position, targetIdLength);
|
||||
position += targetIdLength;
|
||||
|
||||
string documentTemplateId = null;
|
||||
|
||||
// Has document template id flag
|
||||
if ((flags & 1) == 1)
|
||||
{
|
||||
var documentTemplateIdLength = UniqueIdentifier[position++];
|
||||
documentTemplateId = encoding.GetString(UniqueIdentifier, position, documentTemplateIdLength);
|
||||
}
|
||||
|
||||
AttachmentTypes? attachmentType = null;
|
||||
if ((flags & 2) == 2)
|
||||
attachmentType = AttachmentTypes.Device;
|
||||
else if ((flags & 4) == 4)
|
||||
attachmentType = AttachmentTypes.Job;
|
||||
else if ((flags & 8) == 8)
|
||||
attachmentType = AttachmentTypes.User;
|
||||
|
||||
var timeStamp = DateTime.FromFileTimeUtc(116444736000000000L).AddTicks(TimeSpan.TicksPerSecond * timeStampEpoch).ToLocalTime();
|
||||
|
||||
Identifier = new DocumentUniqueIdentifier(Database, version, deploymentChecksum, documentTemplateId, targetId, creatorId, timeStamp, pageIndex, attachmentType);
|
||||
}
|
||||
}
|
||||
|
||||
Identifier = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsDocumentUniqueIdentifier(string Identifier)
|
||||
{
|
||||
return Identifier != null && Identifier.StartsWith("Disco|", System.StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool IsDocumentUniqueIdentifier(byte[] Identifier)
|
||||
{
|
||||
// Identifier[0] = 0xC4; Magic number to identify Disco ICT QR Codes
|
||||
return Identifier != null && Identifier.Length > 2 && Identifier[0] == MagicNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Documents;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Disco.Services
|
||||
{
|
||||
public static class DocumentUniqueIdentifierExtensions
|
||||
{
|
||||
|
||||
public static DocumentUniqueIdentifier CreateUniqueIdentifier(this DocumentTemplate Template, DiscoDataContext Database, IAttachmentTarget Target, User Creator, DateTime Timestamp, int PageIndex)
|
||||
{
|
||||
return DocumentUniqueIdentifier.Create(Database, Template, Target, Creator, Timestamp, PageIndex);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,441 @@
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Logging;
|
||||
using Disco.Services.Logging.Models;
|
||||
|
||||
namespace Disco.Services.Documents
|
||||
{
|
||||
public class DocumentsLog : LogBase
|
||||
{
|
||||
|
||||
public enum EventTypeIds
|
||||
{
|
||||
ImportStarting = 10,
|
||||
ImportProgress,
|
||||
ImportFinished,
|
||||
ImportWarning = 15,
|
||||
ImportError,
|
||||
ImportAttachmentExpressionEvaluated = 50,
|
||||
ImportPageStarting = 100,
|
||||
ImportPageImageUpdate = 104,
|
||||
ImportPageProgress,
|
||||
ImportPageDetected = 110,
|
||||
ImportPageUndetected = 115,
|
||||
ImportPageError = 120,
|
||||
ImportPageUndetectedStored = 150,
|
||||
DocumentGenerated = 500,
|
||||
DocumentGeneratedWithExpression
|
||||
}
|
||||
|
||||
private const int _ModuleId = 40;
|
||||
|
||||
public static DocumentsLog Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return (DocumentsLog)LogContext.LogModules[_ModuleId];
|
||||
}
|
||||
}
|
||||
|
||||
public override string ModuleDescription
|
||||
{
|
||||
get
|
||||
{
|
||||
return "Documents";
|
||||
}
|
||||
}
|
||||
public override int ModuleId
|
||||
{
|
||||
get
|
||||
{
|
||||
return _ModuleId;
|
||||
}
|
||||
}
|
||||
public override string ModuleName
|
||||
{
|
||||
get
|
||||
{
|
||||
return "Documents";
|
||||
}
|
||||
}
|
||||
private static void Log(DocumentsLog.EventTypeIds EventTypeId, params object[] Args)
|
||||
{
|
||||
DocumentsLog.Current.Log((int)EventTypeId, Args);
|
||||
}
|
||||
public static void LogImportStarting(string SessionId, string DocumentName)
|
||||
{
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportStarting, new object[]
|
||||
{
|
||||
SessionId,
|
||||
DocumentName
|
||||
});
|
||||
}
|
||||
public static void LogImportProgress(string SessionId, int? Progress, string Status)
|
||||
{
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportProgress, new object[]
|
||||
{
|
||||
SessionId,
|
||||
Progress,
|
||||
Status
|
||||
});
|
||||
}
|
||||
public static void LogImportFinished(string SessionId)
|
||||
{
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportFinished, new object[]
|
||||
{
|
||||
SessionId
|
||||
});
|
||||
}
|
||||
public static void LogImportWarning(string SessionId, string Message)
|
||||
{
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportWarning, new object[]
|
||||
{
|
||||
SessionId,
|
||||
Message
|
||||
});
|
||||
}
|
||||
public static void LogImportError(string SessionId, string Message)
|
||||
{
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportError, new object[]
|
||||
{
|
||||
SessionId,
|
||||
Message
|
||||
});
|
||||
}
|
||||
public static void LogImportAttachmentExpressionEvaluated(DocumentTemplate template, IAttachmentTarget target, IAttachment attachment, string Result)
|
||||
{
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportAttachmentExpressionEvaluated, new object[]
|
||||
{
|
||||
template.Id,
|
||||
target.AttachmentReferenceId,
|
||||
attachment.Id,
|
||||
Result
|
||||
});
|
||||
}
|
||||
public static void LogImportPageStarting(string SessionId, int PageNumber)
|
||||
{
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportPageStarting, new object[]
|
||||
{
|
||||
SessionId,
|
||||
PageNumber
|
||||
});
|
||||
}
|
||||
public static void LogImportPageImageUpdate(string SessionId, int PageNumber)
|
||||
{
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportPageImageUpdate, new object[]
|
||||
{
|
||||
SessionId,
|
||||
PageNumber
|
||||
});
|
||||
}
|
||||
public static void LogImportPageProgress(string SessionId, int PageNumber, int? Progress, string Status)
|
||||
{
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportPageProgress, new object[]
|
||||
{
|
||||
SessionId,
|
||||
PageNumber,
|
||||
Progress,
|
||||
Status
|
||||
});
|
||||
}
|
||||
public static void LogImportPageDetected(string SessionId, int PageNumber, string DocumentTypeId, string DocumentTypeName, string TargetType, string AssignedId, string AssignedName)
|
||||
{
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportPageDetected, new object[]
|
||||
{
|
||||
SessionId,
|
||||
PageNumber,
|
||||
DocumentTypeId,
|
||||
DocumentTypeName,
|
||||
TargetType,
|
||||
AssignedId,
|
||||
AssignedName
|
||||
});
|
||||
}
|
||||
public static void LogImportPageUndetected(string SessionId, int PageNumber)
|
||||
{
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportPageUndetected, new object[]
|
||||
{
|
||||
SessionId,
|
||||
PageNumber
|
||||
});
|
||||
}
|
||||
public static void LogImportPageError(string SessionId, int PageNumber, string Message)
|
||||
{
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportPageError, new object[]
|
||||
{
|
||||
SessionId,
|
||||
PageNumber,
|
||||
Message
|
||||
});
|
||||
}
|
||||
public static void LogImportPageUndetectedStored(string SessionId, int PageNumber)
|
||||
{
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.ImportPageUndetectedStored, new object[]
|
||||
{
|
||||
SessionId,
|
||||
PageNumber
|
||||
});
|
||||
}
|
||||
public static void LogDocumentGenerated(DocumentTemplate Template, Device Device, User Author, string ExpressionResult)
|
||||
{
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGeneratedWithExpression, new object[]
|
||||
{
|
||||
Template.Id,
|
||||
Device.SerialNumber,
|
||||
Author.UserId,
|
||||
ExpressionResult
|
||||
});
|
||||
}
|
||||
public static void LogDocumentGenerated(DocumentTemplate Template, Job Job, User Author, string ExpressionResult)
|
||||
{
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGeneratedWithExpression, new object[]
|
||||
{
|
||||
Template.Id,
|
||||
Job.Id,
|
||||
Author.UserId,
|
||||
ExpressionResult
|
||||
});
|
||||
}
|
||||
public static void LogDocumentGenerated(DocumentTemplate Template, User User, User Author, string ExpressionResult)
|
||||
{
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGeneratedWithExpression, new object[]
|
||||
{
|
||||
Template.Id,
|
||||
User.UserId,
|
||||
Author.UserId,
|
||||
ExpressionResult
|
||||
});
|
||||
}
|
||||
public static void LogDocumentGenerated(DocumentTemplate Template, IAttachmentTarget Data, User Author, string ExpressionResult)
|
||||
{
|
||||
if (Data is Job)
|
||||
LogDocumentGenerated(Template, (Job)Data, Author, ExpressionResult);
|
||||
else if (Data is User)
|
||||
LogDocumentGenerated(Template, (User)Data, Author, ExpressionResult);
|
||||
else if (Data is Device)
|
||||
LogDocumentGenerated(Template, (Device)Data, Author, ExpressionResult);
|
||||
else
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGeneratedWithExpression, new object[]
|
||||
{
|
||||
Template.Id,
|
||||
"UNKNOWN",
|
||||
Author.UserId,
|
||||
ExpressionResult
|
||||
});
|
||||
}
|
||||
public static void LogDocumentGenerated(DocumentTemplate Template, Device Device, User Author)
|
||||
{
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGenerated, new object[]
|
||||
{
|
||||
Template.Id,
|
||||
Device.SerialNumber,
|
||||
Author.UserId
|
||||
});
|
||||
}
|
||||
public static void LogDocumentGenerated(DocumentTemplate Template, Job Job, User Author)
|
||||
{
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGenerated, new object[]
|
||||
{
|
||||
Template.Id,
|
||||
Job.Id,
|
||||
Author.UserId
|
||||
});
|
||||
}
|
||||
public static void LogDocumentGenerated(DocumentTemplate Template, User User, User Author)
|
||||
{
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGenerated, new object[]
|
||||
{
|
||||
Template.Id,
|
||||
User.UserId,
|
||||
Author.UserId
|
||||
});
|
||||
}
|
||||
public static void LogDocumentGenerated(DocumentTemplate Template, object Data, User Author)
|
||||
{
|
||||
if (Data is Job)
|
||||
LogDocumentGenerated(Template, (Job)Data, Author);
|
||||
else if (Data is User)
|
||||
LogDocumentGenerated(Template, (User)Data, Author);
|
||||
else if (Data is Device)
|
||||
LogDocumentGenerated(Template, (Device)Data, Author);
|
||||
else
|
||||
DocumentsLog.Log(DocumentsLog.EventTypeIds.DocumentGenerated, new object[]
|
||||
{
|
||||
Template.Id,
|
||||
"UNKNOWN",
|
||||
Author.UserId
|
||||
});
|
||||
}
|
||||
protected override System.Collections.Generic.List<LogEventType> LoadEventTypes()
|
||||
{
|
||||
return new System.Collections.Generic.List<LogEventType>
|
||||
{
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.ImportStarting,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Import Starting",
|
||||
Format = "Starting import of document: {1} (SessionId: {0})",
|
||||
Severity = (int)LogEventType.Severities.Information,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.ImportProgress,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Import Progress",
|
||||
Format = "Processing: {1}% Complete; Status: {2}",
|
||||
Severity = (int)LogEventType.Severities.Information,
|
||||
UseLive = true,
|
||||
UsePersist = false,
|
||||
UseDisplay = false
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.ImportFinished,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Import Finished",
|
||||
Format = "Import of document complete (SessionId: {0})",
|
||||
Severity = (int)LogEventType.Severities.Information,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.ImportWarning,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Import Warning",
|
||||
Format = "Import Warning: {1} (SessionId: {0})",
|
||||
Severity = (int)LogEventType.Severities.Warning,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.ImportError,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Import Error",
|
||||
Format = "Import Error: {1} (SessionId: {0})",
|
||||
Severity = (int)LogEventType.Severities.Error,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.ImportAttachmentExpressionEvaluated,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Import Attachment Expression Evaluated",
|
||||
Format = "The import attachment expression for '{0}' was evaluated for '{1}' (attachment id: {2}) with the result: {3}",
|
||||
Severity = (int)LogEventType.Severities.Information,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.ImportPageStarting,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Import Page Starting",
|
||||
Format = "Starting import of page: {1} (SessionId: {0})",
|
||||
Severity = (int)LogEventType.Severities.Information,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.ImportPageImageUpdate,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Import Page Image Update",
|
||||
Format = null,
|
||||
Severity = (int)LogEventType.Severities.Information,
|
||||
UseLive = true,
|
||||
UsePersist = false,
|
||||
UseDisplay = false
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.ImportPageProgress,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Import Page Progress",
|
||||
Format = "Processing: Page {1}; {2}% Complete; Status: {3}",
|
||||
Severity = (int)LogEventType.Severities.Information,
|
||||
UseLive = true,
|
||||
UsePersist = false,
|
||||
UseDisplay = false
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.ImportPageDetected,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Import Page Assigned",
|
||||
Format = "Page {1} of type '{3}' assigned to {4}: '{6}'",
|
||||
Severity = (int)LogEventType.Severities.Information,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.ImportPageUndetected,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Import Page Undetected",
|
||||
Format = "Page {1} not detected",
|
||||
Severity = (int)LogEventType.Severities.Warning,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.ImportPageError,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Import Page Error",
|
||||
Format = "Page {1}, Import Error: {2}",
|
||||
Severity = (int)LogEventType.Severities.Error,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.ImportPageUndetectedStored,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Import Page Undetected Stored",
|
||||
Format = null,
|
||||
Severity = (int)LogEventType.Severities.Information,
|
||||
UseLive = true,
|
||||
UsePersist = false,
|
||||
UseDisplay = false
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.DocumentGenerated,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Document Generated",
|
||||
Format = "A '{0}' document was generated for '{1}' by '{2}'",
|
||||
Severity = (int)LogEventType.Severities.Information,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
},
|
||||
new LogEventType
|
||||
{
|
||||
Id = (int)EventTypeIds.DocumentGeneratedWithExpression,
|
||||
ModuleId = _ModuleId,
|
||||
Name = "Document Generated with Expression",
|
||||
Format = "A '{0}' document was generated for '{1}' by '{2}'. The expression returned: {3}",
|
||||
Severity = (int)LogEventType.Severities.Information,
|
||||
UseLive = true,
|
||||
UsePersist = true,
|
||||
UseDisplay = true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user