public ResourceBE GetResource(uint? parentId, ResourceBE.ParentType? parentType, ResourceBE.Type resourceType, string name, DeletionFilter deletionStateFilter) { if(name == null) { throw new ArgumentNullException("name"); } uint[] parentids = parentId == null ? new uint[] { } : new[] { parentId.Value }; return GetResources(parentids, parentType, new[] { resourceType }, new[] { name }, deletionStateFilter, null, null, null).FirstOrDefault(); }
public ResourceBE(ResourceBE sourceRev) : this(sourceRev.ResourceType) { ResourceId = sourceRev.ResourceId; Revision = sourceRev.Revision; ChangeDescription = sourceRev.ChangeDescription; Name = sourceRev.Name; Timestamp = sourceRev.Timestamp; ChangeMask = sourceRev.ChangeMask; UserId = sourceRev.UserId; ChangeSetId = sourceRev.ChangeSetId; Deleted = sourceRev.Deleted; ParentId = sourceRev.ParentId; ParentPageId = sourceRev.ParentPageId; ParentUserId = sourceRev.ParentUserId; ChildResources = sourceRev.ChildResources; // revision contents Size = sourceRev.Size; MimeType = sourceRev.MimeType; Content = sourceRev.Content; ContentId = sourceRev.ContentId; Meta = sourceRev.Meta; // resource info ResourceIsDeleted = sourceRev.ResourceIsDeleted; ResourceUpdateUserId = sourceRev.ResourceUpdateUserId; ResourceUpdateTimestamp = sourceRev.ResourceUpdateTimestamp; ResourceHeadRevision = sourceRev.ResourceHeadRevision; ResourceCreateUserId = sourceRev.ResourceCreateUserId; ResourceCreateTimestamp = sourceRev.ResourceCreateTimestamp; }
//--- Methods --- public bool IsAllowedForImageMagickPreview(ResourceBE file) { // TODO (steveb): recognize mime-types as well if(Array.IndexOf(_dekiContext.Instance.ImageMagickExtensions, file.FilenameExtension.ToLowerInvariant()) == -1) { return false; } return _dekiContext.Instance.MaxImageSize >= file.Size; }
public void DeleteFile(ResourceBE attachment, SizeType size) { if (!attachment.IsHeadRevision()) { throw new StorageNonHeadRevisionDeleteFatalException(); } for (int i = 1; i <= attachment.Content.Revision; i++) { string filename = FilePath(attachment, size); try { File.Delete(filename); } catch { } } }
private static void FSToS3(ResourceBE[] attachmentList) { XDoc s3config = new XDoc("config") .Start("publickey").Value(_public_key).End() .Start("privatekey").Value(_private_key).End() .Start("bucket").Value(_default_bucket).End() .Start("prefix").Value(_prefix).End(); S3Storage s3 = new S3Storage(s3config, LogUtils.CreateLog<S3Storage>()); XDoc fsconfig = new XDoc("config") .Start("path").Value(_attachmentPath).End(); FSStorage fs = new FSStorage(fsconfig); transferFiles(attachmentList, fs, s3); }
//--- Methods --- public StreamInfo GetFile(ResourceBE attachment, SizeType size, bool allowFileLink) { MimeType mime; switch (size) { case SizeType.THUMB: case SizeType.WEBVIEW: mime = AttachmentPreviewBL.ResolvePreviewMime(attachment.MimeType); break; default: mime = attachment.MimeType; break; } return GetFileInternal(FilePath(attachment, size), mime); }
private ResourceBE BuildRevForExistingResource(ResourceBE currentResource, MimeType mimeType, uint size, string description) { ResourceBE newRev = BuildResourceRev(currentResource); newRev.MimeType = mimeType; newRev.Size = size; newRev.ChangeDescription = description; newRev.Content = currentResource.Content; newRev.ContentId = currentResource.ContentId; return newRev; }
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 XDoc GetPropertyXml(ResourceBE property, XUri parentResourceUri, string propSuffix, uint? contentCutoff) { return GetPropertyXml(new ResourceBE[] { property }, parentResourceUri, false, propSuffix, null, contentCutoff, null); }
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(); }
//--- Methods --- #region ResourceDA query wrappers public IList<ResourceBE> GetResources(IList<uint> parentids, ResourceBE.ParentType? parentType, IList<ResourceBE.Type> resourceTypes, IList<string> names, DeletionFilter deletionStateFilter, bool? populateRevisions, uint? limit, uint? offset) { return DbUtils.CurrentSession.Resources_GetByQuery(parentids, parentType, resourceTypes, names, deletionStateFilter, populateRevisions, offset, limit); }
private Dictionary<ulong, List<ResourceBE>> GroupByParentIdInternal(IEnumerable<ResourceBE> resources, ResourceBE.ParentType? parentType) { Dictionary<ulong, List<ResourceBE>> temp = new Dictionary<ulong, List<ResourceBE>>(); foreach(ResourceBE res in resources) { ulong? id = null; switch(parentType) { case ResourceBE.ParentType.PAGE: id = res.ParentPageId; break; case ResourceBE.ParentType.USER: id = res.ParentUserId; break; default: id = res.ParentId; break; } if((id ?? 0) != 0) { if(!temp.ContainsKey(id.Value)) { temp[id.Value] = new List<ResourceBE>(); } temp[id.Value].Add(res); } } return temp; }
public Dictionary<ulong, IList<ResourceBE>> GroupByParentId(IEnumerable<ResourceBE> resources, ResourceBE.ParentType? parentType) { Dictionary<ulong, List<ResourceBE>> temp = GroupByParentIdInternal(resources, parentType); Dictionary<ulong, IList<ResourceBE>> ret = new Dictionary<ulong, IList<ResourceBE>>(); //Convert lists to arrays foreach(KeyValuePair<ulong, List<ResourceBE>> kvp in temp) { ret[kvp.Key] = kvp.Value; } return ret; }
public void ReAssociateResourceState(ResourceBE oldRev, ResourceBE newRev) { if(oldRev == null || newRev == null) { return; } }
public virtual ResourceBE UpdateResourceRevision(ResourceBE res) { ResourceBE ret = DbUtils.CurrentSession.Resources_UpdateRevision(res); ReAssociateResourceState(res, ret); return ret; }
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; }
protected virtual ResourceBE BuildResourceRev(ResourceBE currentRevision) { currentRevision.AssertHeadRevision(); //Clone the resource revision ResourceBE newRev = new ResourceBE(currentRevision) { ChangeMask = ResourceBE.ChangeOperations.UNDEFINED, Timestamp = DateTime.UtcNow, UserId = DekiContext.Current.User.ID, ChangeSetId = 0, Deleted = false, ChangeDescription = null, IsHidden = false, Meta = currentRevision.Meta }; //Initialize for new revision (clear anything that shouldn't be carried over from current rev) return newRev; }
public IList<ResourceBE> GetResources(uint? parentId, ResourceBE.ParentType parentType, IList<ResourceBE.Type> resourceTypes, string[] names, DeletionFilter deletionStateFilter) { uint[] parentids = parentId == null ? new uint[] { } : new[] { parentId.Value }; return GetResources(parentids, parentType, resourceTypes, names, deletionStateFilter, null, null, null); }
public bool ValidateEtag(string etag, ResourceBE headResource, bool throwException) { if(!headResource.IsHeadRevision()) { throw new ResourceEtagNotHeadInvalidArgumentException(headResource.ResourceId, headResource.Revision); } bool isValid = false; if(etag != null && StringUtil.EqualsInvariant(etag, headResource.ETag())) { isValid = true; } if(!isValid && throwException) { throw new ResourceEtagConflictException(etag ?? string.Empty, headResource.ResourceId); } return isValid; }
public IList<ResourceBE> GetResourceRevisions(uint resourceid, ResourceBE.ChangeOperations changeTypesFilter, SortDirection sortRevisions, uint? limit) { IList<ResourceBE> ret = DbUtils.CurrentSession.Resources_GetRevisions(resourceid, changeTypesFilter, sortRevisions, limit).ToArray(); return ret; }
public IList<ResourceBE> GetResourcesByChangeSet(uint changeSetId, ResourceBE.Type resourceType) { return DbUtils.CurrentSession.Resources_GetByChangeSet(changeSetId, resourceType).ToArray(); }
public virtual ResourceBE BuildRevForMoveAndRename(ResourceBE currentResource, PageBE targetPage, string name, uint changeSetId) { ResourceBE newRev = BuildResourceRev(currentResource); //NOTE MaxM: This logic exists here since BuildResourceRev clears out fields preventing chaining of entity building for separate actions on one revision if(targetPage != null && (uint)targetPage.ID != newRev.ParentPageId.Value) { newRev.ParentPageId = (uint)targetPage.ID; newRev.ChangeMask |= ResourceBE.ChangeOperations.PARENT; } if(name != null && !StringUtil.EqualsInvariant(name, currentResource.Name)) { newRev.Name = name; newRev.ChangeMask |= ResourceBE.ChangeOperations.NAME; } newRev.ChangeSetId = changeSetId; return newRev; }
public IList<ResourceBE> PopulateChildren(ResourceBE[] resources, ResourceBE.Type[] resourceTypes, bool associateRevisions) { if(ArrayUtil.IsNullOrEmpty(resources)) { return resources; } Dictionary<uint, object> resourceIdsHash = new Dictionary<uint, object>(); foreach(ResourceBE r in resources) { resourceIdsHash[r.ResourceId] = null; } uint[] resourceIds = new List<uint>(resourceIdsHash.Keys).ToArray(); IList<ResourceBE> children = GetResources(resourceIds, null, resourceTypes, null, DeletionFilter.ACTIVEONLY, associateRevisions, null, null); Dictionary<ulong, IList<ResourceBE>> childrenByParentId = GroupByParentId(children, null); if(!associateRevisions) { //Head revisions of all children of the resource are associated foreach(ResourceBE parent in resources) { if(childrenByParentId.TryGetValue(parent.ResourceId, out children)) { parent.ChildResources = children.ToArray(); } } } else { //Corresponding revisions of all children of the resource are associated foreach(ResourceBE parent in resources) { if(childrenByParentId.TryGetValue(parent.ResourceId, out children)) { List<ResourceBE> childrenAtRevs = new List<ResourceBE>(); Dictionary<uint, ResourceBE[]> revsForChild = GroupByResourceId(children); foreach(KeyValuePair<uint, ResourceBE[]> childRevSet in revsForChild) { ResourceBE[] childRevisions = childRevSet.Value; //Ensure that the revisions are sorted by rev# desc. Index Array.Sort<ResourceBE>(childRevisions, delegate(ResourceBE left, ResourceBE right) { return right.Revision.CompareTo(left.Revision); }); if(parent.IsHeadRevision()) { //Head revision of parent should use head revision of children if(childRevisions.Length > 0) { childrenAtRevs.Add(childRevisions[0]); } } else { //Determine the highest timestamp a child property revision can have DateTime timestampOfNextRev = DateTime.MaxValue; //Revision HEAD - 1 can use the HEAD resources timestamp if(parent.Revision + 1 == parent.ResourceHeadRevision) { timestampOfNextRev = parent.ResourceUpdateTimestamp; } else { //Determine timestamp of next revision ResourceBE nextRev = null; //Look to see if current list of resources contains the next revision foreach(ResourceBE r in resources) { if(parent.ResourceId == r.ResourceId && parent.Revision + 1 == r.Revision) { nextRev = r; break; } } //Perform DB call to retrieve next revision if(nextRev == null) { nextRev = GetResourceRevision(parent.ResourceId, parent.Revision + 1); } if(nextRev != null) { timestampOfNextRev = nextRev.Timestamp; } } //Get most recent revision of child before the timestamp of the next revision of parent. for(int i = 0; i < childRevisions.Length; i++) { if(timestampOfNextRev > childRevisions[i].Timestamp) { childrenAtRevs.Add(childRevisions[i]); break; } } } } parent.ChildResources = childrenAtRevs.ToArray(); } } } return resources; }
public virtual ResourceBE BuildRevForRemove(ResourceBE currentResource, DateTime timestamp, uint changeSetId) { ResourceBE newRev = BuildResourceRev(currentResource); newRev.ResourceIsDeleted = true; newRev.Deleted = true; newRev.ChangeSetId = changeSetId; newRev.Timestamp = timestamp; newRev.ChangeMask = newRev.ChangeMask | ResourceBE.ChangeOperations.DELETEFLAG; return newRev; }
public ResourceBE DeleteProperty(ResourceBE prop, ResourceBE.ParentType parentType, XUri parentUri) { DekiContext.Current.Instance.EventSink.PropertyDelete(DekiContext.Current.Now, prop, DekiContext.Current.User, parentType, parentUri); return _resourceBL.Delete(prop); }
public virtual ResourceBE BuildRevForRestore(ResourceBE currentResource, PageBE targetPage, string resourceName, uint changeSetId) { ResourceBE newRev = BuildResourceRev(currentResource); newRev.ResourceIsDeleted = false; newRev.ChangeSetId = changeSetId; newRev.ParentPageId = (uint)targetPage.ID; newRev.Name = resourceName; newRev.ChangeMask = newRev.ChangeMask | ResourceBE.ChangeOperations.DELETEFLAG; return newRev; }
private XDoc AppendPropertyXml(XDoc doc, ResourceBE property, XUri parentResourceUri, string propSuffix, bool? explicitRevisionInfo, uint? contentCutoff, Dictionary<uint, UserBE> usersById) { bool requiresEnd = false; explicitRevisionInfo = explicitRevisionInfo ?? false; string propElement = string.IsNullOrEmpty(propSuffix) ? "property" : "property." + propSuffix; if(doc == null || doc.IsEmpty) { doc = new XDoc(propElement); } else { doc.Start(propElement); requiresEnd = true; } //Build the base uri to the property bool includeContents = property.Size <= (contentCutoff ?? DEFAULT_CONTENT_CUTOFF) && (property.MimeType.Match(MimeType.ANY_TEXT) ); //TODO: contents null check doc.Attr("name", property.Name) .Attr("href", /*explicitRevisionInfo.Value ? property.PropertyInfoUri(parentResourceUri, true) : */property.PropertyInfoUri(parentResourceUri)); if(property.IsHeadRevision()) { doc.Attr("etag", property.ETag()); } /* PROPERTY REVISIONS: if(!property.IsHeadRevision() || explicitRevisionInfo.Value) { revisions not currently exposed. doc.Attr("revision", property.Revision); } */ /* PROPERTY REVISIONS: doc.Start("revisions") .Attr("count", property.ResourceHeadRevision) .Attr("href", property.UriRevisions()) .End(); */ string content = null; if(includeContents) { content = property.Content.ToText(); } doc.Start("contents") .Attr("type", property.MimeType.ToString()) .Attr("size", property.Size) .Attr("href", /*PROPERTY REVISIONS: explicitRevisionInfo.Value ? property.PropertyContentUri(true) : */property.PropertyContentUri(parentResourceUri)) .Value(content) .End(); doc.Elem("date.modified", property.Timestamp); UserBE userModified; usersById.TryGetValue(property.UserId, out userModified); if(userModified != null) { doc.Add(UserBL.GetUserXml(userModified, "modified", Utils.ShowPrivateUserInfo(userModified))); } doc.Elem("change-description", property.ChangeDescription); if(property.Deleted) { UserBE userDeleted; usersById.TryGetValue(property.UserId, out userDeleted); if(userDeleted != null) { doc.Add(UserBL.GetUserXml(userDeleted, "deleted", Utils.ShowPrivateUserInfo(userDeleted))); } doc.Elem("date.deleted", property.Timestamp); } if(requiresEnd) { doc.End(); //property } return doc; }
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; }
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 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; }