using System;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Web;
using AutoMapper;
using GroupDocs.Annotation.Domain;
using GroupDocs.Annotation.Exception;
using GroupDocs.Annotation.Handler;
using GroupDocs.Annotation.Handler.Input.DataObjects;
using GroupDocs.Annotation.Handler.Input;
using GroupDocs.Demo.Annotation.Webforms.BusinessLogic.Options;
using GroupDocs.Demo.Annotation.Webforms.SignalR;
using AddReplyResult = GroupDocs.Demo.Annotation.Webforms.AnnotationResults.AddReplyResult;
using AnnotationInfo = GroupDocs.Demo.Annotation.Webforms.AnnotationResults.Data.AnnotationInfo;
using AnnotationReplyInfo = GroupDocs.Demo.Annotation.Webforms.AnnotationResults.Data.AnnotationReplyInfo;
using AnnotationReviewerRights = GroupDocs.Annotation.Domain.AnnotationReviewerRights;
using AnnotationType = GroupDocs.Annotation.Domain.AnnotationType;
using CreateAnnotationResult = GroupDocs.Demo.Annotation.Webforms.AnnotationResults.CreateAnnotationResult;
using DeleteAnnotationResult = GroupDocs.Demo.Annotation.Webforms.AnnotationResults.DeleteAnnotationResult;
using DeleteReplyResult = GroupDocs.Demo.Annotation.Webforms.AnnotationResults.DeleteReplyResult;
using DocumentType = GroupDocs.Annotation.Domain.DocumentType;
using EditReplyResult = GroupDocs.Demo.Annotation.Webforms.AnnotationResults.EditReplyResult;
using GetCollaboratorsResult = GroupDocs.Demo.Annotation.Webforms.AnnotationResults.GetCollaboratorsResult;
using ListAnnotationsResult = GroupDocs.Demo.Annotation.Webforms.AnnotationResults.ListAnnotationsResult;
using MoveAnnotationResult = GroupDocs.Demo.Annotation.Webforms.AnnotationResults.MoveAnnotationResult;
using Point = GroupDocs.Demo.Annotation.Webforms.AnnotationResults.DataGeometry.Point;
using Rectangle = GroupDocs.Demo.Annotation.Webforms.AnnotationResults.DataGeometry.Rectangle;
using ResizeAnnotationResult = GroupDocs.Demo.Annotation.Webforms.AnnotationResults.ResizeAnnotationResult;
using RestoreRepliesResult = GroupDocs.Demo.Annotation.Webforms.AnnotationResults.RestoreRepliesResult;
using Result = GroupDocs.Demo.Annotation.Webforms.AnnotationResults.Result;
using ReviewerInfo = GroupDocs.Demo.Annotation.Webforms.AnnotationResults.Data.ReviewerInfo;
using SaveAnnotationTextResult = GroupDocs.Demo.Annotation.Webforms.AnnotationResults.SaveAnnotationTextResult;
using SetCollaboratorsResult = GroupDocs.Demo.Annotation.Webforms.AnnotationResults.SetCollaboratorsResult;
using TextFieldInfo = GroupDocs.Demo.Annotation.Webforms.AnnotationResults.Data.TextFieldInfo;
namespace GroupDocs.Demo.Annotation.Webforms.Service
{
/// <summary>
/// Encapsulates methods for annotations management
/// </summary>
public class AnnotationService : IAnnotationService
{
#region Fields
private readonly IAnnotationBroadcaster _annotationBroadcaster;
private readonly IAuthenticationService _authenticationSvc;
private readonly IUserDataHandler _userSvc;
private readonly AnnotationImageHandler _annotator;
private readonly IDocumentDataHandler _documentSvc;
private readonly IInputDataHandler _fileSvc;
private string storagePath = AppDomain.CurrentDomain.GetData("DataDirectory").ToString();
private IMapper _mapper;
#endregion Fields
/// <summary>
/// Initializes a new instance of the AnnotationService class
/// </summary>
/// <param name="annotationBroadcaster">The annotation Socket events broadcasting object</param>
/// <param name="authenticationSvc">The authentication service</param>
/// <param name="annotator">The annotation management service</param>
public AnnotationService(IAnnotationBroadcaster annotationBroadcaster, IAuthenticationService authenticationSvc,
AnnotationImageHandler annotator)
{
_annotationBroadcaster = annotationBroadcaster;
_authenticationSvc = authenticationSvc;
_userSvc = annotator.GetUserDataHandler();
_annotator = annotator;
_documentSvc = annotator.GetDocumentDataHandler();
_fileSvc = annotator.GetInputDataHandler();
MapperConfiguration config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<GroupDocs.Annotation.Domain.Rectangle, Rectangle>();
cfg.CreateMap<GroupDocs.Annotation.Domain.ReviewerInfo, ReviewerInfo>();
cfg.CreateMap<ReviewerInfo, GroupDocs.Annotation.Domain.ReviewerInfo>();
cfg.CreateMap<GroupDocs.Annotation.Domain.AnnotationReplyInfo, AnnotationReplyInfo>();
cfg.CreateMap<GroupDocs.Annotation.Domain.TextFieldInfo, TextFieldInfo>();
cfg.CreateMap<GroupDocs.Annotation.Domain.Results.Result, Result>();
cfg.CreateMap<GroupDocs.Annotation.Domain.Point?, Point>()
.ForMember(dst => dst.X, opt => opt.MapFrom(src => src.HasValue ? src.Value.X : 0.0))
.ForMember(dst => dst.Y, opt => opt.MapFrom(src => src.HasValue ? src.Value.Y : 0.0));
cfg.CreateMap<GroupDocs.Annotation.Domain.AnnotationInfo, AnnotationInfo>();
cfg.CreateMap<GroupDocs.Annotation.Domain.Results.ListAnnotationsResult, ListAnnotationsResult>();
cfg.CreateMap<GroupDocs.Annotation.Domain.Results.SetCollaboratorsResult, SetCollaboratorsResult>();
cfg.CreateMap<GroupDocs.Annotation.Domain.Results.CreateAnnotationResult, CreateAnnotationResult>();
cfg.CreateMap<GroupDocs.Annotation.Domain.Results.DeleteAnnotationResult, DeleteAnnotationResult>();
cfg.CreateMap<GroupDocs.Annotation.Domain.Results.AddReplyResult, AddReplyResult>();
cfg.CreateMap<GroupDocs.Annotation.Domain.Results.DeleteReplyResult, DeleteReplyResult>();
cfg.CreateMap<GroupDocs.Annotation.Domain.Results.EditReplyResult, EditReplyResult>();
cfg.CreateMap<GroupDocs.Annotation.Domain.Results.MoveAnnotationResult, MoveAnnotationResult>();
cfg.CreateMap<GroupDocs.Annotation.Domain.Results.ResizeAnnotationResult, ResizeAnnotationResult>();
cfg.CreateMap<GroupDocs.Annotation.Domain.Results.SaveAnnotationTextResult, SaveAnnotationTextResult>();
cfg.CreateMap<GroupDocs.Annotation.Domain.Results.GetCollaboratorsResult, GetCollaboratorsResult>();
});
_mapper = config.CreateMapper();
}
/// <summary>
/// Returns a list of annotations for a document
/// </summary>
/// <param name="connectionId">Socket connection identifier to validate user permissions for</param>
/// <param name="fileId">The document path to get annotations for</param>
/// <returns>An instance of an object containing information document annotations</returns>
public ListAnnotationsResult ListAnnotations(string connectionId, string fileId)
{
var reviewer = _annotationBroadcaster.GetConnectionUser(connectionId);
if (reviewer == null)
{
throw new AnnotatorException("There is no such reviewer.");
}
var user = _userSvc.GetUserByGuid(reviewer.Value.UserGuid);
var document = GetDocument(fileId, user.Id);
if (document == null)
{
_documentSvc.Add(new Document
{
OwnerId = user.Id,
Name = fileId,
CreatedOn = DateTime.Now,
Guid = Guid.NewGuid().ToString()
});
document = _documentSvc.GetDocument(fileId);
}
return _mapper.Map<ListAnnotationsResult>(_annotator.GetAnnotations(document.Id, null, user.Id));
}
/// <summary>
/// Creates a new annotation for a document
/// </summary>
/// <param name="connectionId">Socket connection identifier to validate user permissions for</param>
/// <param name="fileId">The document path to create the annotation for</param>
/// <param name="type">The annotation type</param>
/// <param name="message">The annotation text message</param>
/// <param name="rectangle">The annotation bounds</param>
/// <param name="pageNumber">The document page number to create the annotation at</param>
/// <param name="annotationPosition">The annotation left-top position</param>
/// <param name="svgPath">The annotation SVG path</param>
/// <param name="options">The annotation drawing options (pen color, width etc.)</param>
/// <param name="font">The annotation text font</param>
/// <returns>An instance of an object containing information about a created annotation</returns>
public CreateAnnotationResult CreateAnnotation(string connectionId, string fileId, byte type, string message,
Rectangle rectangle, int pageNumber, Point annotationPosition, string svgPath, DrawingOptions options, FontOptions font)
{
var reviewer = _annotationBroadcaster.GetConnectionUser(connectionId);
if (reviewer == null)
{
throw new AnnotatorException("There is no such reviewer.");
}
var user = _userSvc.GetUserByGuid(reviewer.Value.UserGuid);
var document = GetDocument(fileId, user.Id);
var collaboratorsInfo = _mapper.Map<GetCollaboratorsResult>(_annotator.GetCollaborators(document.Id));
var caller = collaboratorsInfo.Collaborators.FirstOrDefault(c => c.Guid == reviewer.Value.UserGuid);
var annotation = new GroupDocs.Annotation.Domain.AnnotationInfo
{
Type = (AnnotationType)type,
Box = new GroupDocs.Annotation.Domain.Rectangle(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height),
PageNumber = pageNumber,
AnnotationPosition = new GroupDocs.Annotation.Domain.Point(annotationPosition.X, annotationPosition.Y),
SvgPath = svgPath,
PenColor = options != null ? options.PenColor : -984833,
PenWidth = options != null ? (byte?)options.PenWidth : 1,
PenStyle = options != null ? (byte?)options.DashStyle : (byte?)DashStyle.Solid,
BackgroundColor = options != null ? options.BrushColor : -984833,
FontFamily = !string.IsNullOrEmpty(font.Family) ? "Arial" : "Calibri",
FontSize = font.Size != null ? font.Size : 4,
};
if (!string.IsNullOrWhiteSpace(message))
{
annotation.Replies = new[] { new GroupDocs.Annotation.Domain.AnnotationReplyInfo { Message = message } };
}
var result = _annotator.CreateAnnotation(annotation, document.Id, user.Id);
_annotationBroadcaster.CreateAnnotation(
collaboratorsInfo.Collaborators.Select(c => c.Guid).ToList(),
connectionId,
reviewer.Value.UserGuid,
caller != null ? caller.PrimaryEmail : _authenticationSvc.AnonymousUserName,
fileId,
annotation.Type,
result.Guid,
(byte)result.Access,
result.ReplyGuid,
pageNumber,
_mapper.Map<Rectangle>(rectangle),
annotationPosition,
svgPath,
options,
font);
return _mapper.Map<CreateAnnotationResult>(result);
}
/// <summary>
/// Removes an annotation from a document
/// </summary>
/// <param name="connectionId">Socket connection identifier to validate user permissions for</param>
/// <param name="fileId">The document path to remove the annotation from</param>
/// <param name="annotationGuid">The annotation global unique identifier</param>
/// <returns>An instance of an object containing the removed annotation metadata</returns>
public DeleteAnnotationResult DeleteAnnotation(string connectionId, string fileId, string annotationGuid)
{
var reviewer = _annotationBroadcaster.GetConnectionUser(connectionId);
if (reviewer == null)
{
throw new AnnotatorException("There is no such reviewer.");
}
var user = _userSvc.GetUserByGuid(reviewer.Value.UserGuid);
var document = GetDocument(fileId, user.Id);
var collaboratorsInfo = _mapper.Map<GetCollaboratorsResult>(_annotator.GetCollaborators(document.Id));
var annotation = _annotator.GetAnnotation(annotationGuid, document.Id, user.Id);
var result = _annotator.DeleteAnnotation(annotation.Id, document.Id, user.Id);
_annotationBroadcaster.DeleteAnnotation(collaboratorsInfo.Collaborators.Select(c => c.Guid).ToList(), fileId, connectionId, annotationGuid);
return _mapper.Map<DeleteAnnotationResult>(result);
}
/// <summary>
/// Adds a reply to an annotation
/// </summary>
/// <param name="connectionId">Socket connection identifier to validate user permissions for</param>
/// <param name="fileId">The document path to add the reply to</param>
/// <param name="annotationGuid">The annotation global unique identifier</param>
/// <param name="message">The reply text</param>
/// <param name="parentReplyGuid">The parent reply global unique identifier</param>
/// <returns>An instance of an object containing the the added reply metadata</returns>
public AddReplyResult AddAnnotationReply(string connectionId, string fileId, string annotationGuid, string message, string parentReplyGuid)
{
var reviewer = _annotationBroadcaster.GetConnectionUser(connectionId);
if (reviewer == null)
{
throw new AnnotatorException("There is no such reviewer.");
}
var user = _userSvc.GetUserByGuid(reviewer.Value.UserGuid);
var document = GetDocument(fileId, user.Id);
var annotation = _annotator.GetAnnotation(annotationGuid, document.Id, user.Id);
var collaboratorsInfo = _annotator.GetCollaborators(document.Id);
var caller = collaboratorsInfo.Collaborators.FirstOrDefault(c => c.Guid == reviewer.Value.UserGuid);
var callerName = caller != null && (!string.IsNullOrEmpty(caller.FirstName) || !string.IsNullOrEmpty(caller.LastName)) ?
string.Format("{0} {1}", caller.FirstName ?? string.Empty, caller.LastName ?? string.Empty).Trim() :
_authenticationSvc.AnonymousUserName;
var result = _annotator.CreateAnnotationReply(annotation.Id, message, parentReplyGuid, document.Id, user.Id);
_annotationBroadcaster.AddAnnotationReply(collaboratorsInfo.Collaborators.Select(c => c.Guid).ToList(),
connectionId, reviewer.Value.UserGuid, callerName, annotationGuid,
result.ReplyGuid, parentReplyGuid,
result.ReplyDateTime, message);
return _mapper.Map<AddReplyResult>(result);
}
/// <summary>
/// Removes a reply from an annotation
/// </summary>
/// <param name="connectionId">Socket connection identifier to validate user permissions for</param>
/// <param name="fileId">The document path to remove the reply from</param>
/// <param name="annotationGuid">The annotation global unique identifier</param>
/// <param name="replyGuid">The reply global unique identifier</param>
/// <returns>An instance of an object containing information about the removed reply</returns>
public DeleteReplyResult DeleteAnnotationReply(string connectionId, string fileId, string annotationGuid, string replyGuid)
{
var reviewer = _annotationBroadcaster.GetConnectionUser(connectionId);
if (reviewer == null)
{
throw new AnnotatorException("There is no such reviewer.");
}
var user = _userSvc.GetUserByGuid(reviewer.Value.UserGuid);
var document = GetDocument(fileId, user.Id);
var collaboratorsInfo = _annotator.GetCollaborators(document.Id);
var result = _annotator.DeleteAnnotationReply(replyGuid, document.Id, user.Id);
_annotationBroadcaster.DeleteAnnotationReply(collaboratorsInfo.Collaborators.Select(c => c.Guid).ToList(), connectionId, annotationGuid, replyGuid, _mapper.Map<AnnotationReplyInfo[]>(result.Replies));
return _mapper.Map<DeleteReplyResult>(result);
}
/// <summary>
/// Updates a reply text
/// </summary>
/// <param name="connectionId">Socket connection identifier to validate user permissions for</param>
/// <param name="fileId">The document path to update the reply text for</param>
/// <param name="annotationGuid">The annotation global unique identifier</param>
/// <param name="replyGuid">The reply global unique identifier</param>
/// <param name="message">The text message to update</param>
/// <returns>An instance of an object containing the operation result</returns>
public EditReplyResult EditAnnotationReply(string connectionId, string fileId, string annotationGuid, string replyGuid, string message)
{
var reviewer = _annotationBroadcaster.GetConnectionUser(connectionId);
if (reviewer == null)
{
throw new AnnotatorException("There is no such reviewer.");
}
var user = _userSvc.GetUserByGuid(reviewer.Value.UserGuid);
var document = GetDocument(fileId, user.Id);
var collaboratorsInfo = _annotator.GetCollaborators(document.Id);
var result = _annotator.EditAnnotationReply(replyGuid, message, document.Id, user.Id);
_annotationBroadcaster.EditAnnotationReply(collaboratorsInfo.Collaborators.Select(c => c.Guid).ToList(), connectionId, annotationGuid, replyGuid, message);
return _mapper.Map<EditReplyResult>(result);
}
/// <summary>
/// Restores a hierarchy of annotation replies
/// </summary>
/// <param name="connectionId">Socket connection identifier to validate user permissions for</param>
/// <param name="fileId">The document path to update the reply text for</param>
/// <param name="annotationGuid">The annotation global unique identifier</param>
/// <param name="replies">The list of annotation replies to restore</param>
/// <returns>An instance of an object containing the operation result</returns>
public RestoreRepliesResult RestoreAnnotationReplies(string connectionId, string fileId, string annotationGuid, AnnotationReplyInfo[] replies)
{
var reviewer = _annotationBroadcaster.GetConnectionUser(connectionId);
if (reviewer == null)
{
throw new AnnotatorException("There is no such reviewer.");
}
var user = _userSvc.GetUserByGuid(reviewer.Value.UserGuid);
var document = GetDocument(fileId, user.Id);
_annotator.CheckReviewerPermissions(user.Id, document.Id, AnnotationReviewerRights.CanAnnotate);
if (replies == null || replies.Length == 0)
{
return new RestoreRepliesResult { AnnotationGuid = annotationGuid, ReplyIds = new string[0] };
}
var idsMap = new StringDictionary();
var result = new RestoreRepliesResult { AnnotationGuid = annotationGuid, ReplyIds = new string[replies.Length] };
var annotation = _mapper.Map<CreateAnnotationResult>(_annotator.GetAnnotation(annotationGuid, document.Id, user.Id));
for (var i = 0; i < replies.Length; i++)
{
var r = replies[i];
var parentGuid = (!string.IsNullOrEmpty(r.ParentReplyGuid) && idsMap.ContainsKey(r.ParentReplyGuid) ?
idsMap[r.ParentReplyGuid] : r.ParentReplyGuid);
var replyResult = _annotator.CreateAnnotationReply(annotation.Id, r.Message, parentGuid, document.Id, user.Id);
idsMap[r.Guid] = replyResult.ReplyGuid;
result.ReplyIds[i] = replyResult.ReplyGuid;
}
return result;
}
/// <summary>
/// Resisizes the annotation
/// </summary>
/// <param name="connectionId">Socket connection identifier to validate user permissions for</param>
/// <param name="fileId">The document path to resize the annotation for</param>
/// <param name="annotationGuid">The annotation global unique identifier</param>
/// <param name="width">The new width of the annotation</param>
/// <param name="height">The new height of the annotation</param>
/// <returns>An instance of an object containing the operation result</returns>
public ResizeAnnotationResult ResizeAnnotation(string connectionId, string fileId, string annotationGuid, double width, double height)
{
var reviewer = _annotationBroadcaster.GetConnectionUser(connectionId);
if (reviewer == null)
{
throw new AnnotatorException("There is no such reviewer.");
}
var user = _userSvc.GetUserByGuid(reviewer.Value.UserGuid);
var document = GetDocument(fileId, user.Id);
var collaboratorsInfo = _annotator.GetCollaborators(document.Id);
var annotation = _annotator.GetAnnotation(annotationGuid, document.Id, user.Id);
var result = _annotator.ResizeAnnotation(annotation.Id, new AnnotationSizeInfo { Width = width, Height = height }, document.Id, user.Id);
_annotationBroadcaster.ResizeAnnotation(collaboratorsInfo.Collaborators.Select(c => c.Guid).ToList(), fileId, connectionId, annotationGuid, width, height);
return _mapper.Map<ResizeAnnotationResult>(result);
}
/// <summary>
/// Moves the annotation marker to a new position
/// </summary>
/// <param name="connectionId">Socket connection identifier to validate user permissions for</param>
/// <param name="fileId">The document path to move the annotation marker for</param>
/// <param name="annotationGuid">The annotation global unique identifier</param>
/// <param name="left">The X coordinate of the annotation</param>
/// <param name="top">The Y coordinate of the annotation</param>
/// <param name="pageNumber">The document page number to move the annotation to</param>
/// <returns>An instance of an object containing the operation result and annotation metadata</returns>
public MoveAnnotationResult MoveAnnotationMarker(string connectionId, string fileId, string annotationGuid, double left, double top, int? pageNumber)
{
var reviewer = _annotationBroadcaster.GetConnectionUser(connectionId);
if (reviewer == null)
{
throw new AnnotatorException("There is no such reviewer.");
}
var user = _userSvc.GetUserByGuid(reviewer.Value.UserGuid);
var document = GetDocument(fileId, user.Id);
var collaboratorsInfo = _annotator.GetCollaborators(document.Id);
var annotation = _annotator.GetAnnotation(annotationGuid, document.Id, user.Id);
var position = new Point { X = left, Y = top };
var result = _annotator.MoveAnnotationMarker(annotation.Id, new GroupDocs.Annotation.Domain.Point(position.X, position.Y), pageNumber, document.Id, user.Id);
_annotationBroadcaster.MoveAnnotationMarker(collaboratorsInfo.Collaborators.Select(c => c.Guid).ToList(), connectionId, annotationGuid, position, pageNumber);
return _mapper.Map<MoveAnnotationResult>(result);
}
/// <summary>
/// Updates the text field information
/// </summary>
/// <param name="connectionId">Socket connection identifier to validate user permissions for</param>
/// <param name="fileId">The document path to update the text field information for</param>
/// <param name="annotationGuid">The annotation global unique identifier</param>
/// <param name="text">The text of the annotation</param>
/// <param name="fontFamily">The font family used to render the text</param>
/// <param name="fontSize">The font size used to render the text</param>
/// <returns>An instance of an object containing the operation result</returns>
public SaveAnnotationTextResult SaveTextField(string connectionId, string fileId, string annotationGuid, string text, string fontFamily, double fontSize)
{
var reviewer = _annotationBroadcaster.GetConnectionUser(connectionId);
if (reviewer == null)
{
throw new AnnotatorException("There is no such reviewer.");
}
var user = _userSvc.GetUserByGuid(reviewer.Value.UserGuid);
var document = GetDocument(fileId, user.Id);
var collaboratorsInfo = _annotator.GetCollaborators(document.Id);
var annotation = _annotator.GetAnnotation(annotationGuid, document.Id, user.Id);
var result = _annotator.SaveTextField(annotation.Id, new GroupDocs.Annotation.Domain.TextFieldInfo { FieldText = text, FontFamily = fontFamily, FontSize = fontSize }, document.Id, user.Id);
_annotationBroadcaster.UpdateTextField(collaboratorsInfo.Collaborators.Select(c => c.Guid).ToList(), connectionId, annotationGuid, text, fontFamily, fontSize);
return _mapper.Map<SaveAnnotationTextResult>(result);
}
/// <summary>
/// Updates the text field color
/// </summary>
/// <param name="connectionId">Socket connection identifier to validate user permissions for</param>
/// <param name="fileId">The document path to update the text field color for</param>
/// <param name="annotationGuid">The annotation global unique identifier</param>
/// <param name="fontColor">The font color of the text</param>
/// <returns>An instance of an object containing the operation result</returns>
public SaveAnnotationTextResult SetTextFieldColor(string connectionId, string fileId, string annotationGuid, int fontColor)
{
var reviewer = _annotationBroadcaster.GetConnectionUser(connectionId);
if (reviewer == null)
{
throw new AnnotatorException("There is no such reviewer.");
}
var user = _userSvc.GetUserByGuid(reviewer.Value.UserGuid);
var document = GetDocument(fileId, user.Id);
var collaboratorsInfo = _annotator.GetCollaborators(document.Id);
var annotation = _annotator.GetAnnotation(annotationGuid, document.Id, user.Id);
var result = _annotator.SetTextFieldColor(annotation.Id, fontColor, document.Id, user.Id);
_annotationBroadcaster.SetTextFieldColor(collaboratorsInfo.Collaborators.Select(c => c.Guid).ToList(), fileId, connectionId, annotationGuid, fontColor);
return _mapper.Map<SaveAnnotationTextResult>(result);
}
/// <summary>
/// Updates the background color of the annotation
/// </summary>
/// <param name="connectionId">Socket connection identifier to validate user permissions for</param>
/// <param name="fileId">The document path to update the background color for</param>
/// <param name="annotationGuid">The annotation global unique identifier</param>
/// <param name="color">The background color of the annotation</param>
/// <returns>An instance of an object containing the operation result</returns>
public SaveAnnotationTextResult SetAnnotationBackgroundColor(string connectionId, string fileId, string annotationGuid, int color)
{
var reviewer = _annotationBroadcaster.GetConnectionUser(connectionId);
if (reviewer == null)
{
throw new AnnotatorException("There is no such reviewer.");
}
var user = _userSvc.GetUserByGuid(reviewer.Value.UserGuid);
var document = GetDocument(fileId, user.Id);
var collaboratorsInfo = _annotator.GetCollaborators(document.Id);
var annotation = _annotator.GetAnnotation(annotationGuid, document.Id, user.Id);
var result = _annotator.SetAnnotationBackgroundColor(annotation.Id, color, document.Id, user.Id);
_annotationBroadcaster.SetAnnotationBackgroundColor(collaboratorsInfo.Collaborators.Select(c => c.Guid).ToList(), fileId, connectionId, annotationGuid, color);
return _mapper.Map<SaveAnnotationTextResult>(result);
}
/// <summary>
/// Adds document collaborator
/// </summary>
/// <param name="fileId">The document path to add the collaborator to</param>
/// <param name="reviewerEmail">The email address of the collaborator</param>
/// <param name="reviewerFirstName">The first name of the collaborator</param>
/// <param name="reviewerLastName">The last name of the collaborator</param>
/// <param name="reviewerInvitationMessage">The invitation text message to be sent to the collaborator</param>
/// <param name="avatar">The file stream of the collaborator's avatar</param>
/// <returns>An instance of an object containing the operation result and collaborators details</returns>
public SetCollaboratorsResult AddCollaborator(string fileId, string reviewerEmail, string reviewerFirstName, string reviewerLastName, string reviewerInvitationMessage, Stream avatar = null)
{
return AddCollaborator(fileId, reviewerEmail, reviewerFirstName, reviewerLastName, reviewerInvitationMessage, AnnotationReviewerRights.All, avatar);
}
/// <summary>
/// Adds document collaborator
/// </summary>
/// <param name="fileId">The document path to add the collaborator to</param>
/// <param name="reviewerEmail">The email address of the collaborator</param>
/// <param name="reviewerFirstName">The first name of the collaborator</param>
/// <param name="reviewerLastName">The last name of the collaborator</param>
/// <param name="reviewerInvitationMessage">The invitation text message to be sent to the collaborator</param>
/// <param name="rights">The annotation permissions for the collaborator</param>
/// <param name="avatar">The file stream of the collaborator's avatar</param>
/// <returns>An instance of an object containing the operation result and collaborators details</returns>
public SetCollaboratorsResult AddCollaborator(string fileId, string reviewerEmail, string reviewerFirstName, string reviewerLastName, string reviewerInvitationMessage, AnnotationReviewerRights rights, Stream avatar = null)
{
MemoryStream memoryStream = (MemoryStream)avatar;
var reviewer = new GroupDocs.Annotation.Domain.ReviewerInfo
{
PrimaryEmail = reviewerEmail,
FirstName = reviewerFirstName,
LastName = reviewerLastName,
AccessRights = rights,
Avatar = memoryStream != null ? memoryStream.ToArray() : new byte[0]
};
User user = _userSvc.GetUserByEmail(reviewerEmail);
if (user == null)
{
_userSvc.Add(new User
{
Email = reviewer.PrimaryEmail,
Guid = Guid.NewGuid().ToString(),
Photo = reviewer.Avatar,
FirstName = reviewer.FirstName,
LastName = reviewer.LastName
});
user = _userSvc.GetUserByEmail(reviewerEmail);
}
var document = GetDocument(fileId, user.Id);
var result = _mapper.Map<SetCollaboratorsResult>(_annotator.AddCollaborator(document.Id, reviewer));
return result;
}
/// <summary>
/// Removes the document collaborator
/// </summary>
/// <param name="fileId">The document path to remove the collaborator from</param>
/// <param name="reviewerEmail">The email address of the collaborator</param>
/// <returns>An instance of an object containing the operation result and collaborators details</returns>
public SetCollaboratorsResult DeleteCollaborator(string fileId, string reviewerEmail)
{
var document = _documentSvc.GetDocument(fileId);
var result = _mapper.Map<SetCollaboratorsResult>(_annotator.DeleteCollaborator(document.Id, reviewerEmail));
return result;
}
/// <summary>
/// Returns the document collaborators information
/// </summary>
/// <param name="fileId">The document path to get collaborators for</param>
/// <returns>An instance of an object containing the operation result and collaborators details</returns>
public GetCollaboratorsResult GetCollaborators(string fileId)
{
var document = _documentSvc.GetDocument(fileId);
return _mapper.Map<GetCollaboratorsResult>(_annotator.GetCollaborators(document.Id));
}
/// <summary>
/// Returns the collaborator's metadata
/// </summary>
/// <param name="userId">The collaborator global unique identifier</param>
/// <returns>An instance of an object containing the collaborator's details</returns>
public ReviewerInfo GetCollaboratorMetadata(string userId)
{
return _mapper.Map<ReviewerInfo>(_annotator.GetCollaboratorMetadata(userId));
}
/// <summary>
/// Returns an annotation document collaborator's metadata
/// </summary>
/// <param name="fileId">The document path to get collaborator for</param>
/// <param name="userName">The collaborator name</param>
/// <returns>An instance of an object containing the collaborator's details</returns>
public ReviewerInfo GetDocumentCollaborator(string fileId, string userName)
{
var document = _documentSvc.GetDocument(fileId);
return _mapper.Map<ReviewerInfo>(_annotator.GetDocumentCollaborator(document.Id, userName));
}
/// <summary>
/// Updates a collaborator display color
/// </summary>
/// <param name="fileId">The document path to update the collaborator display color for</param>
/// <param name="userName">The collaborator name</param>
/// <param name="color">The display color</param>
/// <returns>An instance of an object containing the collaborator's details</returns>
public ReviewerInfo SetCollaboratorColor(string fileId, string userName, uint color)
{
var document = _documentSvc.GetDocument(fileId);
var collaborator = _annotator.GetDocumentCollaborator(document.Id, userName);
collaborator.Color = color;
var result = _annotator.UpdateCollaborator(document.Id, collaborator);
var reviewer = result.Collaborators.FirstOrDefault(c => c.PrimaryEmail == userName);
_annotationBroadcaster.SetReviewersColors(result.Collaborators.Select(c => c.Guid).ToList(), null, _mapper.Map<ReviewerInfo[]>(result.Collaborators));
return _mapper.Map<ReviewerInfo>(reviewer);
}
/// <summary>
/// Updates collaborator annotation permissions
/// </summary>
/// <param name="fileId">The document path to update the collaborator permission for</param>
/// <param name="userName">The collaborator name</param>
/// <param name="rights">The collaborator's annotation permissions</param>
/// <returns>An instance of an object containing the collaborator's details</returns>
public ReviewerInfo SetCollaboratorRights(string fileId, string userName, AnnotationReviewerRights rights)
{
var document = _documentSvc.GetDocument(fileId);
var collaborator = _annotator.GetDocumentCollaborator(document.Id, userName);
collaborator.AccessRights = rights;
var result = _annotator.UpdateCollaborator(document.Id, collaborator);
_annotationBroadcaster.SetReviewersColors(result.Collaborators.Select(c => c.Guid).ToList(), null, _mapper.Map<ReviewerInfo[]>(result.Collaborators));
return _mapper.Map<ReviewerInfo>(result.Collaborators.FirstOrDefault(c => c.PrimaryEmail == userName));
}
/// <summary>
/// Updates the document global annotation permissions
/// </summary>
/// <param name="fileId">The document path to update the permissions for</param>
/// <param name="rights">The annotation permissions</param>
public void SetDocumentAccessRights(string fileId, AnnotationReviewerRights rights)
{
var document = _documentSvc.GetDocument(fileId);
long documentId = document != null ? document.Id : _annotator.CreateDocument(fileId);
_annotator.SetDocumentAccessRights(documentId, rights);
}
/// <summary>
/// Removes document annotations
/// </summary>
/// <param name="fileId">The document path to remove annotations from</param>
public void DeleteAnnotations(string fileId)
{
var document = _documentSvc.GetDocument(fileId);
_annotator.DeleteAnnotations(document.Id);
}
/// <summary>
/// Imports annotations from a document into the internal storage
/// </summary>
/// <param name="connectionId">Socket connection identifier to validate user permissions for</param>
/// <param name="fileId">The document path to import annotations from</param>
public void ImportAnnotations(string connectionId, string fileId)
{
var document = _documentSvc.GetDocument(fileId);
if (document == null)
{
var newDocument = new Document
{
Name = fileId,
CreatedOn = DateTime.Now,
Guid = Guid.NewGuid().ToString()
};
_documentSvc.Add(newDocument);
document = _documentSvc.GetDocument(fileId);
}
long userId = 0;
if (connectionId == null)
{
AddCollaborator(fileId, _authenticationSvc.AnonymousUserName, null, null, null);
}
else
{
var connectionUser = _annotationBroadcaster.GetConnectionUser(connectionId);
if (connectionUser == null)
{
throw new AnnotatorException("Connection user is null.");
}
var user = _userSvc.GetUserByGuid(connectionUser.Value.UserGuid);
userId = user.Id;
_annotator.AddCollaborator(document.Id,
new GroupDocs.Annotation.Domain.ReviewerInfo
{
PrimaryEmail = user.Email,
FirstName = user.FirstName,
LastName = user.LastName
});
}
Import(document.Id, fileId, userId);
ImportAnnotationWithCleaning(fileId);
}
private void ImportAnnotationWithCleaning(string fileId)
{
using (Stream inputDoc = _annotator.GetPdfFile(fileId).Stream)
{
SaveCleanDocument(inputDoc, fileId);
}
}
/// <summary>
/// Exports annotations from the internal storage to the original document
/// </summary>
/// <param name="connectionId">Socket connection identifier to validate user permissions for</param>
/// <param name="fileId">The document path to export annotations to</param>
/// <returns>A path to the result file containing exported annotations</returns>
public string ExportAnnotations(string connectionId, string fileId)
{
var document = _documentSvc.GetDocument(fileId);
var reviewer = _annotationBroadcaster.GetConnectionUser(connectionId);
if (reviewer == null)
{
throw new AnnotatorException("There is no such reviewer.");
}
var user = _userSvc.GetUserByGuid(reviewer.Value.UserGuid);
_annotator.CheckReviewerPermissions(user.Id, document.Id, AnnotationReviewerRights.CanExport);
return Export(document.Id, fileId, user.Id);
}
/// <summary>
/// Converts a document to PDF format
/// </summary>
/// <param name="connectionId">Socket connection identifier to validate user permissions for</param>
/// <param name="fileId">The document path to convert</param>
/// <returns>A path to the converted file</returns>
public string GetAsPdf(string connectionId, string fileId)
{
var document = _documentSvc.GetDocument(fileId);
var reviewer = _annotationBroadcaster.GetConnectionUser(connectionId);
if (reviewer == null)
{
throw new AnnotatorException("There is no such reviewer.");
}
var user = _userSvc.GetUserByGuid(reviewer.Value.UserGuid);
_annotator.CheckReviewerPermissions(user.Id, document.Id, AnnotationReviewerRights.CanDownload);
var tempStorage = AppDomain.CurrentDomain.GetData("DataDirectory") + "\\Temp";
var fileName = Path.ChangeExtension(Path.GetRandomFileName(), "pdf");
using (Stream inputDoc = _annotator.GetPdfFile(fileId).Stream)
using (var tempDoc = new FileStream(Path.Combine(tempStorage, fileName), FileMode.Create))
{
inputDoc.Position = 0;
inputDoc.CopyTo(tempDoc);
return "Temp\\" + fileName;
}
}
private Document GetDocument(string fileName, long userId)
{
var document = _documentSvc.GetDocument(fileName);
if (document == null)
{
_annotator.CreateDocument(fileName, DocumentType.Pdf, userId);
var doc = _documentSvc.GetDocument(fileName);
return doc;
}
return document;
}
public string Export(long documentId, string fileId, long userId)
{
using (Stream inputDoc = _annotator.GetPdfFile(fileId).Stream)
{
var resultStream = _annotator.ExportAnnotationsToDocument(documentId, inputDoc, DocumentType.Pdf, userId);
var fileName = string.Format("{0}_WithComments_{1}.{2}",
Path.GetFileNameWithoutExtension(fileId),
DateTime.UtcNow.ToString("yyyy-MM-dd HH-mm-ss"),
"pdf");
string tempFilePath = Path.Combine(storagePath + "//Temp", fileName);
try
{
using (var fs = new FileStream(tempFilePath, FileMode.Create))
{
resultStream.Position = 0;
resultStream.CopyTo(fs);
}
}
catch (Exception e)
{
throw new AnnotatorException("Failed to save output file to the storage.");
}
return Path.Combine("Temp", fileName);
}
}
/// <summary>
/// Import annotations with merge functionality
/// </summary>
/// <param name="documentId"></param>
/// <param name="fileId"></param>
/// <param name="userId"></param>
private void Import(long documentId, string fileId, long userId)
{
using (Stream inputDoc = _annotator.GetPdfFile(fileId).Stream)
{
_annotator.ImportAnnotations(documentId, inputDoc, DocumentType.Pdf, userId);
}
}
private void SaveCleanDocument(Stream inputDoc, string fileId)
{
Stream resultClean = _annotator.RemoveAnnotationStream(inputDoc, DocumentType.Pdf);
string path = Path.GetDirectoryName(fileId);
var uploadDir = path != ""
? Path.Combine(HttpContext.Current.Server.MapPath("~/App_Data"), path)
: HttpContext.Current.Server.MapPath("~/App_Data");
var fileName = string.Format("{0}.{1}",
Path.GetFileNameWithoutExtension(fileId),
"pdf");
var filePath = Path.Combine(uploadDir, fileName);
inputDoc.Dispose();
if (!Directory.Exists(uploadDir))
{
Directory.CreateDirectory(uploadDir);
}
using (var stream = File.Create(filePath))
{
resultClean.CopyTo(stream);
}
resultClean.Dispose();
}
}
}