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:
Gary Sharp
2016-08-26 09:46:35 +10:00
parent 44f6d325db
commit 5ea9a814d6
98 changed files with 3168 additions and 3202 deletions
+5 -1
View File
@@ -16,6 +16,10 @@
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Common.Logging" publicKeyToken="AF08829B84F0328E" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
@@ -46,4 +50,4 @@
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
</configuration>
@@ -0,0 +1,195 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Exceptionless;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Disco.Services
{
public static class AttachmentActionExtensions
{
public static DeviceAttachment CreateAttachment(this Device Device, DiscoDataContext Database, User CreatorUser, string Filename, string MimeType, string Comments, Stream Content, DocumentTemplate DocumentTemplate = null, Image PdfThumbnail = null)
{
if (string.IsNullOrEmpty(MimeType) || MimeType.Equals("unknown/unknown", StringComparison.OrdinalIgnoreCase))
MimeType = Interop.MimeTypes.ResolveMimeType(Filename);
DeviceAttachment da = new DeviceAttachment()
{
DeviceSerialNumber = Device.SerialNumber,
TechUserId = CreatorUser.UserId,
Filename = Filename,
MimeType = MimeType,
Timestamp = DateTime.Now,
Comments = Comments
};
if (DocumentTemplate != null)
da.DocumentTemplateId = DocumentTemplate.Id;
Database.DeviceAttachments.Add(da);
Database.SaveChanges();
da.SaveAttachment(Database, Content);
Content.Position = 0;
if (PdfThumbnail == null)
da.GenerateThumbnail(Database, Content);
else
da.SaveThumbnailAttachment(Database, PdfThumbnail);
return da;
}
public static JobAttachment CreateAttachment(this Job Job, DiscoDataContext Database, User CreatorUser, string Filename, string MimeType, string Comments, Stream Content, DocumentTemplate DocumentTemplate = null, Image PdfThumbnail = null)
{
if (string.IsNullOrEmpty(MimeType) || MimeType.Equals("unknown/unknown", StringComparison.OrdinalIgnoreCase))
MimeType = Interop.MimeTypes.ResolveMimeType(Filename);
JobAttachment ja = new JobAttachment()
{
JobId = Job.Id,
TechUserId = CreatorUser.UserId,
Filename = Filename,
MimeType = MimeType,
Timestamp = DateTime.Now,
Comments = Comments
};
if (DocumentTemplate != null)
ja.DocumentTemplateId = DocumentTemplate.Id;
Database.JobAttachments.Add(ja);
Database.SaveChanges();
ja.SaveAttachment(Database, Content);
Content.Position = 0;
if (PdfThumbnail == null)
ja.GenerateThumbnail(Database, Content);
else
ja.SaveThumbnailAttachment(Database, PdfThumbnail);
return ja;
}
public static UserAttachment CreateAttachment(this User User, DiscoDataContext Database, User CreatorUser, string Filename, string MimeType, string Comments, Stream Content, DocumentTemplate DocumentTemplate = null, Image PdfThumbnail = null)
{
if (string.IsNullOrEmpty(MimeType) || MimeType.Equals("unknown/unknown", StringComparison.OrdinalIgnoreCase))
MimeType = Interop.MimeTypes.ResolveMimeType(Filename);
UserAttachment ua = new UserAttachment()
{
UserId = User.UserId,
TechUserId = CreatorUser.UserId,
Filename = Filename,
MimeType = MimeType,
Timestamp = DateTime.Now,
Comments = Comments
};
if (DocumentTemplate != null)
ua.DocumentTemplateId = DocumentTemplate.Id;
Database.UserAttachments.Add(ua);
Database.SaveChanges();
ua.SaveAttachment(Database, Content);
Content.Position = 0;
if (PdfThumbnail == null)
ua.GenerateThumbnail(Database, Content);
else
ua.SaveThumbnailAttachment(Database, PdfThumbnail);
return ua;
}
public static string GenerateThumbnail(this IAttachment attachment, DiscoDataContext Database, Stream AttachmentStream)
{
string thumbnailFilePath = attachment.RepositoryThumbnailFilename(Database);
Image thumbnail;
if (GenerateThumbnail(AttachmentStream, attachment.MimeType, out thumbnail))
{
thumbnail.SaveJpg(90, thumbnailFilePath);
}
return thumbnailFilePath;
}
public static string GenerateThumbnail(this IAttachment attachment, DiscoDataContext Database)
{
string thumbnailFilePath = attachment.RepositoryThumbnailFilename(Database);
using (var attachmentStream = File.OpenRead(attachment.RepositoryFilename(Database)))
{
Image thumbnail;
if (GenerateThumbnail(attachmentStream, attachment.MimeType, out thumbnail))
{
thumbnail.SaveJpg(90, thumbnailFilePath);
}
}
return thumbnailFilePath;
}
public static bool GenerateThumbnail(Stream Source, string SourceMimeType, out Image Thumbnail)
{
if (Source != null)
{
// GDI+ (jpg, png, gif, bmp)
if (SourceMimeType.Equals("image/jpeg", StringComparison.OrdinalIgnoreCase) || SourceMimeType.Contains("jpg") ||
SourceMimeType.Equals("image/png", StringComparison.OrdinalIgnoreCase) || SourceMimeType.Contains("png") ||
SourceMimeType.Equals("image/gif", StringComparison.OrdinalIgnoreCase) || SourceMimeType.Contains("gif") ||
SourceMimeType.Equals("image/bmp", StringComparison.OrdinalIgnoreCase) || SourceMimeType.Contains("bmp"))
{
try
{
using (Image sourceImage = Image.FromStream(Source))
{
Thumbnail = sourceImage.ResizeImage(48, 48);
using (Image mimeTypeIcon = Disco.Services.Properties.Resources.MimeType_img16)
{
Thumbnail.EmbedIconOverlay(mimeTypeIcon);
}
return true;
}
}
catch (Exception ex)
{
ex.ToExceptionless().Submit();
}
}
// PDF
if (SourceMimeType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase) || SourceMimeType.Contains("pdf"))
{
try
{
using (var pdfiumDocument = PdfiumViewer.PdfDocument.Load(Source))
{
if (pdfiumDocument.PageCount > 0)
{
var pageSize = pdfiumDocument.PageSizes[0];
var size = ImagingExtensions.CalculateResize((int)pageSize.Width, (int)pageSize.Height, 48, 48);
Thumbnail = pdfiumDocument.Render(0, (int)size.Width, (int)size.Height, 72, 72, true);
return true;
}
}
}
catch (Exception ex)
{
ex.ToExceptionless().Submit();
}
}
}
Thumbnail = null;
return false;
}
}
}
@@ -0,0 +1,89 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using System;
using System.Drawing;
using System.IO;
namespace Disco.Services
{
public static class AttachmentDataStoreExtensions
{
public static string RepositoryFilename(this IAttachment Attachment, DiscoDataContext Database)
{
switch (Attachment.AttachmentType)
{
case AttachmentTypes.Device:
return Path.Combine(DataStore.CreateLocation(Database, "DeviceAttachments", Attachment.Timestamp),
string.Format("{0}_{1}_file", Attachment.Reference, Attachment.Id));
case AttachmentTypes.Job:
return Path.Combine(DataStore.CreateLocation(Database, "JobAttachments", Attachment.Timestamp),
string.Format("{0}_{1}_file", Attachment.Reference, Attachment.Id));
case AttachmentTypes.User:
return Path.Combine(DataStore.CreateLocation(Database, "UserAttachments", Attachment.Timestamp),
string.Format("{0}_{1}_file", ((string)Attachment.Reference).Replace('\\', '_'), Attachment.Id));
default:
throw new ArgumentException("Unknown Attachment Type", nameof(Attachment));
}
}
public static string RepositoryThumbnailFilename(this IAttachment Attachment, DiscoDataContext Database)
{
switch (Attachment.AttachmentType)
{
case AttachmentTypes.Device:
return Path.Combine(DataStore.CreateLocation(Database, "DeviceAttachments", Attachment.Timestamp),
string.Format("{0}_{1}_thumb.jpg", Attachment.Reference, Attachment.Id));
case AttachmentTypes.Job:
return Path.Combine(DataStore.CreateLocation(Database, "JobAttachments", Attachment.Timestamp),
string.Format("{0}_{1}_thumb.jpg", Attachment.Reference, Attachment.Id));
case AttachmentTypes.User:
return Path.Combine(DataStore.CreateLocation(Database, "UserAttachments", Attachment.Timestamp),
string.Format("{0}_{1}_thumb.jpg", ((string)Attachment.Reference).Replace('\\', '_'), Attachment.Id));
default:
throw new ArgumentException("Unknown Attachment Type", nameof(Attachment));
}
}
public static string SaveAttachment(this IAttachment Attachment, DiscoDataContext Database, Stream FileContent)
{
string filePath = Attachment.RepositoryFilename(Database);
DataStore.WriteFile(filePath, FileContent);
return filePath;
}
public static string SaveAttachment(this IAttachment Attachment, DiscoDataContext Database, byte[] FileContent)
{
string filePath = Attachment.RepositoryFilename(Database);
DataStore.WriteFile(filePath, FileContent);
return filePath;
}
public static string SaveThumbnailAttachment(this IAttachment Attachment, DiscoDataContext Database, Image Thumbnail)
{
string filePath = Attachment.RepositoryThumbnailFilename(Database);
Thumbnail.SaveJpg(90, filePath);
return filePath;
}
public static string SaveThumbnailAttachment(this IAttachment Attachment, DiscoDataContext Database, Stream FileContent)
{
string filePath = Attachment.RepositoryThumbnailFilename(Database);
DataStore.WriteFile(filePath, FileContent);
return filePath;
}
public static string SaveThumbnailAttachment(this IAttachment Attachment, DiscoDataContext Database, byte[] FileContent)
{
string filePath = Attachment.RepositoryThumbnailFilename(Database);
DataStore.WriteFile(filePath, FileContent);
return filePath;
}
public static void RepositoryDelete(this IAttachment Attachment, DiscoDataContext Database)
{
DataStore.DeleteFiles(Attachment.RepositoryFilename(Database), Attachment.RepositoryThumbnailFilename(Database));
}
}
}
+57
View File
@@ -0,0 +1,57 @@
using Disco.Data.Configuration;
using Disco.Data.Repository;
using System;
using System.IO;
namespace Disco.Services
{
public static class DataStore
{
public static string CreateLocation(DiscoDataContext Database, string SubLocation, DateTime? SubSubLocationTimestamp = null)
{
return CreateLocation(Database.DiscoConfiguration, SubLocation, SubSubLocationTimestamp);
}
public static string CreateLocation(SystemConfiguration DiscoConfiguration, string SubLocation, DateTime? SubSubLocationTimestamp = null)
{
string SubSubLocation = string.Empty;
if (SubSubLocationTimestamp.HasValue)
SubSubLocation = SubSubLocationTimestamp.Value.ToString(@"yyyy\\MM");
string storeDirectory = System.IO.Path.Combine(DiscoConfiguration.DataStoreLocation, SubLocation, SubSubLocation);
if (!System.IO.Directory.Exists(storeDirectory))
System.IO.Directory.CreateDirectory(storeDirectory);
return storeDirectory;
}
public static void DeleteFile(string FilePath)
{
if (File.Exists(FilePath))
File.Delete(FilePath);
}
public static void DeleteFiles(params string[] FilePaths)
{
foreach (string filePath in FilePaths)
{
DeleteFile(filePath);
}
}
public static void WriteFile(string FilePath, Stream FileContent)
{
using (FileStream outStream = new FileStream(FilePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
FileContent.CopyTo(outStream);
}
}
public static void WriteFile(string FilePath, byte[] FileContent)
{
File.WriteAllBytes(FilePath, FileContent);
}
}
}
@@ -0,0 +1,71 @@
using Disco.Models.Repository;
using System;
using System.Drawing;
using System.IO;
namespace Disco.Services
{
public static class DeviceDataStoreExtensions
{
public static bool ImageImport(this DeviceModel deviceModel, Stream ImageStream)
{
try
{
using (Bitmap inputBitmap = new Bitmap(ImageStream))
{
using (Image outputBitmap = inputBitmap.ResizeImage(256, 256))
{
using (MemoryStream ms = new MemoryStream())
{
outputBitmap.SavePng(ms);
ms.Position = 0;
var deviceModelImagePath = deviceModel.ImageFilePath();
using (var storeStream = new FileStream(deviceModelImagePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
ms.CopyTo(storeStream);
}
//deviceModel.Image = ms.ToArray();
}
}
}
return true;
}
catch (Exception)
{
return false;
}
}
public static FileStream Image(this DeviceModel deviceModel)
{
var deviceModelImagePath = deviceModel.ImageFilePath();
if (File.Exists(deviceModelImagePath))
return new FileStream(deviceModelImagePath, FileMode.Open, FileAccess.Read, FileShare.Read);
else
return null;
}
public static string ImageFilePath(this DeviceModel deviceModel)
{
var configCache = new Disco.Data.Configuration.SystemConfiguration(null);
var deviceModelImagesDataStore = DataStore.CreateLocation(configCache, "DeviceModelImages");
return Path.Combine(deviceModelImagesDataStore, string.Format("{0}.png", deviceModel.Id));
}
public static string ImageHash(this DeviceModel deviceModel)
{
var deviceModelImagePath = deviceModel.ImageFilePath();
if (File.Exists(deviceModelImagePath))
return File.GetLastWriteTimeUtc(deviceModelImagePath).ToBinary().ToString();
else
return "-1";
}
}
}
+82 -1
View File
@@ -70,6 +70,18 @@
<Reference Include="Owin">
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
</Reference>
<Reference Include="PdfiumViewer, Version=2.10.0.0, Culture=neutral, PublicKeyToken=91e4789cfb0609e0, processorArchitecture=MSIL">
<HintPath>..\packages\PdfiumViewer.2.10.0.0\lib\net20\PdfiumViewer.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PdfSharp, Version=1.32.3057.0, Culture=neutral, PublicKeyToken=f94615aa0424f9eb, processorArchitecture=MSIL">
<HintPath>..\packages\PDFsharp.1.32.3057.0\lib\net20\PdfSharp.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PdfSharp.Charting, Version=1.32.3057.0, Culture=neutral, PublicKeyToken=f94615aa0424f9eb, processorArchitecture=MSIL">
<HintPath>..\packages\PDFsharp.1.32.3057.0\lib\net20\PdfSharp.Charting.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Quartz">
<HintPath>..\Resources\Libraries\Quartz\Quartz.dll</HintPath>
</Reference>
@@ -77,6 +89,9 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\RazorGenerator.Mvc.2.2.3\lib\net40\RazorGenerator.Mvc.dll</HintPath>
</Reference>
<Reference Include="Spring.Core">
<HintPath>..\..\..\Resources\Libraries\Spring.NET\Spring.Core.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" />
@@ -86,6 +101,7 @@
<HintPath>..\packages\Microsoft.SqlServer.Compact.4.0.8876.1\lib\net40\System.Data.SqlServerCe.dll</HintPath>
</Reference>
<Reference Include="System.DirectoryServices" />
<Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.Extensions">
@@ -146,9 +162,19 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\WebActivatorEx.2.0.5\lib\net40\WebActivatorEx.dll</HintPath>
</Reference>
<Reference Include="zxing, Version=0.14.0.0, Culture=neutral, PublicKeyToken=4e88037ac681fe60, processorArchitecture=MSIL">
<HintPath>..\packages\ZXing.Net.0.14.0.1\lib\net40\zxing.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="zxing.presentation, Version=0.14.0.0, Culture=neutral, PublicKeyToken=4e88037ac681fe60, processorArchitecture=MSIL">
<HintPath>..\packages\ZXing.Net.0.14.0.1\lib\net40\zxing.presentation.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="App_Start\RazorGeneratorMvcStart.cs" />
<Compile Include="Attachments\AttachmentActionExtensions.cs" />
<Compile Include="Attachments\AttachmentDataStoreExtensions.cs" />
<Compile Include="Authorization\AccessDeniedException.cs" />
<Compile Include="Authorization\AuthorizationLog.cs" />
<Compile Include="Authorization\AuthorizationToken.cs" />
@@ -194,6 +220,8 @@
<Compile Include="Authorization\Roles\RoleCache.cs" />
<Compile Include="Authorization\Roles\RoleClaims.cs" />
<Compile Include="Authorization\Roles\RoleToken.cs" />
<Compile Include="DataStore.cs" />
<Compile Include="Devices\DeviceDataStoreExtensions.cs" />
<Compile Include="Devices\Exporting\DeviceExport.cs" />
<Compile Include="Devices\Exporting\DeviceExportTask.cs" />
<Compile Include="Devices\Exporting\DeviceExportTaskContext.cs" />
@@ -227,8 +255,37 @@
<Compile Include="Devices\ManagedGroups\DeviceManagedGroups.cs" />
<Compile Include="Devices\ManagedGroups\DeviceProfileAssignedUsersManagedGroup.cs" />
<Compile Include="Devices\ManagedGroups\DeviceProfileDevicesManagedGroup.cs" />
<Compile Include="Documents\AttachmentImport\Importer.cs" />
<Compile Include="Documents\AttachmentImport\ImporterCleanCacheJob.cs" />
<Compile Include="Documents\AttachmentImport\ImporterJob.cs" />
<Compile Include="Documents\AttachmentImport\ImportDirectoryMonitor.cs" />
<Compile Include="Documents\AttachmentImport\ImportPage.cs" />
<Compile Include="Documents\DocumentsLog.cs" />
<Compile Include="Documents\DocumentTemplateActionExtensions.cs" />
<Compile Include="Documents\DocumentTemplateDataStoreExtensions.cs" />
<Compile Include="Documents\DocumentTemplateExpressionExtensions.cs" />
<Compile Include="Documents\DocumentUniqueIdentifier.cs" />
<Compile Include="Documents\DocumentUniqueIdentifierExtensions.cs" />
<Compile Include="Expressions\EvaluateExpressionParseException.cs" />
<Compile Include="Expressions\EvaluateExpressionPart.cs" />
<Compile Include="Expressions\Expression.cs" />
<Compile Include="Expressions\ExpressionCache.cs" />
<Compile Include="Expressions\ExpressionCachePreloadTask.cs" />
<Compile Include="Expressions\ExpressionTypeDescriptor.cs" />
<Compile Include="Expressions\ExpressionTypeMemberDescriptor.cs" />
<Compile Include="Expressions\Extensions\DataExt.cs" />
<Compile Include="Expressions\Extensions\DeviceExt.cs" />
<Compile Include="Expressions\Extensions\ImageExt.cs" />
<Compile Include="Expressions\Extensions\ImageResultImplementations\BaseImageExpressionResult.cs" />
<Compile Include="Expressions\Extensions\ImageResultImplementations\BitmapImageExpressionResult.cs" />
<Compile Include="Expressions\Extensions\ImageResultImplementations\FileImageExpressionResult.cs" />
<Compile Include="Expressions\Extensions\ImageResultImplementations\FileMontageImageExpressionResult.cs" />
<Compile Include="Expressions\Extensions\UserExt.cs" />
<Compile Include="Expressions\IExpressionPart.cs" />
<Compile Include="Expressions\TextExpressionPart.cs" />
<Compile Include="Extensions\DateTimeExtensions.cs" />
<Compile Include="Extensions\EnumerableExtensions.cs" />
<Compile Include="Extensions\ImagingExtensions.cs" />
<Compile Include="Extensions\RxExtensions.cs" />
<Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="Extensions\UIHelpers.cs" />
@@ -257,6 +314,7 @@
<Compile Include="Interop\DiscoServices\Jobs.cs" />
<Compile Include="Interop\DiscoServices\PluginLibrary.cs" />
<Compile Include="Interop\DiscoServices\PluginLibraryUpdateTask.cs" />
<Compile Include="Interop\MimeTypes.cs" />
<Compile Include="Interop\VicEduDept\VicSmart.cs" />
<Compile Include="Interop\DiscoServices\UpdateQuery.cs" />
<Compile Include="Interop\DiscoServices\UpdateQueryTask.cs" />
@@ -322,6 +380,11 @@
<Compile Include="Plugins\PluginWebViewPage.cs" />
<Compile Include="Plugins\WebPageHelper.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Searching\Search.cs" />
<Compile Include="Tasks\IScheduledTaskStatus.cs" />
<Compile Include="Tasks\ScheduledTask.cs" />
@@ -383,7 +446,25 @@
<ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<None Include="Resources\MimeType-doc48.png" />
<None Include="Resources\MimeType-img16.png" />
<None Include="Resources\MimeType-pdf16.png" />
<None Include="Resources\MimeType-pdf48.png" />
<None Include="Resources\MimeType-unknown48.png" />
<Content Include="x64\pdfium.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="x86\pdfium.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>
@@ -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);
}
}
}
+441
View File
@@ -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
}
};
}
}
}
@@ -0,0 +1,23 @@
using Spring.Expressions.Parser.antlr;
namespace Disco.Services.Expressions
{
public class EvaluateExpressionParseException
{
public string Expression { get; set; }
public int PositionRow { get; set; }
public int PositionColumn { get; set; }
public string Message { get; set; }
internal static EvaluateExpressionParseException FromRecognitionException(RecognitionException e, string Expression)
{
return new EvaluateExpressionParseException()
{
Expression = Expression,
Message = e.Message,
PositionRow = e.getLine(),
PositionColumn = e.getColumn()
};
}
}
}
@@ -0,0 +1,82 @@
using Spring.Expressions.Parser.antlr;
using System.Collections;
namespace Disco.Services.Expressions
{
public class EvaluateExpressionPart : IExpressionPart
{
private Spring.Expressions.IExpression _Expression;
private RecognitionException _ExpressionParseException;
private EvaluateExpressionParseException _EvaluateParseException;
public string RawSource { get; set; }
public string Source { get; set; }
public bool ErrorsAllowed { get; set; }
public bool IsDynamic { get { return true; } set { return; } }
public EvaluateExpressionParseException ParseException
{
get
{
if (_ExpressionParseException == null)
return null;
else
if (_EvaluateParseException == null)
_EvaluateParseException = EvaluateExpressionParseException.FromRecognitionException(_ExpressionParseException, this.Source);
return _EvaluateParseException;
}
}
public bool ParseError
{
get { return (_ExpressionParseException != null); }
}
public string ParseErrorMessage
{
get
{
if (ParseError)
return ParseException.Message;
else
return null;
}
}
public EvaluateExpressionPart(string Source)
{
this.RawSource = Source;
if (Source.StartsWith("{") && Source.EndsWith("}"))
Source = Source.Substring(1, Source.Length - 2);
if (Source[0] == '~')
{
this.ErrorsAllowed = true;
this.Source = Source.Substring(1);
}
else
{
this.ErrorsAllowed = false;
this.Source = Source;
}
try
{
this._Expression = Spring.Expressions.Expression.Parse(this.Source);
}
catch (RecognitionException ex)
{
this._ExpressionParseException = ex;
}
}
object IExpressionPart.Evaluate(object ExpressionContext, IDictionary Variables)
{
if (this._ExpressionParseException == null)
{
return this._Expression.GetValue(ExpressionContext, Variables);
}
throw this._ExpressionParseException;
}
}
}
+265
View File
@@ -0,0 +1,265 @@
using Disco.Data.Repository;
using Disco.Models.BI.Expressions;
using Disco.Models.Repository;
using Disco.Models.Services.Documents;
using Spring.Core.TypeResolution;
using Spring.Expressions;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace Disco.Services.Expressions
{
public sealed class Expression : List<IExpressionPart>
{
public string Name { get; private set; }
public string Source { get; private set; }
public bool IsDynamic { get; private set; }
public int Ordinal { get; private set; }
private Expression(string Name, string Source, int Ordinal)
{
this.Name = Name;
this.Source = Source;
this.Ordinal = Ordinal;
}
public static void InitializeExpressions()
{
TypeRegistry.RegisterType("DataExt", typeof(Extensions.DataExt));
TypeRegistry.RegisterType("UserExt", typeof(Extensions.UserExt));
TypeRegistry.RegisterType("DeviceExt", typeof(Extensions.DeviceExt));
TypeRegistry.RegisterType("ImageExt", typeof(Extensions.ImageExt));
}
public T EvaluateFirst<T>(object ExpressionContext, IDictionary Variables)
{
T result = default(T);
if (this.Count > 0)
{
try
{
object expressionResult = this[0].Evaluate(ExpressionContext, Variables);
if (expressionResult != null)
{
if (expressionResult is T)
{
result = (T)expressionResult;
}
else
{
throw new InvalidOperationException("Expression returned an invalid type");
}
}
}
catch (Exception ex)
{
throw new InvalidOperationException("Expression evaluation resulted in an error", ex);
}
}
return result;
}
internal static IExpression Parse(string source)
{
throw new NotImplementedException();
}
public Tuple<string, bool, object> Evaluate(object ExpressionContext, IDictionary Variables)
{
var resultValue = new StringBuilder();
object resultObject = null;
bool resultError = false;
foreach (var expressionPart in this)
{
try
{
object partValue = expressionPart.Evaluate(ExpressionContext, Variables);
if (partValue != null)
{
// Check for Result Objects
if (partValue is IImageExpressionResult)
resultObject = partValue;
else
resultValue.Append(partValue.ToString());
}
}
catch (Exception ex)
{
if (!expressionPart.ErrorsAllowed)
{
resultValue.Append("## ERROR # ");
resultValue.Append(ex.Message);
resultValue.Append(" ##");
resultError = true;
}
}
}
return new Tuple<string, bool, object>(resultValue.ToString(), resultError, resultObject);
}
public static Expression TokenizeSingleDynamic(string Name, string ExpressionSource, int Ordinal)
{
var e = new Expression(Name, ExpressionSource, Ordinal);
if (ExpressionSource != null && !string.IsNullOrWhiteSpace(ExpressionSource))
e.Add(new EvaluateExpressionPart(ExpressionSource));
e.IsDynamic = true;
return e;
}
public static Expression Tokenize(string Name, string ExpressionSource, int Ordinal)
{
var e = new Expression(Name, ExpressionSource, Ordinal);
if (!ExpressionSource.Contains("{") || !ExpressionSource.Contains("}"))
{
e.Add(new TextExpressionPart(ExpressionSource));
}
else
{
var token = new StringBuilder();
bool tokenEval = false;
int tokenEvalDepth = 0;
foreach (char c in ExpressionSource)
{
switch (c)
{
case '{':
{
if (!tokenEval)
{
if (token.Length > 0)
{
e.Add(new TextExpressionPart(token.ToString()));
token = new StringBuilder();
}
tokenEval = true;
tokenEvalDepth = 0;
}
tokenEvalDepth++;
token.Append(c);
break;
}
case '}':
{
token.Append(c);
if (tokenEval)
{
tokenEvalDepth--;
if (tokenEvalDepth <= 0)
{
if (token.Length != 2 && (token.Length != 3 || token[1] != '@'))
{
e.Add(new EvaluateExpressionPart(token.ToString()));
e.IsDynamic = true;
token = new StringBuilder();
}
tokenEval = false;
}
}
break;
}
default:
{
token.Append(c);
break;
}
}
}
if (token.Length > 0)
{
e.Add(new TextExpressionPart(token.ToString()));
}
}
return e;
}
public static IDictionary StandardVariables(DocumentTemplate AttachmentType, DiscoDataContext Database, User User, System.DateTime TimeStamp, DocumentState DocumentState)
{
return new Hashtable
{
{
"DataContext",
Database
},
{
"User",
User
},
{
"TimeStamp",
TimeStamp
},
{
"AttachmentType",
AttachmentType
},
{
"State",
DocumentState
}
};
}
public static Dictionary<string, string> StandardVariableTypes()
{
return new Dictionary<string, string>
{
{
"#DataContext",
typeof(DiscoDataContext).AssemblyQualifiedName
},
{
"#User",
typeof(User).AssemblyQualifiedName
},
{
"#TimeStamp",
typeof(System.DateTime).AssemblyQualifiedName
},
{
"#AttachmentType",
typeof(DocumentTemplate).AssemblyQualifiedName
},
{
"#State",
typeof(DocumentState).AssemblyQualifiedName
}
};
}
public static Dictionary<string, string> ExtensionLibraryTypes()
{
return new Dictionary<string, string>
{
{
"DataExt",
typeof(Extensions.DataExt).AssemblyQualifiedName
},
{
"DeviceExt",
typeof(Extensions.DeviceExt).AssemblyQualifiedName
},
{
"ImageExt",
typeof(Extensions.ImageExt).AssemblyQualifiedName
},
{
"UserExt",
typeof(Extensions.UserExt).AssemblyQualifiedName
}
};
}
}
}
@@ -0,0 +1,99 @@
using System.Collections.Concurrent;
namespace Disco.Services.Expressions
{
public static class ExpressionCache
{
private static ConcurrentDictionary<string, ConcurrentDictionary<string, Expression>> _Cache = new ConcurrentDictionary<string, ConcurrentDictionary<string, Expression>>();
public delegate Expression CreateValueDelegate();
public static ConcurrentDictionary<string, Expression> GetModule(string Module, bool Create = false)
{
ConcurrentDictionary<string, Expression> moduleCache;
if (_Cache.TryGetValue(Module, out moduleCache))
return moduleCache;
else
{
if (Create)
{
moduleCache = new ConcurrentDictionary<string, Expression>();
_Cache.TryAdd(Module, moduleCache);
return moduleCache;
}
else
return null;
}
}
private static Expression GetModuleValue(string Module, string Key, CreateValueDelegate CreateValue)
{
ConcurrentDictionary<string, Expression> moduleCache = GetModule(Module, (CreateValue != null));
if (moduleCache != null)
{
Expression expression;
if (moduleCache.TryGetValue(Key, out expression))
{
return expression;
}
if (CreateValue != null)
{
expression = CreateValue();
Expression oldExpression;
if (moduleCache.TryGetValue(Key, out oldExpression))
moduleCache.TryUpdate(Key, expression, oldExpression);
else
moduleCache.TryAdd(Key, expression);
return expression;
}
}
return null;
}
public static Expression GetValue(string Module, string Key, CreateValueDelegate CreateValue)
{
return GetModuleValue(Module, Key, CreateValue);
}
public static Expression GetValue(string Module, string Key)
{
return GetModuleValue(Module, Key, null);
}
public static bool InvalidModule(string Module)
{
ConcurrentDictionary<string, Expression> moduleCache;
return _Cache.TryRemove(Module, out moduleCache);
}
public static bool InvalidateKey(string Module, string Key)
{
Expression expression;
ConcurrentDictionary<string, Expression> moduleCache = GetModule(Module, false);
if (moduleCache != null)
{
bool removeResult = moduleCache.TryRemove(Key, out expression);
if (moduleCache.Count == 0)
InvalidModule(Module);
return removeResult;
}
else
return false;
}
public static bool SetValue(string Module, string Key, Expression Expression)
{
ConcurrentDictionary<string, Expression> moduleCache = GetModule(Module, true);
if (moduleCache.ContainsKey(Key))
{
Expression oldExpression;
if (moduleCache.TryGetValue(Key, out oldExpression))
{
return moduleCache.TryUpdate(Key, Expression, oldExpression);
}
}
return moduleCache.TryAdd(Key, Expression);
}
}
}
@@ -0,0 +1,38 @@
using Disco.Data.Repository;
using Disco.Services.Tasks;
using Quartz;
using System;
using System.Linq;
namespace Disco.Services.Expressions
{
public class ExpressionCachePreloadTask : ScheduledTask
{
public override string TaskName { get { return "Expression Cache - Preload 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)
{
// Run in Background 5 Second after Scheduled (on App Startup)
TriggerBuilder triggerBuilder = TriggerBuilder.Create().StartAt(new DateTimeOffset(DateTime.Now).AddSeconds(5));
this.ScheduleTask(triggerBuilder);
}
protected override void ExecuteTask()
{
// Cache Document Template Filter Expressions
using (DiscoDataContext database = new DiscoDataContext())
{
foreach (var documentTemplate in database.DocumentTemplates.Where(dt => dt.FilterExpression != null && dt.FilterExpression != string.Empty))
{
if (!string.IsNullOrWhiteSpace(documentTemplate.FilterExpression))
documentTemplate.FilterExpressionFromCache();
}
}
}
}
}
@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Disco.Services.Expressions
{
public class ExpressionTypeDescriptor
{
public string ExpressionType { get; set; }
public string Name { get; set; }
public List<ExpressionTypeMemberDescriptor> Members { get; set; }
public static ExpressionTypeDescriptor Build(Type t, bool StaticDeclaredMembersOnly = true)
{
ExpressionTypeDescriptor i = new ExpressionTypeDescriptor
{
ExpressionType = t.AssemblyQualifiedName,
Name = t.Name
};
i.Members = new List<ExpressionTypeMemberDescriptor>();
MemberInfo[] members;
if (StaticDeclaredMembersOnly)
members = t.GetMembers(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
else
members = t.GetMembers(BindingFlags.Public | BindingFlags.Instance);
for (int j = 0; j < members.Length; j++)
{
MemberInfo member = members[j];
if (member is PropertyInfo)
{
PropertyInfo pi = (PropertyInfo)member;
if (!pi.IsSpecialName && pi.CanRead)
{
i.Members.Add(ExpressionTypeMemberDescriptor.Build(pi));
}
}
if (member is MethodInfo)
{
MethodInfo mi2 = (MethodInfo)member;
if (!mi2.IsSpecialName)
{
i.Members.Add(ExpressionTypeMemberDescriptor.Build(mi2));
}
}
}
i.Members = (
from mi in i.Members
orderby mi.Name
select mi).ToList();
return i;
}
}
}
@@ -0,0 +1,58 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Disco.Services.Expressions
{
public class ExpressionTypeMemberDescriptor
{
public const string FunctionKind = "function";
public const string PropertyKind = "property";
public const string ParameterKind = "parameter";
public string Kind { get; set; }
public string Name { get; set; }
public string ReturnType { get; set; }
public string ReturnExpressionType { get; set; }
public List<ExpressionTypeMemberDescriptor> Parameters { get; set; }
public static ExpressionTypeMemberDescriptor Build(MethodInfo m)
{
ExpressionTypeMemberDescriptor md = new ExpressionTypeMemberDescriptor
{
Kind = "function",
Name = m.Name,
ReturnType = m.ReturnType.Name,
ReturnExpressionType = m.ReturnType.AssemblyQualifiedName
};
md.Parameters = (
from mdp in m.GetParameters()
select Build(mdp)).ToList();
return md;
}
public static ExpressionTypeMemberDescriptor Build(PropertyInfo p)
{
ExpressionTypeMemberDescriptor md = new ExpressionTypeMemberDescriptor
{
Kind = "property",
Name = p.Name,
ReturnType = p.PropertyType.Name,
ReturnExpressionType = p.PropertyType.AssemblyQualifiedName
};
md.Parameters = (
from mdp in p.GetIndexParameters()
select Build(mdp)).ToList();
return md;
}
public static ExpressionTypeMemberDescriptor Build(ParameterInfo pi)
{
return new ExpressionTypeMemberDescriptor
{
Kind = "parameter",
Name = pi.Name,
ReturnType = pi.ParameterType.Name,
ReturnExpressionType = pi.ParameterType.AssemblyQualifiedName
};
}
}
}
@@ -0,0 +1,173 @@
using System;
using System.Collections;
using System.Data;
using System.Data.Odbc;
using System.Data.SqlClient;
namespace Disco.Services.Expressions.Extensions
{
public static class DataExt
{
#region SqlClient
private static SqlConnection BuildSqlConnection(string Server, string Database, string Username, string Password)
{
var dbConnectionStringBuilder = new SqlConnectionStringBuilder();
dbConnectionStringBuilder.ApplicationName = "Disco";
dbConnectionStringBuilder.DataSource = Server;
dbConnectionStringBuilder.InitialCatalog = Database;
dbConnectionStringBuilder.MultipleActiveResultSets = true;
dbConnectionStringBuilder.PersistSecurityInfo = true;
if (Username == null || Password == null)
dbConnectionStringBuilder.IntegratedSecurity = true;
else
{
dbConnectionStringBuilder.UserID = Username;
dbConnectionStringBuilder.Password = Password;
}
return new SqlConnection(dbConnectionStringBuilder.ConnectionString);
}
private static void BuildSqlParameters(SqlCommand dbCommand, Hashtable SqlParameters)
{
if (SqlParameters != null)
{
foreach (var sqlParameterKey in SqlParameters.Keys)
{
string key = sqlParameterKey.ToString();
if (!key.StartsWith("@"))
key = string.Concat("@", key);
dbCommand.Parameters.AddWithValue(key, SqlParameters[sqlParameterKey]);
}
}
}
public static DataTable QuerySqlDatabase(string Server, string Database, string Username, string Password, string SqlQuery, Hashtable SqlParameters)
{
using (SqlConnection dbConnection = BuildSqlConnection(Server, Database, Username, Password))
{
using (SqlCommand dbCommand = new SqlCommand(SqlQuery, dbConnection))
{
BuildSqlParameters(dbCommand, SqlParameters);
using (SqlDataAdapter dbAdapter = new SqlDataAdapter(dbCommand))
{
var dbTable = new DataTable();
dbAdapter.Fill(dbTable);
return dbTable;
}
}
}
}
public static DataTable QuerySqlDatabase(string Server, string Database, string SqlQuery, Hashtable SqlParameters)
{
return QuerySqlDatabase(Server, Database, null, null, SqlQuery, SqlParameters);
}
public static DataTable QuerySqlDatabase(string Server, string Database, string SqlQuery)
{
return QuerySqlDatabase(Server, Database, null, null, SqlQuery, null);
}
public static object QuerySqlDatabaseScalar(string Server, string Database, string Username, string Password, string SqlQuery, Hashtable SqlParameters)
{
using (SqlConnection dbConnection = BuildSqlConnection(Server, Database, Username, Password))
{
using (SqlCommand dbCommand = new SqlCommand(SqlQuery, dbConnection))
{
BuildSqlParameters(dbCommand, SqlParameters);
try
{
dbConnection.Open();
return dbCommand.ExecuteScalar();
}
catch (Exception)
{
throw;
}
finally
{
dbConnection.Close();
}
}
}
}
public static object QuerySqlDatabaseScalar(string Server, string Database, string SqlQuery, Hashtable SqlParameters)
{
return QuerySqlDatabaseScalar(Server, Database, null, null, SqlQuery, SqlParameters);
}
public static object QuerySqlDatabaseScalar(string Server, string Database, string SqlQuery)
{
return QuerySqlDatabaseScalar(Server, Database, null, null, SqlQuery, null);
}
#endregion
#region ODBC
private static OdbcConnection BuildOdbcConnection(string ConnectionString)
{
return new OdbcConnection(ConnectionString);
}
private static void BuildOdbcParameters(OdbcCommand dbCommand, Hashtable OdbcParameters)
{
if (OdbcParameters != null)
{
foreach (var odbcParameterKey in OdbcParameters.Keys)
{
string key = odbcParameterKey.ToString();
dbCommand.Parameters.AddWithValue(key, OdbcParameters[odbcParameterKey]);
}
}
}
public static DataTable QueryOdbcDatabase(string ConnectionString, string OdbcQuery, Hashtable OdbcParameters)
{
using (OdbcConnection dbConnection = BuildOdbcConnection(ConnectionString))
{
using (OdbcCommand dbCommand = new OdbcCommand(OdbcQuery, dbConnection))
{
BuildOdbcParameters(dbCommand, OdbcParameters);
using (OdbcDataAdapter dbAdapter = new OdbcDataAdapter(dbCommand))
{
var dbTable = new DataTable();
dbAdapter.Fill(dbTable);
return dbTable;
}
}
}
}
public static DataTable QueryOdbcDatabase(string ConnectionString, string OdbcQuery)
{
return QueryOdbcDatabase(ConnectionString, OdbcQuery, null);
}
public static object QueryOdbcDatabaseScalar(string ConnectionString, string OdbcQuery, Hashtable OdbcParameters)
{
using (OdbcConnection dbConnection = BuildOdbcConnection(ConnectionString))
{
using (OdbcCommand dbCommand = new OdbcCommand(OdbcQuery, dbConnection))
{
BuildOdbcParameters(dbCommand, OdbcParameters);
try
{
dbConnection.Open();
return dbCommand.ExecuteScalar();
}
catch (Exception)
{
throw;
}
finally
{
dbConnection.Close();
}
}
}
}
public static object QueryOdbcDatabaseScalar(string ConnectionString, string OdbcQuery)
{
return QueryOdbcDatabaseScalar(ConnectionString, OdbcQuery, null);
}
#endregion
}
}
@@ -0,0 +1,47 @@
using Disco.Models.Repository;
using System;
using System.Linq;
namespace Disco.Services.Expressions.Extensions
{
public static class DeviceExt
{
public static object GetActiveDirectoryObjectValue(Device Device, string PropertyName, int Index = 0)
{
var adMachineAccount = Device.ActiveDirectoryAccount(PropertyName);
if (adMachineAccount != null)
return adMachineAccount.GetPropertyValues<object>(PropertyName).Skip(Index).FirstOrDefault();
else
return null;
}
public static string GetActiveDirectoryStringValue(Device Device, string PropertyName, int Index = 0)
{
var objectValue = GetActiveDirectoryObjectValue(Device, PropertyName, Index);
string stringValue = objectValue as string;
if (stringValue == null && objectValue != null)
stringValue = objectValue.ToString();
return stringValue;
}
public static int GetActiveDirectoryIntegerValue(Device Device, string PropertyName, int Index = 0)
{
var objectValue = GetActiveDirectoryObjectValue(Device, PropertyName, Index);
if (objectValue == null)
return default(int);
else
{
int intValue;
try
{
intValue = (int)Convert.ChangeType(objectValue, typeof(int));
}
catch (Exception)
{
throw;
}
return intValue;
}
}
}
}
@@ -0,0 +1,132 @@
using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Services.Expressions.Extensions.ImageResultImplementations;
using System;
using System.Collections;
using System.Drawing;
using System.IO;
using System.Linq;
namespace Disco.Services.Expressions.Extensions
{
public static class ImageExt
{
public static FileImageExpressionResult ImageFromFile(string AbsoluteFilePath)
{
return new FileImageExpressionResult(AbsoluteFilePath);
}
public static FileImageExpressionResult ImageFromDataStoreFile(string RelativeFilePath)
{
var configCache = new Disco.Data.Configuration.SystemConfiguration(null);
string DataStoreLocation = configCache.DataStoreLocation;
string AbsoluteFilePath = System.IO.Path.Combine(DataStoreLocation, RelativeFilePath);
return new FileImageExpressionResult(AbsoluteFilePath);
}
public static FileImageExpressionResult JobAttachmentFirstImage(Job Job, DiscoDataContext Database)
{
var attachment = Job.JobAttachments.FirstOrDefault(ja => ja.MimeType.StartsWith("image/", StringComparison.OrdinalIgnoreCase));
if (attachment != null)
{
var filename = attachment.RepositoryFilename(Database);
return new FileImageExpressionResult(filename);
}
else
return null;
}
public static FileImageExpressionResult JobAttachmentLastImage(Job Job, DiscoDataContext Database)
{
var attachment = Job.JobAttachments.LastOrDefault(ja => ja.MimeType.StartsWith("image/", StringComparison.OrdinalIgnoreCase));
if (attachment != null)
{
var filename = attachment.RepositoryFilename(Database);
return new FileImageExpressionResult(filename);
}
else
return null;
}
public static FileImageExpressionResult JobAttachmentImage(JobAttachment JobAttachment, DiscoDataContext Database)
{
if (JobAttachment == null)
throw new ArgumentNullException("JobAttachment");
if (!JobAttachment.MimeType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
throw new ArgumentException("Invalid Image MimeType for Attachment");
var filename = JobAttachment.RepositoryFilename(Database);
return new FileImageExpressionResult(filename);
}
public static FileMontageImageExpressionResult JobAttachmentImageMontage(Job Job, DiscoDataContext Database)
{
if (Job == null)
throw new ArgumentNullException("Job");
if (Job.JobAttachments == null)
throw new ArgumentException("Job.JobAttachments is null", "Job");
var attachments = Job.JobAttachments.Where(a => a.MimeType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)).ToList();
if (attachments.Count > 0)
{
var attachmentFilepaths = attachments.Select(a => a.RepositoryFilename(Database)).ToList();
return new FileMontageImageExpressionResult(attachmentFilepaths);
}
else
return null;
}
public static FileMontageImageExpressionResult JobAttachmentsImageMontage(ArrayList JobAttachments, DiscoDataContext Database)
{
if (JobAttachments == null)
throw new ArgumentNullException("JobAttachments");
var attachments = JobAttachments.Cast<JobAttachment>().Where(a => a.MimeType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)).ToList();
if (attachments.Count > 0)
{
var attachmentFilepaths = attachments.Select(a => a.RepositoryFilename(Database)).ToList();
return new FileMontageImageExpressionResult(attachmentFilepaths);
}
else
return null;
}
public static BitmapImageExpressionResult ImageFromStream(Stream ImageStream)
{
if (ImageStream == null)
throw new ArgumentNullException("ImageStream");
return new BitmapImageExpressionResult(Bitmap.FromStream(ImageStream));
}
public static BitmapImageExpressionResult ImageFromByteArray(byte[] ImageByteArray)
{
if (ImageByteArray == null)
throw new ArgumentNullException("ImageByteArray");
return new BitmapImageExpressionResult(Bitmap.FromStream(new MemoryStream(ImageByteArray)));
}
public static BitmapImageExpressionResult DeviceModelImage(DeviceModel DeviceModel)
{
if (DeviceModel == null)
throw new ArgumentNullException("DeviceModel");
using (Stream deviceModelImage = DeviceModel.Image())
{
if (deviceModelImage == null)
return null;
else
return ImageFromStream(deviceModelImage);
}
}
public static BitmapImageExpressionResult OrganisationLogo()
{
var configCache = new Disco.Data.Configuration.SystemConfiguration(null);
BitmapImageExpressionResult result;
using (var orgLogo = configCache.OrganisationLogo)
{
result = ImageFromStream(orgLogo);
}
result.LosslessFormat = true;
return result;
}
}
}
@@ -0,0 +1,66 @@
using Disco.Models.BI.Expressions;
using System;
using System.Drawing;
using System.IO;
namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
{
public abstract class BaseImageExpressionResult : IImageExpressionResult
{
public byte Quality { get; set; }
public bool LosslessFormat { get; set; }
public bool ShowField { get; set; }
public string BackgroundColour { get; set; }
public bool BackgroundPreferTransparent { get; set; }
public BaseImageExpressionResult()
{
this.LosslessFormat = false;
this.Quality = 90;
this.ShowField = false;
this.BackgroundPreferTransparent = true;
}
public abstract Stream GetImage(int Width, int Height);
protected Stream RenderImage(Image SourceImage, int Width, int Height)
{
if (SourceImage == null)
throw new ArgumentNullException("SourceImage");
if (Width <= 0)
throw new ArgumentOutOfRangeException("Width", "Width must be > 0");
if (Height <= 0)
throw new ArgumentOutOfRangeException("Height", "Height must be > 0");
Brush backgroundBrush = null;
if (!LosslessFormat || !BackgroundPreferTransparent)
{
if (string.IsNullOrEmpty(this.BackgroundColour))
backgroundBrush = Brushes.White;
else
backgroundBrush = new SolidBrush(ColorTranslator.FromHtml(this.BackgroundColour));
}
using (Image resizedImage = SourceImage.ResizeImage(Width, Height, backgroundBrush))
{
return OutputImage(resizedImage);
}
}
protected Stream OutputImage(Image SourceImage)
{
MemoryStream imageStream = new MemoryStream();
if (LosslessFormat)
{ // Lossless Format - PNG
SourceImage.SavePng(imageStream);
}
else
{ // Lossy Format - JPG
byte quality = Math.Min((byte)100, Math.Max((byte)1, this.Quality));
SourceImage.SaveJpg(quality, imageStream);
}
imageStream.Position = 0;
return imageStream;
}
}
}
@@ -0,0 +1,24 @@
using System;
using System.Drawing;
using System.IO;
namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
{
public class BitmapImageExpressionResult : BaseImageExpressionResult
{
public Image Image { get; set; }
public BitmapImageExpressionResult(Image Image)
{
if (Image == null)
throw new ArgumentNullException("Image");
this.Image = Image;
}
public override Stream GetImage(int Width, int Height)
{
return this.RenderImage(this.Image, Width, Height);
}
}
}
@@ -0,0 +1,29 @@
using System;
using System.Drawing;
using System.IO;
namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
{
public class FileImageExpressionResult : BaseImageExpressionResult
{
public string AbsoluteFilePath { get; set; }
public FileImageExpressionResult(string AbsoluteFilePath)
{
if (string.IsNullOrWhiteSpace(AbsoluteFilePath))
throw new ArgumentNullException("AbsoluteFilePath");
if (!File.Exists(AbsoluteFilePath))
throw new FileNotFoundException("Image not found", AbsoluteFilePath);
this.AbsoluteFilePath = AbsoluteFilePath;
}
public override Stream GetImage(int Width, int Height)
{
using (Image SourceImage = Bitmap.FromFile(this.AbsoluteFilePath))
{
return this.RenderImage(SourceImage, Width, Height);
}
}
}
}
@@ -0,0 +1,179 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Linq;
namespace Disco.Services.Expressions.Extensions.ImageResultImplementations
{
public class FileMontageImageExpressionResult : BaseImageExpressionResult
{
public List<string> AbsoluteFilePaths { get; set; }
public bool MontageHorizontalLayout { get; set; }
public bool MontageVerticalLayout { get; set; }
public bool MontageTableLayout { get; set; }
public int Padding { get; set; }
public FileMontageImageExpressionResult(List<string> AbsoluteFilePaths)
{
if (AbsoluteFilePaths == null)
throw new ArgumentNullException("AbsoluteFilePaths");
if (AbsoluteFilePaths.Count == 0)
throw new ArgumentException("AbsoluteFilePaths is empty", "AbsoluteFilePaths");
this.AbsoluteFilePaths = AbsoluteFilePaths;
this.MontageTableLayout = true;
this.Padding = 4;
}
public override Stream GetImage(int Width, int Height)
{
List<Image> Images = new List<Image>();
try
{
// Load Images
foreach (string imageFilePath in this.AbsoluteFilePaths)
Images.Add(Bitmap.FromFile(imageFilePath));
// Build Montage
using (Bitmap montageImage = new Bitmap(Width, Height))
{
using (Graphics montageGraphics = Graphics.FromImage(montageImage))
{
montageGraphics.CompositingQuality = CompositingQuality.HighQuality;
montageGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
montageGraphics.SmoothingMode = SmoothingMode.HighQuality;
// Draw Background
if (!LosslessFormat || !BackgroundPreferTransparent)
{
Brush backgroundBrush = Brushes.White;
if (!string.IsNullOrEmpty(this.BackgroundColour))
backgroundBrush = new SolidBrush(ColorTranslator.FromHtml(this.BackgroundColour));
montageGraphics.FillRectangle(backgroundBrush, montageGraphics.VisibleClipBounds);
}
if (this.MontageHorizontalLayout)
DoHorizontalLayout(Images, montageGraphics);
else
if (this.MontageVerticalLayout)
DoVirticalLayout(Images, montageGraphics);
else
DoTableLayout(Images, montageGraphics);
}
return this.OutputImage(montageImage);
}
}
catch (Exception) { throw; }
finally
{
// Dispose of any Images
if (Images != null)
foreach (Image i in Images)
i.Dispose();
}
}
private void DoHorizontalLayout(List<Image> Images, Graphics MontageGraphics)
{
float imageScale;
float imagePosition = 0;
int imagesWidthTotal = Images.Sum(i => i.Width);
int imagesHeightMax = Images.Max(i => i.Height);
int imagesPadding = ((Images.Count - 1) * this.Padding);
imageScale = (float)(MontageGraphics.VisibleClipBounds.Width - imagesPadding) / (float)imagesWidthTotal;
if ((MontageGraphics.VisibleClipBounds.Height / (float)imagesHeightMax) < imageScale)
imageScale = (float)MontageGraphics.VisibleClipBounds.Height / (float)imagesHeightMax;
foreach (Image image in Images)
{
MontageGraphics.DrawImageResized(image, imageScale, imagePosition, 0);
imagePosition += (imageScale * image.Width) + this.Padding;
}
}
private void DoVirticalLayout(List<Image> Images, Graphics MontageGraphics)
{
float imageScale;
float imagePosition = 0;
int imagesWidthMax = Images.Max(i => i.Width);
int imagesHeightTotal = Images.Sum(i => i.Height);
int imagesPadding = ((Images.Count - 1) * this.Padding);
imageScale = (float)(MontageGraphics.VisibleClipBounds.Height - imagesPadding) / (float)imagesHeightTotal;
if ((MontageGraphics.VisibleClipBounds.Width / (float)imagesWidthMax) < imageScale)
imageScale = (float)MontageGraphics.VisibleClipBounds.Width / (float)imagesWidthMax;
foreach (Image image in Images)
{
MontageGraphics.DrawImageResized(image, imageScale, 0, imagePosition);
imagePosition += (imageScale * image.Height) + this.Padding;
}
}
private void DoTableLayout(List<Image> Images, Graphics MontageGraphics)
{
var stageSize = MontageGraphics.VisibleClipBounds.Size.ToSize();
var itemAverageSize = new SizeF(Images.Average(i => (float)i.Size.Width), Images.Average(i => (float)i.Size.Height));
var calculatedLayout = CalculateColumnCount(stageSize, itemAverageSize, Images.Count);
SizeF cellSize = new SizeF((MontageGraphics.VisibleClipBounds.Width - ((calculatedLayout.Item1 - 1) * this.Padding)) / calculatedLayout.Item1,
(MontageGraphics.VisibleClipBounds.Height - ((calculatedLayout.Item2 - 1) * this.Padding)) / calculatedLayout.Item2);
int imageIndex = 0;
for (int rowIndex = 0; rowIndex < calculatedLayout.Item2; rowIndex++)
{
for (int columnIndex = 0; columnIndex < calculatedLayout.Item1; columnIndex++)
{
if (imageIndex < Images.Count)
{
var image = Images[imageIndex];
var cellPoint = new PointF((cellSize.Width * columnIndex) + (this.Padding * columnIndex), (cellSize.Height * rowIndex) + (this.Padding * rowIndex));
MontageGraphics.Clip = new Region(new RectangleF(cellPoint, cellSize));
MontageGraphics.DrawImageResized(image);
imageIndex++;
}
else
break;
}
}
}
private Tuple<int, int, double> CalculateColumnCount(Size StageSize, SizeF ItemAverageSize, int ItemCount)
{
double? bestUsedSpace = null;
int bestColumnCount = 1;
int bestRowCount = 1;
double bestItemRatio = 1;
for (int columnCount = 1; columnCount <= ItemCount; columnCount++)
{
int rowCount = (int)Math.Ceiling((double)ItemCount / (double)columnCount);
int requiredWidthPadding = (columnCount - 1) * this.Padding;
int requiredHeightPadding = (rowCount - 1) * this.Padding;
Size usableStageSize = new Size(StageSize.Width - requiredWidthPadding, StageSize.Height - requiredHeightPadding);
double stageWidthRatio = (float)usableStageSize.Width / (float)usableStageSize.Height;
double stageHeightRatio = (float)usableStageSize.Height / (float)usableStageSize.Width;
int requiredWidth = (int)Math.Ceiling(ItemAverageSize.Width * columnCount);
int requiredHeight = (int)Math.Ceiling(ItemAverageSize.Height * rowCount);
int usedSpace = requiredWidth * requiredHeight;
int stageArea = Math.Max((requiredWidth * (int)Math.Ceiling(requiredWidth * stageHeightRatio)),
(requiredHeight * (int)Math.Ceiling(requiredHeight * stageWidthRatio)));
double usedStageSpace = (double)usedSpace / stageArea;
if (bestUsedSpace == null || bestUsedSpace < usedStageSpace)
{
bestUsedSpace = usedStageSpace;
bestColumnCount = columnCount;
bestRowCount = rowCount;
bestItemRatio = Math.Min((double)usableStageSize.Width / (double)requiredWidth, (double)usableStageSize.Height / (double)requiredHeight);
}
}
return new Tuple<int, int, double>(bestColumnCount, bestRowCount, bestItemRatio);
}
}
}
@@ -0,0 +1,73 @@
using Disco.Models.Repository;
using Disco.Services.Users;
using System;
using System.Linq;
namespace Disco.Services.Expressions.Extensions
{
public static class UserExt
{
#region Active Directory Extensions
public static object GetActiveDirectoryObjectValue(User User, string PropertyName, int Index = 0)
{
var adUserAccount = User.ActiveDirectoryAccount(PropertyName);
if (adUserAccount != null)
return adUserAccount.GetPropertyValues<object>(PropertyName).Skip(Index).FirstOrDefault();
else
return null;
}
public static string GetActiveDirectoryStringValue(User User, string PropertyName, int Index = 0)
{
var objectValue = GetActiveDirectoryObjectValue(User, PropertyName, Index);
string stringValue = objectValue as string;
if (stringValue == null && objectValue != null)
stringValue = objectValue.ToString();
return stringValue;
}
public static int GetActiveDirectoryIntegerValue(User User, string PropertyName, int Index = 0)
{
var objectValue = GetActiveDirectoryObjectValue(User, PropertyName, Index);
if (objectValue == null)
return default(int);
else
{
int intValue;
try
{
intValue = (int)Convert.ChangeType(objectValue, typeof(int));
}
catch (Exception)
{
throw;
}
return intValue;
}
}
#endregion
#region Authorization Testing Extensions
public static bool HasAuthorization(User User, string Claim)
{
var authorization = UserService.GetAuthorization(User.UserId);
return authorization.Has(Claim);
}
public static bool HasAuthorizationAll(User User, params string[] Claims)
{
var authorization = UserService.GetAuthorization(User.UserId);
return authorization.HasAll(Claims);
}
public static bool HasAuthorizationAny(User User, params string[] Claims)
{
var authorization = UserService.GetAuthorization(User.UserId);
return authorization.HasAny(Claims);
}
#endregion
}
}
@@ -0,0 +1,15 @@
using System.Collections;
namespace Disco.Services.Expressions
{
public interface IExpressionPart
{
string RawSource { get; set; }
string Source { get; set; }
bool ErrorsAllowed { get; set; }
bool ParseError { get; }
string ParseErrorMessage { get; }
bool IsDynamic { get; set; }
object Evaluate(object ExpressionContext, IDictionary Variables);
}
}
@@ -0,0 +1,74 @@
using System;
using System.Collections;
namespace Disco.Services.Expressions
{
public class TextExpressionPart : IExpressionPart
{
private string _Source;
bool IExpressionPart.ErrorsAllowed
{
get
{
return false;
}
set
{
return;
}
}
string IExpressionPart.Source
{
get
{
return this._Source;
}
set
{
return;
}
}
string IExpressionPart.RawSource
{
get
{
return this._Source;
}
set
{
return;
}
}
bool IExpressionPart.IsDynamic
{
get
{
return false;
}
set
{
return;
}
}
public bool ParseError
{
get { return false; }
}
public string ParseErrorMessage
{
get { return null; }
}
public TextExpressionPart(string Source)
{
this._Source = Source;
}
object IExpressionPart.Evaluate(object ExpressionContext, System.Collections.IDictionary Variables)
{
return this._Source;
}
}
}
@@ -0,0 +1,233 @@
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
namespace Disco.Services
{
public static class ImagingExtensions
{
public static Bitmap RotateImage(this Image Source, float Angle, Brush BackgroundColor = null, bool ResizeIfOver45Deg = true)
{
var destWidth = Source.Width;
var destHeight = Source.Height;
var resizedDest = false;
if (ResizeIfOver45Deg && ((Angle > 45 && Angle < 135) || (Angle < -45 && Angle > -135)))
{
destWidth = Source.Height;
destHeight = Source.Width;
resizedDest = true;
}
var destination = new Bitmap(destWidth, destHeight);
destination.SetResolution(Source.HorizontalResolution, Source.VerticalResolution);
using (Graphics destinationGraphics = Graphics.FromImage(destination))
{
destinationGraphics.CompositingQuality = CompositingQuality.HighQuality;
destinationGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
destinationGraphics.SmoothingMode = SmoothingMode.HighQuality;
if (BackgroundColor != null)
destinationGraphics.FillRectangle(BackgroundColor, destinationGraphics.VisibleClipBounds);
float offsetWidth = destWidth / 2;
float offsetHeight = destHeight / 2;
destinationGraphics.TranslateTransform(offsetWidth, offsetHeight);
destinationGraphics.RotateTransform(Angle);
RectangleF destinationLocation;
if (resizedDest)
destinationLocation = new RectangleF(
offsetHeight * -1, offsetWidth * -1,
destHeight, destWidth);
else
destinationLocation = new RectangleF(
offsetWidth * -1, offsetHeight * -1,
destWidth, destHeight);
destinationGraphics.DrawImage(Source, destinationLocation, new RectangleF(0, 0, Source.Width, Source.Height), GraphicsUnit.Pixel);
}
return destination;
}
public static Bitmap ResizeImage(this Image Source, int TargetWidth, int TargetHeight, Brush BackgroundColor = null)
{
var destination = new Bitmap(TargetWidth, TargetHeight);
destination.SetResolution(72, 72);
using (Graphics destinationGraphics = Graphics.FromImage(destination))
{
destinationGraphics.CompositingQuality = CompositingQuality.HighQuality;
destinationGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
destinationGraphics.SmoothingMode = SmoothingMode.HighQuality;
if (BackgroundColor != null)
destinationGraphics.FillRectangle(BackgroundColor, destinationGraphics.VisibleClipBounds);
destinationGraphics.DrawImageResized(Source);
}
return destination;
}
public static RectangleF CalculateResize(int SourceWidth, int SourceHeight, int TargetWidth, int TargetHeight, out float scaleRatio)
{
scaleRatio = Math.Min((float)(TargetWidth) / SourceWidth, (float)(TargetHeight) / SourceHeight);
float width = SourceWidth * scaleRatio,
height = SourceHeight * scaleRatio,
x = 0,
y = 0;
if (width < TargetWidth)
x = (TargetWidth - width) / 2;
if (height < TargetHeight)
y = (TargetHeight - height) / 2;
return new RectangleF(x, y, width, height);
}
public static RectangleF CalculateResize(int SourceWidth, int SourceHeight, int TargetWidth, int TargetHeight)
{
float scaleRatio;
return CalculateResize(SourceWidth, SourceHeight, TargetHeight, TargetHeight, out scaleRatio);
}
public static RectangleF CalculateResize(this Image Source, int TargetWidth, int TargetHeight, out float scaleRatio)
{
return CalculateResize(Source.Width, Source.Height, TargetWidth, TargetHeight, out scaleRatio);
}
public static RectangleF CalculateResize(this Image Source, int TargetWidth, int TargetHeight)
{
return CalculateResize(Source.Width, Source.Height, TargetHeight, TargetHeight);
}
public static void DrawImageResized(this Graphics graphics, Image SourceImage)
{
RectangleF clipBounds = graphics.VisibleClipBounds;
var resizeBounds = SourceImage.CalculateResize((int)clipBounds.Width, (int)clipBounds.Height);
graphics.DrawImage(SourceImage, resizeBounds, new RectangleF(0, 0, SourceImage.Width, SourceImage.Height), GraphicsUnit.Pixel);
}
public static void DrawImageResized(this Graphics graphics, Image SourceImage, float Scale, float LocationX, float LocationY)
{
RectangleF clipBounds = graphics.VisibleClipBounds;
float width = SourceImage.Width * Scale,
height = SourceImage.Height * Scale,
x = LocationX,
y = LocationY;
x += clipBounds.Left;
y += clipBounds.Top;
graphics.DrawImage(SourceImage, new RectangleF(x, y, width, height), new RectangleF(0, 0, SourceImage.Width, SourceImage.Height), GraphicsUnit.Pixel);
}
public static void EmbedIconOverlay(this Image Source, Image Icon)
{
int top = Math.Max(0, Source.Height - Icon.Height);
int left = Math.Max(0, Source.Width - Icon.Width);
using (Graphics sourceGraphics = Graphics.FromImage(Source))
{
sourceGraphics.DrawImage(Icon, left, top);
}
}
public static void SavePng(this Image Source, string Filename)
{
using (FileStream outStream = new FileStream(Filename, FileMode.Create, FileAccess.Write, FileShare.None))
{
SavePng(Source, outStream);
outStream.Flush();
outStream.Close();
}
}
public static void SavePng(this Image Source, Stream OutStream)
{
Source.Save(OutStream, ImageFormat.Png);
}
public static Stream SavePng(this Image Source)
{
MemoryStream outStream = new MemoryStream();
Source.SavePng(outStream);
outStream.Position = 0;
return outStream;
}
public static Stream SaveJpg(this Image Source, int Quality)
{
MemoryStream outStream = new MemoryStream();
Source.SaveJpg(Quality, outStream);
outStream.Position = 0;
return outStream;
}
public static void SaveJpg(this Image Source, int Quality, string Filename)
{
using (FileStream outStream = new FileStream(Filename, FileMode.Create, FileAccess.Write, FileShare.None))
{
SaveJpg(Source, Quality, outStream);
outStream.Flush();
outStream.Close();
}
}
public static void SaveJpg(this Image Source, int Quality, Stream OutStream)
{
ImageCodecInfo jpgCodec = ImageCodecInfo.GetImageEncoders().Where(c => c.MimeType.Equals("image/jpeg", StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
if (jpgCodec != null)
{
if (Quality < 0 || Quality > 100)
throw new ArgumentOutOfRangeException("Quality", "Quality must be a positive integer <= 100");
using (EncoderParameters ep = new EncoderParameters(1))
{
ep.Param[0] = new EncoderParameter(Encoder.Quality, Quality);
Source.Save(OutStream, jpgCodec, ep);
}
}
else
{
// Fallback
Source.Save(OutStream, ImageFormat.Jpeg);
}
}
public static Color InterpolateColours(this Color Start, Color End, double Progress)
{
if (Progress > 1 || Progress < 0)
throw new ArgumentOutOfRangeException("Progress", "Progress must be >= 0 && <= 1");
return Color.FromArgb(
(byte)(Start.A * (1 - Progress) + (End.A * Progress)),
(byte)(Start.R * (1 - Progress) + (End.R * Progress)),
(byte)(Start.G * (1 - Progress) + (End.G * Progress)),
(byte)(Start.B * (1 - Progress) + (End.B * Progress))
);
}
public static RectangleF Multiply(this RectangleF Other, float Multiplier)
{
return new RectangleF(Other.X * Multiplier, Other.Y * Multiplier, Other.Width * Multiplier, Other.Height * Multiplier);
}
public static RectangleF Divide(this RectangleF Other, float Divisor)
{
return new RectangleF(Other.X / Divisor, Other.Y / Divisor, Other.Width / Divisor, Other.Height / Divisor);
}
}
}
@@ -1,13 +1,12 @@
using System;
using Disco.Models.Repository;
using Disco.Services.Interop.ActiveDirectory;
using System.Collections.Generic;
using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;
namespace Disco.Services.Interop.ActiveDirectory
namespace Disco.Services
{
public static class ActiveDirectoryExtensions
{
@@ -98,6 +97,17 @@ namespace Disco.Services.Interop.ActiveDirectory
#endregion
public static ADUserAccount ActiveDirectoryAccount(this User User, params string[] AdditionalProperties)
{
return ActiveDirectory.RetrieveADUserAccount(User.UserId, AdditionalProperties);
}
public static ADMachineAccount ActiveDirectoryAccount(this Device Device, params string[] AdditionalProperties)
{
if (ActiveDirectory.IsValidDomainAccountId(Device.DeviceDomainId))
return ActiveDirectory.RetrieveADMachineAccount(Device.DeviceDomainId, AdditionalProperties: AdditionalProperties);
else
return null;
}
}
}
+81
View File
@@ -0,0 +1,81 @@
using Microsoft.Win32;
namespace Disco.Services.Interop
{
public static class MimeTypes
{
public static string ResolveMimeType(string Filename)
{
string fileExtension;
if (Filename.Contains("."))
fileExtension = Filename.Substring(Filename.LastIndexOf(".") + 1).ToLower();
else
fileExtension = Filename.ToLower();
// Try Known Mime Types
switch (fileExtension)
{
case "pdf":
return "application/pdf";
case "doc":
return "application/msword";
case "docx":
return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
case "docm":
return "application/vnd.ms-word.document.macroEnabled.12";
case "xml":
return "text/xml";
case "xls":
return "application/vnd.ms-excel";
case "xlsx":
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
case "xlsm":
return "application/vnd.ms-excel.sheet.macroEnabled.12";
case "csv":
return "application/vnd.ms-excel";
case "jpg":
return "image/jpeg";
case "gif":
return "image/gif";
case "png":
return "image/png";
case "bmp":
return "image/bmp";
case "avi":
return "video/avi";
case "mpeg":
case "mpg":
return "video/mpeg";
case "mp3":
return "audio/mpeg";
case "mp4":
return "video/mp4";
case "wmv":
return "video/x-ms-wmv";
case "mov":
return "video/quicktime";
}
// Check System Registry
try
{
RegistryKey regExtensionKey = Registry.ClassesRoot.OpenSubKey("." + fileExtension);
if (regExtensionKey != null)
{
string regExtensionContentType = regExtensionKey.GetValue("Content Type") as string;
if (regExtensionContentType != null)
{
return regExtensionContentType;
}
}
}
catch
{
// Ignore Errors
}
// Return Default
return "unknown/unknown";
}
}
}
+113
View File
@@ -0,0 +1,113 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Disco.Services.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Disco.Services.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap MimeType_doc48 {
get {
object obj = ResourceManager.GetObject("MimeType_doc48", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap MimeType_img16 {
get {
object obj = ResourceManager.GetObject("MimeType_img16", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap MimeType_pdf16 {
get {
object obj = ResourceManager.GetObject("MimeType_pdf16", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap MimeType_pdf48 {
get {
object obj = ResourceManager.GetObject("MimeType_pdf48", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap MimeType_unknown48 {
get {
object obj = ResourceManager.GetObject("MimeType_unknown48", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
}
}
+136
View File
@@ -0,0 +1,136 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="MimeType_doc48" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\resources\mimetype-doc48.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="MimeType_img16" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\resources\mimetype-img16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="MimeType_pdf16" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\resources\mimetype-pdf16.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="MimeType_pdf48" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\resources\mimetype-pdf48.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="MimeType_unknown48" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\resources\mimetype-unknown48.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+3
View File
@@ -16,6 +16,8 @@
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net40" />
<package id="Newtonsoft.Json" version="6.0.3" targetFramework="net45" />
<package id="Owin" version="1.0" targetFramework="net45" />
<package id="PdfiumViewer" version="2.10.0.0" targetFramework="net45" />
<package id="PDFsharp" version="1.32.3057.0" targetFramework="net45" />
<package id="RazorGenerator.Mvc" version="2.2.3" targetFramework="net45" />
<package id="Rx-Core" version="2.2.5" targetFramework="net45" />
<package id="Rx-Interfaces" version="2.2.5" targetFramework="net45" />
@@ -24,4 +26,5 @@
<package id="Rx-PlatformServices" version="2.2.5" targetFramework="net45" />
<package id="SqlServerCompact" version="4.0.8854.1" targetFramework="net40" />
<package id="WebActivatorEx" version="2.0.5" targetFramework="net45" />
<package id="ZXing.Net" version="0.14.0.1" targetFramework="net45" />
</packages>
Binary file not shown.