public void Can_create_BE_from_XDoc() {
            var doc = new XDoc("test").Elem("foo", StringUtil.CreateAlphaNumericKey(6));
            var be = new ResourceContentBE(doc);

            var doc2 = XDocFactory.From(be.ToStream(), be.MimeType);
            Assert.AreEqual(doc,doc2);
        }
 public void Can_create_BE_from_Stream() {
     var stream = new MemoryStream();
     var bytes = Encoding.UTF8.GetBytes("foo");
     stream.Write(bytes, 0, bytes.Length);
     stream.Position = 0;
     var be = new ResourceContentBE(stream,MimeType.TEXT_UTF8);
     Assert.AreEqual(MimeType.TEXT_UTF8,be.MimeType);
     Assert.AreEqual(stream.Length, be.Size);
     Assert.AreEqual(stream.Length, be.ToBytes().Length);
 }
Example #3
0
        public ResourceBE[] SaveBatchProperties(uint? parentId, XUri parentUri, ResourceBE.ParentType parentType, XDoc doc, out string[] failedNames, out Dictionary<string, Exception> saveStatusByName) {

            //This is a specialized method that saves a batch of property updates in one request and connects them with a transaction.
            //Successful updates are returned
            //Status/description of each property update is returned as well as a hash of dreammessages by name

            saveStatusByName = new Dictionary<string, Exception>();
            List<string> failedNamesList = new List<string>();
            Dictionary<string, ResourceBE> resourcesByName = new Dictionary<string, ResourceBE>();
            List<ResourceBE> ret = new List<ResourceBE>();

            //Get list of names and perform dupe check
            foreach(XDoc propDoc in doc["/properties/property"]) {
                string name = propDoc["@name"].AsText ?? string.Empty;
                if(resourcesByName.ContainsKey(name)) {
                    throw new PropertyDuplicateInvalidOperationException(name);
                }

                resourcesByName[name] = null;
            }

            //Retrieve current properties with given name
            resourcesByName = _resourceBL.GetResources(parentId, parentType, ResourceBL.PROPERTIES, new List<string>(resourcesByName.Keys).ToArray(), DeletionFilter.ACTIVEONLY).AsHash(e => e.Name);

            //extract property info, build resource revisions, save resource, and maintain statuses for each save
            foreach(XDoc propDoc in doc["/properties/property"]) {
                ResourceBE res = null;
                string content;
                uint contentLength = 0;
                string description = string.Empty;
                string etag = null;
                MimeType mimeType;
                string name = string.Empty;

                try {
                    name = propDoc["@name"].AsText ?? string.Empty;
                    resourcesByName.TryGetValue(name, out res);
                    if(propDoc["contents"].IsEmpty) {
                        if(res == null) {
                            throw new PropertyDeleteDoesNotExistInvalidArgumentException(name);
                        } else {
                            res = DeleteProperty(res, parentType, parentUri);
                        }
                    } else {

                        //parse content from xml
                        etag = propDoc["@etag"].AsText;
                        description = propDoc["description"].AsText;
                        content = propDoc["contents"].Contents;
                        contentLength = (uint)(content ?? string.Empty).Length;
                        string mimeTypeStr = propDoc["contents/@type"].AsText;
                        if(string.IsNullOrEmpty(mimeTypeStr) || !MimeType.TryParse(mimeTypeStr, out mimeType)) {
                            throw new PropertyMimtypeInvalidArgumentException(name, mimeTypeStr);
                        }
                        ResourceContentBE resourceContent = new ResourceContentBE(content, mimeType);

                        if(res == null) {

                            //new property
                            res = CreateProperty(parentId, parentUri, parentType, name, resourceContent, description, etag, AbortEnum.Exists);
                        } else {

                            //new revision
                            res = UpdatePropertyContent(res, resourceContent, description, etag, AbortEnum.Modified, parentUri, parentType);
                        }
                    }

                    ret.Add(res);
                    saveStatusByName[name] = null;
                } catch(ResourcedMindTouchException x) {

                    //Unexpected errors fall through while business logic errors while saving a property continues processing
                    saveStatusByName[name] = x;
                    failedNamesList.Add(name);
                } catch(DreamAbortException x) {

                    // TODO (arnec): remove this once all usage of DreamExceptions is purged from Deki logic

                    //Unexpected errors fall through while business logic errors while saving a property continues processing
                    saveStatusByName[name] = x;
                    failedNamesList.Add(name);
                }
            }

            failedNames = failedNamesList.ToArray();
            return ret.ToArray();
        }
Example #4
0
 public virtual ResourceBE BuildRevForContentUpdate(ResourceBE currentResource, MimeType mimeType, uint size, string description, string name, ResourceContentBE newContent) {
     ResourceBE newRev = BuildRevForExistingResource(currentResource, mimeType, size, description);
     newRev.Content = newContent;
     newRev.ContentId = 0;
     newRev.ChangeMask |= ResourceBE.ChangeOperations.CONTENT;
     if(name != null && !StringUtil.EqualsInvariant(name, newRev.Name)) {
         newRev.ChangeMask |= ResourceBE.ChangeOperations.NAME;
         newRev.Name = name;
     }
     return newRev;
 }
Example #5
0
 private ResourceBE BuildRevForNewResource(string resourcename, MimeType mimeType, uint size, string description, ResourceBE.Type resourceType, uint userId, ResourceContentBE content) {
     ResourceBE newResource = new ResourceBE(resourceType);
     newResource.Name = resourcename;
     newResource.ChangeMask = newResource.ChangeMask | ResourceBE.ChangeOperations.NAME;
     newResource.Size = size;
     newResource.MimeType = mimeType;
     newResource.ChangeDescription = description;
     newResource.ResourceCreateUserId = newResource.ResourceUpdateUserId = newResource.UserId = userId;
     newResource.ChangeSetId = 0;
     newResource.Timestamp = newResource.ResourceCreateTimestamp = newResource.ResourceUpdateTimestamp = DateTime.UtcNow;
     newResource.Content = content;
     newResource.ChangeMask = newResource.ChangeMask | ResourceBE.ChangeOperations.CONTENT;
     newResource.ResourceHeadRevision = ResourceBE.TAILREVISION;
     newResource.Revision = ResourceBE.TAILREVISION;
     newResource.IsHidden = false;
     return newResource;
 }
Example #6
0
 public virtual ResourceBE BuildRevForNewResource(uint? parentId, ResourceBE.ParentType parentType, string resourcename, MimeType mimeType, uint size, string description, ResourceBE.Type resourceType, uint userId, ResourceContentBE content) {
     ResourceBE newResource = BuildRevForNewResource(resourcename, mimeType, size, description, resourceType, userId, content);
     switch(parentType) {
     case ResourceBE.ParentType.PAGE:
         newResource.ParentPageId = parentId;
         break;
     case ResourceBE.ParentType.USER:
         newResource.ParentUserId = parentId;
         break;
     default:
         newResource.ParentId = parentId;
         break;
     }
     newResource.ChangeMask = newResource.ChangeMask | ResourceBE.ChangeOperations.PARENT;
     return newResource;
 }
Example #7
0
 protected ResourceBE BuildRevForContentUpdate(ResourceBE currentResource, MimeType mimeType, uint size, string description, string name, ResourceContentBE newContent) {
     ResourceBE newRev = _resourceBL.BuildRevForContentUpdate(currentResource, mimeType, size, description, name, newContent);
     newRev.MetaXml.FileId = currentResource.MetaXml.FileId;
     return newRev;
 }
Example #8
0
        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;
        }
 // -- Constructors --
 protected ResourceBE() {
     _content = new ResourceContentBE(true);
 }
 public void Can_roundtrip_text_through_BE() {
     var text = StringUtil.CreateAlphaNumericKey(20);
     var be = new ResourceContentBE(text, MimeType.TEXT_UTF8);
     Assert.AreEqual(text,be.ToText());
 }
 public void BE_hashes_stream() {
     var value = StringUtil.CreateAlphaNumericKey(20);
     var be = new ResourceContentBE(value,MimeType.TEXT_UTF8);
     Assert.AreEqual(StringUtil.ComputeHashString(value,Encoding.UTF8), be.ComputeHashString());
 }
 public void Can_write_bytes_to_blank_BE() {
     var be = new ResourceContentBE(true);
     var v = 42;
     be.SetData(BitConverter.GetBytes(v));
     Assert.AreEqual(v, BitConverter.ToInt32(be.ToBytes(), 0));
 }
Example #13
0
 //--- Constructors ---
 public ResourceBE(Type type) {
     ResourceType = type;
     Content = new ResourceContentBE(true);
 }
Example #14
0
 //--- Constructors ---
 public ResourceBE(Type type)
 {
     ResourceType = type;
     Content      = new ResourceContentBE(true);
 }
Example #15
0
 public ResourceBE UpdatePropertyContent(ResourceBE prop, ResourceContentBE content, string changeDescription, string eTag, AbortEnum abort, XUri parentUri, ResourceBE.ParentType parentType) {
     if(abort == AbortEnum.Modified) {
         _resourceBL.ValidateEtag(eTag, prop, true);
     }
     prop = _resourceBL.BuildRevForContentUpdate(prop, content.MimeType, content.Size, changeDescription, null, content);
     prop = _resourceBL.SaveResource(prop);
     DekiContext.Current.Instance.EventSink.PropertyUpdate(DekiContext.Current.Now, prop, DekiContext.Current.User, parentType, parentUri);
     return prop;
 }
Example #16
0
        public Yield PostPageContents(DreamContext context, DreamMessage request, Result<DreamMessage> response) {
            PageBE cur = PageBL_GetPageFromUrl(context, false);

            // load page contents based on mime type
            string contents;
            MimeType mimeType = request.ContentType;
            if(mimeType.IsXml) {
                XDoc contentsDoc = request.ToDocument();
                if(contentsDoc == null || contentsDoc.IsEmpty || !contentsDoc.HasName("content")) {
                    throw new PostedDocumentInvalidArgumentException("content");
                }
                contents = contentsDoc["body"].ToInnerXHtml();
            } else if(MimeType.TEXT.Match(mimeType) || MimeType.FORM_URLENCODED.Match(mimeType)) {
                contents = request.AsText();
            } else {
                throw new UnsupportedContentTypeInvalidArgumentException(mimeType);
            }

            bool isExistingPage = cur.ID != 0 && !cur.IsRedirect;
            string abort = context.GetParam("abort", "never").ToLowerInvariant();
            if(isExistingPage && "exists" == abort) {
                throw new PageExistsConflictException();
            }

            // Retrieve the title used for path normalization (if any)
            Title relToTitle = Utils.GetRelToTitleFromUrl(context);
            string editTimeStr = context.GetParam("edittime", null);
            DateTime editTime = DateTime.MinValue;
            if(!string.IsNullOrEmpty(editTimeStr)) {
                editTime = editTimeStr.EqualsInvariantIgnoreCase("now") ? DateTime.UtcNow : DbUtils.ToDateTime(editTimeStr);
            }
            string comment = context.GetParam("comment", String.Empty);
            string language = context.GetParam("language", null);
            string displayName = context.GetParam("title", null);
            int section = context.GetParam<int>("section", -1);
            if((section < -1) || ((!isExistingPage) && (0 < section))) {
                throw new SectionParamInvalidArgumentException();
            }

            // determin how unsafe/invalid content should be handled
            bool removeIllegalElements = StringUtil.EqualsInvariantIgnoreCase(context.GetParam("tidy", "convert"), "remove");

            // a new revision is created when no changes are detected when overwrite is enabled
            bool overwrite = context.GetParam<bool>("overwrite", false);

            // check whether the page exists and is not a redirect
            DateTime pageLastEditTime = cur.TimeStamp;
            OldBE baseOld = null;
            OldBE overwrittenOld = null;
            if(isExistingPage) {
                PageBL.AuthorizePage(DekiContext.Current.User, Permissions.UPDATE, cur, false);

                // ensure that 'edittime' is set
                if(DateTime.MinValue == editTime) {
                    throw new PageEditTimeInvalidArgumentException();
                }

                // check if page was modified since since the specified time
                if(pageLastEditTime > editTime) {

                    // ensure we're allowed to save a modified page
                    if("modified" == abort) {
                        throw new PageModifiedConflictException();
                    }

                    // if an edit has occurred since the specified edit time, retrieve the revision upon which it is based
                    // NOTE: this can be null if someone created the page after the specified edit time (ie. no common base revision)
                    baseOld = DbUtils.CurrentSession.Old_GetOldByTimestamp(cur.ID, editTime);

                    // if editing a particular section, use the page upon which the section edits were based.
                    if(0 < section && null == baseOld) {
                        throw new PageHeadingInvalidArgumentException();
                    }
                }
            }

            // save page
            bool conflict;
            try {
                overwrittenOld = PageBL.Save(cur, baseOld, comment, contents, DekiMimeType.DEKI_TEXT, displayName, language, section, context.GetParam("xpath", null), DateTime.UtcNow, 0, true, removeIllegalElements, relToTitle, overwrite, out conflict);
            } catch(DekiScriptDocumentTooLargeException e) {
                response.Return(DreamMessage.Forbidden(string.Format(e.Message)));
                yield break;
            }

            // check if this post is part of an import action
            var importTimeStr = context.GetParam("importtime", null);
            if(!string.IsNullOrEmpty(importTimeStr)) {
                var dateModified = DbUtils.ToDateTime(importTimeStr);
                var lastImport = PropertyBL.Instance.GetPageProperty((uint)cur.ID, SiteImportBuilder.LAST_IMPORT);
                var lastImportDoc = new XDoc("last-import").Elem("etag", cur.Etag).Elem("date.modified", dateModified);
                var content = new ResourceContentBE(lastImportDoc);
                if(lastImport == null) {
                    PropertyBL.Instance.CreateProperty((uint)cur.ID, PageBL.GetUri(cur), ResourceBE.ParentType.PAGE, SiteImportBuilder.LAST_IMPORT, content, string.Format("import at revision {0}", cur.Revision), content.ComputeHashString(), AbortEnum.Never);
                } else {
                    PropertyBL.Instance.UpdatePropertyContent(lastImport, content, string.Format("updated import at revision {0}", cur.Revision), content.ComputeHashString(), AbortEnum.Never, PageBL.GetUri(cur), ResourceBE.ParentType.PAGE);
                }
            }

            // generate xml output
            XDoc editXml = new XDoc("edit") { PageBL.GetPageXml(cur, String.Empty) };

            // if a non-redirect was overwritten, report it
            if((overwrittenOld != null) && (pageLastEditTime != editTime) && isExistingPage && conflict) {
                editXml.Attr("status", "conflict");
                editXml.Add(baseOld == null ? new XDoc("page.base") : PageBL.GetOldXml(cur, baseOld, "base"));
                editXml.Add(PageBL.GetOldXml(cur, overwrittenOld, "overwritten"));
            } else {
                editXml.Attr("status", "success");
            }

            response.Return(DreamMessage.Ok(editXml));
            yield break;
        }
Example #17
0
        public ResourceBE CreateProperty(uint? parentId, XUri parentUri, ResourceBE.ParentType parentType, string name, ResourceContentBE content, string description, string etag, AbortEnum abort) {

            //TODO: The parent resource isn't verified when the resource name and the parent info is given
            ResourceBE prop = _resourceBL.GetResource(parentId, parentType, ResourceBE.Type.PROPERTY, name, DeletionFilter.ACTIVEONLY);
            if(prop != null) {
                switch(abort) {
                case AbortEnum.Exists:
                    throw new PropertyExistsConflictException(name);
                case AbortEnum.Modified:
                    _resourceBL.ValidateEtag(etag, prop, true);
                    break;
                }
                prop = _resourceBL.BuildRevForContentUpdate(prop, content.MimeType, content.Size, description, null, content);
                prop = _resourceBL.SaveResource(prop);
                DekiContext.Current.Instance.EventSink.PropertyUpdate(DekiContext.Current.Now, prop, DekiContext.Current.User, parentType, parentUri);
            } else {
                if((abort == AbortEnum.Modified) && !string.IsNullOrEmpty(etag)) {
                    throw new PropertyUnexpectedEtagConflictException();
                }
                prop = _resourceBL.BuildRevForNewResource(parentId, parentType, name, content.MimeType, content.Size, description, ResourceBE.Type.PROPERTY, DekiContext.Current.User.ID, content);
                prop = _resourceBL.SaveResource(prop);
                DekiContext.Current.Instance.EventSink.PropertyCreate(DekiContext.Current.Now, prop, DekiContext.Current.User, parentType, parentUri);
            }
            return prop;
        }
        private static AttachmentBE PopulateFile(IDataRecord dr) {
            
            string name = GetUTF8String(dr, "img_name");
            uint size = DbUtils.Convert.To<uint>(dr["img_size"], 0);
            MimeType mimetype = new MimeType(GetUTF8String(dr, "img_major_mime") +"/" + GetUTF8String(dr, "img_minor_mime"));
            string changedescription = GetUTF8String(dr, "img_description");
            uint userId;
            if (MediaWikiConverterContext.Current.Merge) {
                userId = MediaWikiConverterContext.Current.MergeUserId;
            } else {
                userId = DbUtils.Convert.To<uint>(dr["img_user"], 0);
            }
            DateTime timestamp = DbUtils.ToDateTime(GetUTF8String(dr, "img_timestamp"));

            ResourceContentBE rc = new ResourceContentBE(true);
            rc.Size = size;
            rc.MimeType = mimetype;

            AttachmentBE file = new ResourceBL<AttachmentBE>(ResourceBE.Type.FILE).BuildRevForNewResource(0/*parent page defined later*/, ResourceBE.Type.PAGE, name, mimetype, size, changedescription, ResourceBE.Type.FILE, userId, rc);
            file.MetaXml.Elem("physicalfilename", GetUTF8String(dr, "img_filename"));
            return file;
        }