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); }
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(); }
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; }
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; }
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; }
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; }
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)); }
//--- Constructors --- public ResourceBE(Type type) { ResourceType = type; Content = new ResourceContentBE(true); }
//--- Constructors --- public ResourceBE(Type type) { ResourceType = type; Content = new ResourceContentBE(true); }
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; }
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; }
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; }