public ResourceBE[] IdentifyUnknownImages(IEnumerable <ResourceBE> attachments)
        {
            List <ResourceBE> ret = new List <ResourceBE>();

            foreach (ResourceBE file in attachments)
            {
                ResourceBE updatedFile = file;
                if (file != null && IsAllowedForImageMagickPreview(file) && (file.MetaXml.ImageHeight == null || file.MetaXml.ImageWidth == null))
                {
                    StreamInfo fileInfo = _dekiContext.Instance.Storage.GetFile(file, SizeType.ORIGINAL, false);
                    if (file != null)
                    {
                        int width;
                        int height;
                        int frames;
                        if (AttachmentPreviewBL.RetrieveImageDimensions(fileInfo, out width, out height, out frames))
                        {
                            file.MetaXml.ImageWidth  = width;
                            file.MetaXml.ImageHeight = height;

                            // check if we need to store the number of frames (default is 1)
                            if (frames > 1)
                            {
                                file.MetaXml.ImageFrames = frames;
                            }
                            updatedFile = _resourceBL.UpdateResourceRevision(file);
                        }
                    }
                }
                ret.Add(updatedFile);
            }
            return(ret.ToArray());
        }
        private XDoc AppendFileXml(XDoc doc, ResourceBE file, string fileSuffix, bool?explicitRevisionInfo, UserBE updatedByUser, PageBE parentPage)
        {
            bool   requiresEnd = false;
            string fileElement = string.IsNullOrEmpty(fileSuffix) ? "file" : "file." + fileSuffix;

            if (doc == null || doc.IsEmpty)
            {
                doc = new XDoc(fileElement);
            }
            else
            {
                doc.Start(fileElement);
                requiresEnd = true;
            }
            doc.Attr("id", file.MetaXml.FileId ?? 0);
            doc.Attr("revision", file.Revision);
            doc.Attr("res-id", file.ResourceId);
            if (file.IsHidden)
            {
                doc.Attr("hidden", true);
            }
            doc.Attr("href", GetUriInfo(file, explicitRevisionInfo));
            doc.Start("filename").Value(file.Name).End();

            //Description comes from a property
            string description = string.Empty;

            if (!ArrayUtil.IsNullOrEmpty(file.ChildResources))
            {
                ResourceBE descProp = Array.Find(file.ChildResources, p => p != null && p.ResourceType == ResourceBE.Type.PROPERTY && p.Name.EqualsInvariantIgnoreCase(PropertyBL.PROP_DESC));
                if (descProp != null)
                {
                    description = descProp.Content.ToText();
                }
            }
            doc.Start("description").Value(description).End();
            doc.Start("contents")
            .Attr("type", file.MimeType == null ? null : file.MimeType.ToString())
            .Attr("size", file.Size);
            if ((file.MetaXml.ImageHeight ?? 0) > 0 && (file.MetaXml.ImageWidth ?? 0) > 0)
            {
                doc.Attr("width", file.MetaXml.ImageWidth.Value);
                doc.Attr("height", file.MetaXml.ImageHeight.Value);
            }
            doc.Attr("href", GetUriContent(file, explicitRevisionInfo));
            doc.End(); //contents
            if ((file.MetaXml.ImageWidth ?? 0) > 0 && (file.MetaXml.ImageHeight ?? 0) > 0)
            {
                string previewMime = AttachmentPreviewBL.ResolvePreviewMime(file.MimeType).ToString();
                doc.Start("contents.preview")
                .Attr("rel", "thumb")
                .Attr("type", previewMime)
                .Attr("maxwidth", _dekiContext.Instance.ImageThumbPixels)
                .Attr("maxheight", _dekiContext.Instance.ImageThumbPixels)
                .Attr("href", GetUriContent(file, explicitRevisionInfo).With("size", "thumb"));
                if (!file.IsHeadRevision() || (explicitRevisionInfo ?? false))
                {
                    doc.Attr("revision", file.Revision);
                }
                doc.End(); //contents.preview: thumb
                doc.Start("contents.preview")
                .Attr("rel", "webview")
                .Attr("type", previewMime)
                .Attr("maxwidth", _dekiContext.Instance.ImageWebviewPixels)
                .Attr("maxheight", _dekiContext.Instance.ImageWebviewPixels)
                .Attr("href", GetUriContent(file, explicitRevisionInfo).With("size", "webview"));
                if (!file.IsHeadRevision() || (explicitRevisionInfo ?? false))
                {
                    doc.Attr("revision", file.Revision);
                }
                doc.End(); //contents.preview: webview
            }
            doc.Start("date.created").Value(file.Timestamp).End();
            if (updatedByUser != null)
            {
                doc.Add(UserBL.GetUserXml(updatedByUser, "createdby", Utils.ShowPrivateUserInfo(updatedByUser)));
            }
            if (file.ResourceIsDeleted && ((file.ChangeMask & ResourceBE.ChangeOperations.DELETEFLAG) == ResourceBE.ChangeOperations.DELETEFLAG))
            {
                if (updatedByUser != null)
                {
                    doc.Add(UserBL.GetUserXml(updatedByUser, "deletedby", Utils.ShowPrivateUserInfo(updatedByUser)));
                }
                doc.Start("date.deleted").Value(file.Timestamp).End();
            }
            if (file.IsHeadRevision() && !(explicitRevisionInfo ?? false) && !file.ResourceIsDeleted)
            {
                uint filteredCount = _session.Resources_GetRevisionCount(file.ResourceId, DEFAULT_REVISION_FILTER);
                doc.Start("revisions");
                doc.Attr("count", filteredCount);
                doc.Attr("totalcount", file.Revision);
                doc.Attr("href", GetUri(file).At("revisions"));
                doc.End();
            }
            else
            {
                if (file.ChangeMask != ResourceBE.ChangeOperations.UNDEFINED)
                {
                    doc.Start("user-action").Attr("type", file.ChangeMask.ToString().ToLowerInvariant()).End();
                }
            }

            //parent page is passed in for verbose output only
            if (parentPage != null)
            {
                doc.Add(PageBL.GetPageXml(parentPage, "parent"));
            }
            if (file.ChildResources != null)
            {
                List <ResourceBE> properties = new List <ResourceBE>();
                foreach (ResourceBE p in file.ChildResources)
                {
                    properties.Add(p);
                }
                doc = PropertyBL.Instance.GetPropertyXml(properties.ToArray(), GetUri(file), null, null, doc);
            }
            if (file.IsHidden)
            {
                uint?userIdHiddenBy = file.MetaXml.RevisionHiddenUserId;
                if (userIdHiddenBy != null)
                {
                    UserBE userHiddenBy = UserBL.GetUserById(userIdHiddenBy.Value);
                    if (userHiddenBy != null)
                    {
                        doc.Add(UserBL.GetUserXml(userHiddenBy, "hiddenby", Utils.ShowPrivateUserInfo(userHiddenBy)));
                    }
                }
                doc.Elem("date.hidden", file.MetaXml.RevisionHiddenTimestamp ?? DateTime.MinValue);
                doc.Elem("description.hidden", file.MetaXml.RevisionHiddenComment ?? string.Empty);
            }
            if (requiresEnd)
            {
                doc.End(); //file
            }
            return(doc);
        }
        public ResourceBE AddAttachment(ResourceBE existingRevision, Stream filestream, long filesize, MimeType mimeType, PageBE targetPage, string userDescription, string fileName, bool isMsWebDav)
        {
            if (_dekiContext.Instance.MaxFileSize < filesize)
            {
                throw new AttachmentMaxFileSizeAllowedInvalidArgumentException(_dekiContext.Instance.MaxFileSize);
            }
            var saveFileName = ValidateFileName(fileName);

            if (existingRevision != null)
            {
                if (!saveFileName.EqualsInvariant(existingRevision.Name))
                {
                    // An existing file is getting renamed. Make sure no file exists with the new name
                    var existingAttachment = GetPageAttachment(targetPage.ID, saveFileName);
                    if (existingAttachment != null)
                    {
                        throw new AttachmentExistsOnPageConflictException(saveFileName, targetPage.Title.AsUserFriendlyName());
                    }
                }
            }

            // If file is found but has been deleted, create a new file.
            if (existingRevision != null && existingRevision.ResourceIsDeleted)
            {
                existingRevision = null;
            }
            if (isMsWebDav)
            {
                _log.DebugFormat("Upload client is MD WebDAV, provided mimetype is: {0}", mimeType);
                var extensionFileType = MimeType.FromFileExtension(Path.GetExtension(saveFileName));
                if (!extensionFileType.Match(mimeType) || extensionFileType == MimeType.DefaultMimeType)
                {
                    mimeType = existingRevision == null ? extensionFileType : existingRevision.MimeType;
                    _log.DebugFormat("using mimetype '{0}' instead", mimeType);
                }
            }
            ResourceBE attachment;
            var        resourceContents = new ResourceContentBE((uint)filesize, mimeType);
            var        isUpdate         = false;

            if (existingRevision == null)
            {
                attachment = _resourceBL.BuildRevForNewResource((uint)targetPage.ID, ResourceBE.ParentType.PAGE, saveFileName, mimeType, (uint)filesize, null, ResourceBE.Type.FILE, _dekiContext.User.ID, resourceContents);
            }
            else
            {
                isUpdate   = true;
                attachment = BuildRevForContentUpdate(existingRevision, mimeType, (uint)filesize, null, saveFileName, resourceContents);
            }

            // rewrite mimetype to text/plain for certain extensions
            string extension = attachment.FilenameExtension;

            if (_dekiContext.Instance.FileExtensionForceAsTextList.Any(forcedExtensions => extension == forcedExtensions))
            {
                attachment.MimeType = MimeType.TEXT;
            }

            // Insert the attachment into the DB
            attachment = SaveResource(attachment);
            try {
                // Save file to storage provider
                _dekiContext.Instance.Storage.PutFile(attachment, SizeType.ORIGINAL, new StreamInfo(filestream, filesize, mimeType));
            } catch (Exception x) {
                _dekiContext.Instance.Log.WarnExceptionFormat(x, "Failed to save attachment to storage provider");

                // Upon save failure, delete the record from the db.
                _session.Resources_DeleteRevision(attachment.ResourceId, attachment.Revision);
                throw;
            }

            // Set description property
            if (!string.IsNullOrEmpty(userDescription))
            {
                attachment = SetDescription(attachment, userDescription);
            }

            // For images resolve width/height (if not in imagemagick's blacklist)
            attachment = IdentifyUnknownImage(attachment);

            // Pre render thumbnails of images
            AttachmentPreviewBL.PreSaveAllPreviews(attachment);
            PageBL.Touch(targetPage, DateTime.UtcNow);

            //TODO MaxM: Connect with transaction
            RecentChangeBL.AddFileRecentChange(targetPage.Touched, targetPage, _dekiContext.User, DekiResources.FILE_ADDED(attachment.Name), 0);
            if (isUpdate)
            {
                _dekiContext.Instance.EventSink.AttachmentUpdate(_dekiContext.Now, attachment, _dekiContext.User);
            }
            else
            {
                _dekiContext.Instance.EventSink.AttachmentCreate(_dekiContext.Now, attachment, _dekiContext.User);
            }
            return(attachment);
        }