Projekt

Obecné

Profil

Stáhnout (24.3 KB) Statistiky
| Větev: | Tag: | Revize:
1 eff8ec56 Vojtěch Bartička
using Core.Contexts;
2
using Core.Entities;
3
using Models.Annotations;
4
using Models.Enums;
5
using Serilog;
6
using System;
7
using System.Collections.Generic;
8
using System.Linq;
9
using System.Text;
10
using System.Threading.Tasks;
11 6bdb3d95 Vojtěch Bartička
using Microsoft.EntityFrameworkCore;
12 a6675a6d Vojtěch Bartička
using AutoMapper;
13
using Models.Tags;
14 3c185841 Vojtěch Bartička
using Ganss.XSS;
15
using HtmlAgilityPack;
16
using System.Text.RegularExpressions;
17 eff8ec56 Vojtěch Bartička
18
namespace Core.Services.AnnotationService
19
{
20
    public class AnnotationServiceEF : IAnnotationService
21
    {
22
        private readonly DatabaseContext context;
23
        private readonly ILogger logger;
24 a6675a6d Vojtěch Bartička
        private readonly IMapper mapper;
25 eff8ec56 Vojtěch Bartička
26 0a9f9349 Vojtěch Bartička
        private const string TAG_ID_ATTRIBUTE_NAME = "aswi-tag-id";
27 c9762683 Vojtěch Bartička
        private const string TAG_INSTANCE_ATTRIBUTE_NAME = "aswi-tag-instance";
28 0a9f9349 Vojtěch Bartička
29 a6675a6d Vojtěch Bartička
        public AnnotationServiceEF(DatabaseContext context, ILogger logger, IMapper mapper)
30 eff8ec56 Vojtěch Bartička
        {
31
            this.context = context;
32
            this.logger = logger;
33 a6675a6d Vojtěch Bartička
            this.mapper = mapper;
34 eff8ec56 Vojtěch Bartička
        }
35
36
        public void CreateDocumentAnnotations(AnnotationsAddRequest request, Guid clientUserId)
37
        {
38
            User addingUser = context.Users.Single(u => u.Id == clientUserId);
39
40
            // Check the documents exist
41 153d77a8 Vojtěch Bartička
            var documents = context.Documents.Where(d => request.DocumentIdList.Contains(d.Id)).ToList();
42
            if (documents.Count() != request.DocumentIdList.Count)
43 eff8ec56 Vojtěch Bartička
            {
44 153d77a8 Vojtěch Bartička
                logger.Information($"Received a non-existent Document ID when assigning documents to users");
45
                throw new InvalidOperationException($"{request.DocumentIdList.Count - documents.Count()} of the received documents do not exist");
46 eff8ec56 Vojtěch Bartička
            }
47
48 6bdb3d95 Vojtěch Bartička
            var users = context.Users.Where(u => request.UserIdList.Contains(u.Id)).ToList();
49 153d77a8 Vojtěch Bartička
            foreach (var user in users)
50 eff8ec56 Vojtěch Bartička
            {
51 153d77a8 Vojtěch Bartička
                var userAnnotatedDocuments = context.Annotations.Where(a => a.User == user).Select(a => a.Document).ToList();
52
                foreach (var doc in documents)
53 eff8ec56 Vojtěch Bartička
                {
54 153d77a8 Vojtěch Bartička
                    if (userAnnotatedDocuments.Contains(doc))
55 eff8ec56 Vojtěch Bartička
                    {
56 153d77a8 Vojtěch Bartička
                        logger.Information($"User {user.Username} has already been assigned the document {doc.Id}, ignoring");
57
                        continue;
58 eff8ec56 Vojtěch Bartička
                    }
59
60
                    context.Annotations.Add(new Annotation()
61
                    {
62
                        User = user,
63
                        UserAssigned = addingUser,
64
                        DateAssigned = DateTime.Now,
65
                        DateLastChanged = DateTime.Now,
66 153d77a8 Vojtěch Bartička
                        Document = doc,
67 eff8ec56 Vojtěch Bartička
                        State = EState.NEW,
68
                        Note = ""
69
                    });
70
                }
71
            }
72
73
            context.SaveChanges();
74
        }
75 6bdb3d95 Vojtěch Bartička
76
        public AnnotationListResponse GetUserAnnotations(Guid userId)
77
        {
78
            var annotations = context.Annotations.Where(a => a.User.Id == userId).Include(a => a.Document).ToList();
79
            var documentIds = annotations.Select(a => a.Document.Id).ToList();
80
            var documents = context.Documents.Where(d => documentIds.Contains(d.Id));
81
            var infos = new List<AnnotationListInfo>();
82
83
            var annotationsDocuments = annotations.Zip(documents, (a, d) => new { Annotation = a, Document = d });
84
            foreach (var ad in annotationsDocuments)
85
            {
86
                infos.Add(new AnnotationListInfo()
87
                {
88
                    AnnotationId = ad.Annotation.Id,
89
                    DocumentName = ad.Document.Name,
90
                    State = ad.Annotation.State
91
                });
92
            }
93
94
            return new AnnotationListResponse()
95
            {
96
                Annotations = infos
97
            };
98
        }
99 a6675a6d Vojtěch Bartička
100
        public AnnotationInfo GetAnnotation(Guid annotationId, Guid userId, ERole userRole)
101
        {
102 c2a89232 Vojtěch Bartička
            var annotation = context.Annotations
103
                .Where(a => a.Id == annotationId)
104
                .Include(a => a.User)
105
                .Include(a => a.Document).ThenInclude(d => d.Content)
106
                .First();
107 3c185841 Vojtěch Bartička
108 a6675a6d Vojtěch Bartička
            if (userRole < ERole.ADMINISTRATOR)
109
            {
110
                if (annotation.User.Id != userId)
111
                {
112
                    throw new UnauthorizedAccessException($"User {userId} does not have assigned annotation {annotationId}");
113
                }
114
            }
115
116 c2a89232 Vojtěch Bartička
            var documentContent = context.Documents.Where(d => d.Id == annotation.Document.Id).Select(d => d.Content).First();
117 a6675a6d Vojtěch Bartička
118 c2a89232 Vojtěch Bartička
            var tags = context.AnnotationTags.Where(at => at.Annotation.Id == annotationId)
119
                .Include(at => at.Tag).ThenInclude(t => t.Category)
120
                .Include(at => at.SubTag)
121
                .ToList();
122
123 0a9f9349 Vojtěch Bartička
            List<TagInstanceInfo> tagInstanceInfos = new();
124 a6675a6d Vojtěch Bartička
            foreach (var tag in tags)
125
            {
126
                var tagInstance = mapper.Map<TagInstanceInfo>(tag);
127 0a9f9349 Vojtěch Bartička
                tagInstanceInfos.Add(tagInstance);
128 a6675a6d Vojtěch Bartička
            }
129
130 0a9f9349 Vojtěch Bartička
            var docToRender = PreprocessHTML(documentContent.Content, tags);
131
132
            // We probably cannot use AutoMapper since we are dealing with too many different entities
133
            AnnotationInfo annotationInfo = new()
134
            {
135
                SourceDocumentContent = documentContent.Content,
136
                DocumentToRender = docToRender,
137
                TagStartPositions = TagStartPositions.ToArray(),
138
                TagLengths = TagStartLengths.ToArray(),
139
                Note = annotation.Note,
140
                State = annotation.State,
141
                Type = IsHtml(documentContent.Content) ? EDocumentType.HTML : EDocumentType.TEXT,
142
                TagInstances = tagInstanceInfos
143
            };
144
145 a6675a6d Vojtěch Bartička
            return annotationInfo;
146
        }
147
148 0a9f9349 Vojtěch Bartička
        private List<int> TagStartPositions = new();
149
        private List<int> TagStartLengths = new();
150
        private List<int> TagClosingPositions = new();
151
        private List<int> TagClosingLengths = new();
152
        private Dictionary<HtmlNode, HtmlNode> NodeDict = new();
153
154
        private string PreprocessHTML(string htmlSource, List<AnnotationTag> tags)
155 3c185841 Vojtěch Bartička
        {
156
            var docOriginal = new HtmlDocument();
157
            docOriginal.LoadHtml(htmlSource);
158
            var docToEdit = new HtmlDocument();
159
            docToEdit.LoadHtml(htmlSource);
160
161
            var descendantsOriginal = docOriginal.DocumentNode.DescendantsAndSelf();
162
            var descendantsToEdit = docToEdit.DocumentNode.DescendantsAndSelf();
163
164 0a9f9349 Vojtěch Bartička
            int currentId = 0;
165 3c185841 Vojtěch Bartička
166 0a9f9349 Vojtěch Bartička
            FillNodeDict(descendantsOriginal, descendantsToEdit);
167
            AssignIdsToOriginalDocument(descendantsOriginal, ref currentId);
168 3c185841 Vojtěch Bartička
169 0a9f9349 Vojtěch Bartička
            WrapTextInSpan(descendantsOriginal, docToEdit);
170 3c185841 Vojtěch Bartička
171 0a9f9349 Vojtěch Bartička
            foreach (var tag in tags)
172 3c185841 Vojtěch Bartička
            {
173 0a9f9349 Vojtěch Bartička
                int i = 0;
174
                List<HtmlNode> addedForSelection = new();
175 c9762683 Vojtěch Bartička
                while (i < descendantsToEdit.Count())
176 3c185841 Vojtěch Bartička
                {
177 c9762683 Vojtěch Bartička
                    for (; i < descendantsToEdit.Count(); i++)
178 0a9f9349 Vojtěch Bartička
                    {
179
                        var node = descendantsToEdit.ElementAt(i);
180
                        if (!node.Name.Contains("#text") || addedForSelection.Contains(node) || addedForSelection.Contains(node.ParentNode) ||
181
                            node.ParentNode.Name == "style")
182
                        {
183
                            continue;
184
                        }
185
186 c9762683 Vojtěch Bartička
                        int nodeId = node.ParentNode.GetAttributeValue(TAG_ID_ATTRIBUTE_NAME, -1);
187
188
                        var start = TagStartPositions[nodeId] + TagStartLengths[nodeId];
189
                        var end = TagClosingPositions[nodeId];
190 0a9f9349 Vojtěch Bartička
191
                        int selectionStart = tag.Position;
192
                        int selectionEnd = tag.Position + tag.Length;
193
194
                        if (selectionStart < end && selectionEnd > start)
195
                        {
196
                            if (selectionStart <= start && selectionEnd >= end)
197
                            {
198
                                addedForSelection.Add(SolveFullFill(node, selectionStart, selectionEnd, start, end, docToEdit, tag));
199
                            }
200
                            else if (selectionStart <= start)
201
                            {
202
                                addedForSelection.AddRange(SolveRightGap(node, selectionStart, selectionEnd, start, end, docToEdit, tag));
203
                            }
204
                            else if (selectionEnd >= end)
205
                            {
206
                                addedForSelection.AddRange(SolveLeftGap(node, selectionStart, selectionEnd, start, end, docToEdit, tag));
207
                            }
208
                            else
209
                            {
210
                                addedForSelection.AddRange(SolveLeftRightGap(node, selectionStart, selectionEnd, start, end, docToEdit, tag));
211
                            }
212
                            break;
213
                        }
214
                    }
215 3c185841 Vojtěch Bartička
                }
216
217
            }
218
219
            string docToRender = docToEdit.DocumentNode.OuterHtml;
220
            HtmlSanitizer sanitizer = new HtmlSanitizer();
221
            sanitizer.AllowedAttributes.Clear();
222 0a9f9349 Vojtěch Bartička
            sanitizer.AllowedAttributes.Add(TAG_ID_ATTRIBUTE_NAME);
223
            sanitizer.AllowedAttributes.Add(TAG_INSTANCE_ATTRIBUTE_NAME);
224 c9762683 Vojtěch Bartička
            sanitizer.AllowedAttributes.Add("end");
225
            sanitizer.AllowedAttributes.Add("start");
226
            sanitizer.AllowedAttributes.Add("class");
227 be4deff8 Vojtěch Bartička
            if (sanitizer.AllowedTags.Contains("script"))
228
            {
229
                sanitizer.AllowedTags.Remove("script");
230 c9762683 Vojtěch Bartička
            }   
231
            if (!sanitizer.AllowedTags.Contains("style"))
232
            {
233
                sanitizer.AllowedTags.Add("style");
234 be4deff8 Vojtěch Bartička
            }
235 3c185841 Vojtěch Bartička
            docToRender = sanitizer.Sanitize(docToRender);
236
237 c9762683 Vojtěch Bartička
            HtmlDocument doc = new HtmlDocument();
238
            doc.LoadHtml(docToRender);
239
            var cssNode = GenerateCSS(doc, tags, new());
240
            doc.DocumentNode.ChildNodes.Insert(0, cssNode);
241
242
            return doc.DocumentNode.OuterHtml;
243 0a9f9349 Vojtěch Bartička
        }
244
245
        private HtmlNode SolveFullFill(HtmlNode node, int selectionStart, int selectionEnd, int start, int end, HtmlDocument docToEdit, AnnotationTag tag)
246
        {
247
            // full fill
248
            string textSelected = node.InnerText;
249
250
            var parentNode = node.ParentNode;
251
            int nodeIndex = parentNode.ChildNodes.IndexOf(node);
252
            parentNode.ChildNodes.RemoveAt(nodeIndex);
253
254
            EPosition markerPosition = EPosition.MARK_NONE;
255
            if (selectionEnd == end && selectionStart == start)
256
            {
257
                markerPosition = EPosition.MARK_LEFT_RIGHT;
258
            }
259
260
            HtmlNode spanSelected = CreateSpan(docToEdit, textSelected, TAG_ID_ATTRIBUTE_NAME, TagStartPositions.Count,
261
                TAG_INSTANCE_ATTRIBUTE_NAME, tag.Instance, start, markerPosition);
262
            parentNode.ChildNodes.Insert(nodeIndex, spanSelected);
263
264
            return spanSelected;
265
        }
266
267
        private List<HtmlNode> SolveRightGap(HtmlNode node, int selectionStart, int selectionEnd, int start, int end, HtmlDocument docToEdit,
268
                                             AnnotationTag tag)
269
        {
270
            // partial fill, end gap
271
            string text = node.InnerText;
272
            string textAfter = text.Substring(Math.Min(selectionStart - start + tag.Length, text.Length));
273
            string textSelected = text.Substring(0, selectionEnd - start);
274
275
            var parentNode = node.ParentNode;
276
            int nodeIndex = parentNode.ChildNodes.IndexOf(node);
277
            parentNode.ChildNodes.RemoveAt(nodeIndex);
278
279
            int spanSelectedStart = start;
280
            int spanAfterStart = start + textSelected.Length;
281
282
            HtmlNode spanSelected = CreateSpan(docToEdit, textSelected, TAG_ID_ATTRIBUTE_NAME, TagStartPositions.Count,
283
                TAG_INSTANCE_ATTRIBUTE_NAME, tag.Instance, spanSelectedStart, EPosition.MARK_RIGHT);
284
            parentNode.ChildNodes.Insert(nodeIndex, spanSelected);
285
286
            HtmlNode spanAfter = CreateSpan(docToEdit, textAfter, TAG_ID_ATTRIBUTE_NAME, TagStartPositions.Count,
287
                TAG_INSTANCE_ATTRIBUTE_NAME, null, spanAfterStart);
288
            parentNode.ChildNodes.Insert(nodeIndex + 1, spanAfter);
289
290
            return new() { spanSelected, spanAfter };
291
        }
292
293
        private List<HtmlNode> SolveLeftGap(HtmlNode node, int selectionStart, int selectionEnd, int start, int end, HtmlDocument docToEdit,
294
                                             AnnotationTag tag)
295
        {
296
            // partial fill, start gap
297
            string text = node.InnerText;
298
            string textBefore = text.Substring(0, selectionStart - start);
299
            string textSelected = text.Substring(selectionStart - start, Math.Min(tag.Length, text.Length - textBefore.Length));
300
301
            var parentNode = node.ParentNode;
302
            int nodeIndex = parentNode.ChildNodes.IndexOf(node);
303
            parentNode.ChildNodes.RemoveAt(nodeIndex);
304
305
            int spanBeforeStart = start;
306
            int spanSelectedStart = start + textBefore.Length;
307
308
            HtmlNode spanBefore = CreateSpan(docToEdit, textBefore, TAG_ID_ATTRIBUTE_NAME, TagStartPositions.Count,
309
                TAG_INSTANCE_ATTRIBUTE_NAME, null, spanBeforeStart);
310
            parentNode.ChildNodes.Insert(nodeIndex, spanBefore);
311
312
            HtmlNode spanSelected = CreateSpan(docToEdit, textSelected, TAG_ID_ATTRIBUTE_NAME, TagStartPositions.Count,
313
                TAG_INSTANCE_ATTRIBUTE_NAME, tag.Instance, spanSelectedStart, EPosition.MARK_LEFT);
314
            parentNode.ChildNodes.Insert(nodeIndex + 1, spanSelected);
315
316
            return new() { spanSelected, spanBefore };
317
        }
318
319
        private List<HtmlNode> SolveLeftRightGap(HtmlNode node, int selectionStart, int selectionEnd, int start, int end, HtmlDocument docToEdit,
320
                                                 AnnotationTag tag)
321
        {
322
            // partial fill, start gap end gap
323
            string text = node.InnerText;
324
            string textBefore = text.Substring(0, selectionStart - start);
325
            string textAfter = text.Substring(selectionStart - start + tag.Length);
326
            string textSelected = text.Substring(selectionStart - start, tag.Length);
327
328
            var parentNode = node.ParentNode;
329
            int nodeIndex = parentNode.ChildNodes.IndexOf(node);
330
            parentNode.ChildNodes.RemoveAt(nodeIndex);
331
332
            int spanBeforeStart = start;
333
            int spanSelectedStart = start + textBefore.Length;
334
            int spanAfterStart = start + textBefore.Length + textSelected.Length;
335
336
            HtmlNode spanBefore = CreateSpan(docToEdit, textBefore, TAG_ID_ATTRIBUTE_NAME, TagStartPositions.Count,
337
                TAG_INSTANCE_ATTRIBUTE_NAME, null, spanBeforeStart);
338
            parentNode.ChildNodes.Insert(nodeIndex, spanBefore);
339
340
            HtmlNode spanSelected = CreateSpan(docToEdit, textSelected, TAG_ID_ATTRIBUTE_NAME, TagStartPositions.Count,
341
                TAG_INSTANCE_ATTRIBUTE_NAME, tag.Instance, spanSelectedStart, EPosition.MARK_LEFT_RIGHT);
342
            parentNode.ChildNodes.Insert(nodeIndex + 1, spanSelected);
343
344
            HtmlNode spanAfter = CreateSpan(docToEdit, textAfter, TAG_ID_ATTRIBUTE_NAME, TagStartPositions.Count,
345
                TAG_INSTANCE_ATTRIBUTE_NAME, null, spanAfterStart);
346
            parentNode.ChildNodes.Insert(nodeIndex + 2, spanAfter);
347
348
            return new() { spanSelected, spanBefore, spanAfter };
349
        }
350
351
        private HtmlNode CreateSpan(HtmlDocument doc, string text, string tagIdAttributeName, int tagId, string tagInstanceAttributeName,
352
                                            Guid? instanceId, int startPosition, EPosition position = EPosition.MARK_NONE)
353
        {
354
            HtmlNode span = doc.CreateElement("span");
355
            span.InnerHtml = text;
356
            TagStartPositions.Add(startPosition);
357
            TagStartLengths.Add(0);
358
            TagClosingPositions.Add(startPosition + text.Length);
359
            TagClosingLengths.Add(0);
360
            span.Attributes.Add(tagIdAttributeName, tagId.ToString());
361
362
            if (instanceId != null)
363
            {
364
                span.AddClass("annotation");
365
                span.Attributes.Add(tagInstanceAttributeName, instanceId.Value.ToString());
366
367
                if (position == EPosition.MARK_LEFT || position == EPosition.MARK_LEFT_RIGHT)
368
                {
369
                    span.Attributes.Add("start", "1");
370
                }
371
                if (position == EPosition.MARK_RIGHT || position == EPosition.MARK_LEFT_RIGHT)
372
                {
373
                    span.Attributes.Add("end", "1");
374
                }
375
            }
376
377
            return span;
378
        }
379
380
        private enum EPosition
381
        {
382
            MARK_LEFT = 5,
383
            MARK_RIGHT = 3,
384
            MARK_LEFT_RIGHT = 2,
385
            MARK_NONE = 0
386
        }
387
388
        private void WrapTextInSpan(IEnumerable<HtmlNode> descendantsOriginal, HtmlDocument docToEdit)
389
        {
390
            foreach (var node in descendantsOriginal)
391
            {
392
                var originalNode = node;
393
                var toEditNode = NodeDict[node];
394
395
                if (originalNode.Name.Contains("#"))
396
                {
397
                    continue;
398
                }
399
                else
400
                {
401
                    bool onlyText = true;
402
                    bool onlySubtags = true;
403
404
                    foreach (var child in node.ChildNodes)
405
                    {
406
                        if (child.Name.Contains("#"))
407
                        {
408
                            onlySubtags = false;
409
                        }
410
                        else
411
                        {
412
                            onlyText = false;
413
                        }
414
                    }
415
416
                    if (onlyText || onlySubtags)
417
                    {
418
                        continue;
419
                    }
420
                    else
421
                    {
422
423
                        foreach (var child in node.ChildNodes)
424
                        {
425
                            if (child.Name.Contains("#text"))
426
                            {
427
                                HtmlNode coveringSpan = docToEdit.CreateElement("span");
428
                                coveringSpan.InnerHtml = child.InnerHtml;
429
                                TagStartPositions.Add(child.InnerStartIndex);
430
                                TagStartLengths.Add(0);
431
                                TagClosingPositions.Add(child.InnerStartIndex + child.InnerLength);
432
                                TagClosingLengths.Add(0);
433
                                coveringSpan.Attributes.Add(TAG_ID_ATTRIBUTE_NAME, (TagStartPositions.Count - 1).ToString());
434
435
                                var parent = NodeDict[node];
436
                                var index = parent.ChildNodes.IndexOf(NodeDict[child]);
437
438
                                parent.ChildNodes.RemoveAt(index);
439
                                parent.ChildNodes.Insert(index, coveringSpan);
440
                            }
441
                        }
442
                    }
443
                }
444
            }
445
        }
446
447
        private HtmlNode GenerateCSS(HtmlDocument docToEdit, List<AnnotationTag> tags, List<int> paddings)
448
        {
449
            HtmlNode style = docToEdit.CreateElement("style");
450
451
            string inner = "span.annotation {border-bottom: 2px solid;}";
452
            inner += "span {line-height: 30px}\n";
453
454
            // TODO temporary
455 c9762683 Vojtěch Bartička
            int lastPadding = 0;
456 0a9f9349 Vojtěch Bartička
            foreach (var tag in tags)
457
            {
458 c9762683 Vojtěch Bartička
                inner += $"span[{TAG_INSTANCE_ATTRIBUTE_NAME}=\"{tag.Instance}\"] {{ border-color:{tag.Tag.Color}; padding-bottom: {lastPadding % 5}px }}";
459 0a9f9349 Vojtěch Bartička
                lastPadding += 2;
460
            }
461
462
            inner += "span[end=\"1\"] {border-end-end-radius: 0px; border-right: 1px solid darkgray}\n";
463
            inner += "span[start=\"1\"] {border-end-start-radius: 0px; border-left: 1px solid darkgray}\n";
464
465
            style.InnerHtml = inner;
466
            return style;
467
        }
468
469
        private void AssignIdsToOriginalDocument(IEnumerable<HtmlNode> descendantsOriginal, ref int currentId)
470
        {
471
            foreach (var node in descendantsOriginal)
472
            {
473
                var originalNode = node;
474
                var toEditNode = NodeDict[node];
475
476
                if (originalNode.Name.Contains("#"))
477
                {
478
                    continue;
479
                }
480
                else
481
                {
482
                    TagStartPositions.Add(originalNode.OuterStartIndex);
483
                    TagStartLengths.Add(originalNode.InnerStartIndex - originalNode.OuterStartIndex);
484
                    currentId = TagStartPositions.Count - 1;
485
                    toEditNode.Attributes.Add(TAG_ID_ATTRIBUTE_NAME, currentId.ToString());
486
487
                    TagClosingPositions.Add(originalNode.InnerStartIndex + originalNode.InnerLength);
488
                    TagClosingLengths.Add((originalNode.OuterStartIndex + originalNode.OuterLength) - (originalNode.InnerStartIndex + originalNode.InnerLength));
489
                }
490
            }
491
        }
492
493
        private void FillNodeDict(IEnumerable<HtmlNode> descendantsOriginal, IEnumerable<HtmlNode> descendantsToEdit)
494
        {
495
            var zipped = descendantsOriginal.Zip(descendantsToEdit, (orig, toEdit) => new
496
            {
497
                Original = orig,
498
                ToEdit = toEdit
499
            });
500
            foreach (var node in zipped)
501
            {
502
                var originalNode = node.Original;
503
                var toEditNode = node.ToEdit;
504
                NodeDict.Add(originalNode, toEditNode);
505
            }
506 3c185841 Vojtěch Bartička
        }
507
508 a6675a6d Vojtěch Bartička
        // TODO temporary
509
        private bool IsHtml(string text)
510
        {
511 0a9f9349 Vojtěch Bartička
            return text.Contains("<html>");
512 a6675a6d Vojtěch Bartička
        }
513 be4deff8 Vojtěch Bartička
514
        public void AddAnnotationInstance(Guid annotationId, Guid userId, ERole userRole, AnnotationInstanceAddRequest request)
515
        {
516
            var annotation = context.Annotations
517
               .Where(a => a.Id == annotationId)
518
               .Include(a => a.User)
519
               .Include(a => a.Document).ThenInclude(d => d.Content)
520
               .First();
521
522
            if (userRole < ERole.ADMINISTRATOR)
523
            {
524
                if (annotation.User.Id != userId)
525
                {
526
                    throw new UnauthorizedAccessException($"User {userId} does not have assigned annotation {annotationId}");
527
                }
528
            }
529
530
            AnnotationTag annotationTag = new()
531
            {
532
                Annotation = annotation,
533
                Instance = request.InstanceId == null ? Guid.NewGuid() : request.InstanceId.Value,
534
                Length = request.Length,
535
                Position = request.Position,
536
                Note = ""
537
            };
538
539
            if (request.Type == ETagType.TAG)
540
            {
541
                annotationTag.Tag = context.Tags.Where(t => t.Id == request.Id).Single();
542
                annotationTag.SubTag = null;
543
            }
544
            else if (request.Type == ETagType.SUBTAG)
545
            {
546
                var subTag = context.SubTags.Where(st => st.Id == request.Id).Include(st => st.Tag).Single();
547
                annotationTag.SubTag = subTag;
548
                annotationTag.Tag = subTag.Tag;
549
            }
550
            else
551
            {
552
                throw new ArgumentException($"Unknown tag type {request.Type}");
553
            }
554
555 abf94f21 Lukáš Vlček
            context.AnnotationTags.Add(annotationTag);
556 be4deff8 Vojtěch Bartička
            context.SaveChanges();
557
        }
558 0f8d6304 Vojtěch Bartička
559
        public void DeleteAnnotationInstance(Guid annotationId, Guid tagInstanceId, Guid loggedUserId, ERole userRole)
560
        {
561
            Annotation annotation = null;
562
            try
563
            {
564
                annotation = context.Annotations
565
                   .Where(a => a.Id == annotationId)
566
                   .Include(a => a.User)
567
                   .Include(a => a.Document).ThenInclude(d => d.Content)
568
                   .First();
569
570
            }
571
            catch (Exception ex)
572
            {
573
                throw new InvalidOperationException("Could not find annotation");
574
            }
575
576
577
            if (userRole < ERole.ADMINISTRATOR)
578
            {
579
                if (annotation.User.Id != loggedUserId)
580
                {
581
                    throw new UnauthorizedAccessException($"User {loggedUserId} does not have assigned annotation {annotationId}");
582
                }
583
            }
584
585
            if (!context.AnnotationTags.Any(at => at.Id == tagInstanceId))
586
            {
587
                throw new InvalidOperationException("Could not find tag instance");
588
            }
589
590
            context.AnnotationTags
591
                .Where(at => at.Id == tagInstanceId).ToList()
592
                .ForEach(a => context.AnnotationTags.Remove(a));
593 0a9f9349 Vojtěch Bartička
594 0f8d6304 Vojtěch Bartička
            context.SaveChanges();
595
        }
596 eff8ec56 Vojtěch Bartička
    }
597
}