/////////////////////////////////////////////////////////// // AyaFile.cs // Implementation of Class AyaFile // CSLA type: Editable Root // Created on: 30-Rocktober-2008 // Object design: John // Coded: 30-Rocktober-2008 /////////////////////////////////////////////////////////// using System; using System.Data; using CSLA.Data; using GZTW.Data; using CSLA; using System.Runtime.Serialization.Formatters.Binary; using System.IO; using System.Collections.Generic; //case 73 namespace GZTW.AyaNova.BLL { /// /// AyaFile object /// Storage, retrieval and deletion of files inside /// the AyaNova database. /// /// Automatic compression / decompression. /// /// NOTE: There is a 50MB source file size limit /// [Serializable] public class AyaFile : BusinessBase { #region Attributes private Guid mID; private SmartDate mCreated; private SmartDate mModified; private Guid mCreator; private Guid mModifier; private Guid mRootObjectID = Guid.Empty; private RootObjectTypes mRootObjectType; private AyaFileType mFileType; private string mName = null; private byte[] mContent=null; private bool bReadOnly = false; private int mFileSize; private int mFileSizeStored; #endregion #region Constructor /// /// Private constructor to prevent direct instantiation /// private AyaFile() { //New ID mID = Guid.NewGuid(); //Set record history to defaults mCreated = new SmartDate(DBUtil.CurrentWorkingDateTime); mModified = new SmartDate(); mCreator = Guid.Empty; mModifier = Guid.Empty; //pre-break various rules Name = ""; RootObjectID = Guid.Empty; RootObjectType = RootObjectTypes.Nothing; mFileType = AyaFileType.WikiFile; mFileSize = 0; } #endregion #region Business properties /// /// Internal Unique GUID value of AyaFile record in database /// public Guid ID { get { return mID; } } /// /// Get created date /// /// /// public string Created { get { return mCreated.ToString(); } } /// /// Get modified date /// /// /// public string Modified { get { return mModified.ToString(); } } /// /// Get user record ID of person who created this record /// /// /// public Guid Creator { get { return mCreator; } } /// /// Get user ID of person who modified this record /// /// /// public Guid Modifier { get { return mModifier; } } /// /// AyaNova object ID /// public Guid RootObjectID { get { return mRootObjectID; } set { if (bReadOnly) ThrowSetError(); else { if (mRootObjectID != value) { mRootObjectID = value; BrokenRules.Assert("RootObjectID", "Error.Object.RequiredFieldEmpty,AyaFile.Label.RootObjectID", "RootObjectID", value == Guid.Empty); MarkDirty(); } } } } /// /// AyaNova object type /// public RootObjectTypes RootObjectType { get { return mRootObjectType; } set { if (bReadOnly) ThrowSetError(); else { if (mRootObjectType != value) { mRootObjectType = value; BrokenRules.Assert("RootObjectType", "Error.Object.RequiredFieldEmpty,AyaFile.Label.RootObjectType", "RootObjectType", value == RootObjectTypes.Nothing); MarkDirty(); } } } } /// /// AyaNova file type /// public AyaFileType FileType { get { return mFileType; } set { if (bReadOnly) ThrowSetError(); else { if (mFileType != value) { mFileType = value; //BrokenRules.Assert("FileType", "Error.Object.RequiredFieldEmpty,AyaFile.Label.FileType", "FileType", value == RootObjectTypes.Nothing); MarkDirty(); } } } } /// /// File name displayed on wiki page and potentially in pick lists /// combo boxes /// /// This is a required field 1-255 Unicode characters and must be a legal file name /// public string Name { get { return mName; } set { if (bReadOnly) ThrowSetError(); else { if (mName != value) { mName = value; BrokenRules.Assert("NameRequired", "Error.Object.RequiredFieldEmpty,AyaFile.Label.Name", "Name", value.Length == 0); BrokenRules.Assert("NameLength", "Error.Object.FieldLengthExceeded255,AyaFile.Label.Name", "Name", value.Length > 255); MarkDirty(); } } } } /// /// Size of file in bytes /// (Note: *file* size, not the amount of bytes actually /// stored in the database which is compressed) /// public int FileSize { get { return this.mFileSize; } } /// /// Size of stored file in bytes /// (Note: *file* size stored in database after compression) /// public int FileSizeStored { get { return this.mFileSize; } } /// /// Returns content of AyaFile as a /// /// public System.IO.MemoryStream GetContent() { if (mContent.Length == mFileSize)//it's not compressed so just return it return new System.IO.MemoryStream(mContent); else//it *is* compressed so decompress and return return new System.IO.MemoryStream(AyaBizUtils.Decompress(mContent)); } /// /// Set the content from a /// Must be less bytes than limit set in /// or 50mb (hard coded limit) whichever is smaller /// /// public void SetContent(System.IO.Stream mStream) { //is file size larger than admin set value or default 50mb limit? long limit=AyaBizUtils.GlobalSettings.MaxFileSizeMB * 1048576; if(limit > 52428800) limit = 52428800; if (mStream.Length > limit) { throw new AyAyaFileTooLargeException(string.Format(LocalizedTextTable.GetLocalizedTextDirect("AyaFile.Error.FileTooLarge"), AyaBizUtils.FileSizeDisplay(limit))); } mFileSize = (int)mStream.Length; mStream.Position = 0; byte[] bData = new byte[mStream.Length]; mStream.Read(bData, 0, (int)mStream.Length); mContent = AyaBizUtils.Compress(bData); //did it compress smaller? //don't want to store uncompressible files that are actually larger after compression if (mContent.Length >= mFileSize) { //yup, it's bigger compressed, so store the uncompressed version instead mContent = bData; } MarkDirty(); } /// /// Byte array set content /// /// public void SetContent(byte[] bData) { //is file size larger than admin set value or default 50mb limit? long limit = AyaBizUtils.GlobalSettings.MaxFileSizeMB * 1048576; if (limit > 52428800) limit = 52428800; if (bData.LongLength > limit) { throw new AyAyaFileTooLargeException(string.Format(LocalizedTextTable.GetLocalizedTextDirect("AyaFile.Error.FileTooLarge"), AyaBizUtils.FileSizeDisplay(limit))); } mFileSize = (int)bData.LongLength; //mStream.Position = 0; //byte[] bData = new byte[mStream.Length]; //mStream.Read(bData, 0, (int)mStream.Length); mContent = AyaBizUtils.Compress(bData); //did it compress smaller? //don't want to store uncompressible files that are actually larger after compression if (mContent.Length >= mFileSize) { //yup, it's bigger compressed, so store the uncompressed version instead mContent = bData; } MarkDirty(); } /// /// Write file to disk /// /// Path to save to /// Optionally force file name or use original if not specified public string WriteToDisk(string sPath, string sName) { System.IO.FileStream fileStream = null; sPath = sPath.Trim(); if (sPath[sPath.Length-1] != Path.DirectorySeparatorChar) sPath += Path.DirectorySeparatorChar; if (string.IsNullOrEmpty(sName)) { sPath += mName; } else sPath += sName; try { fileStream = System.IO.File.Create(sPath); //TESTING ONLY //mFileSize = mContent.Length; if (mContent.Length == mFileSize)//not compressed? { fileStream.Write(mContent, 0, mContent.Length); fileStream.Close(); } else { byte[] b = AyaBizUtils.Decompress(mContent); fileStream.Write(b, 0, b.Length); fileStream.Close(); } } finally { fileStream.Dispose(); } return sPath; } /// /// Get the MIME type for this file based on extension /// public string mimeType { get { return AyaMimeTypeMap.GetMimeType(System.IO.Path.GetExtension(mName)); } } /// /// Throw an error when a read only user /// tries to set a property /// (this should normally never be called unless someone is using the developer api since the UI /// should prevent it from happening initially) /// private void ThrowSetError() { throw new System.Security.SecurityException ( string.Format ( LocalizedTextTable.GetLocalizedTextDirect("Error.Security.NotAuthorizedToChange"), LocalizedTextTable.GetLocalizedTextDirect("O.AyaFile") ) ); } #endregion #region System.object overrides /// /// /// /// public override string ToString() { return "AyaFile" + mID.ToString(); } /// /// public override bool Equals(Object obj) { if (obj == null || GetType() != obj.GetType()) return false; AyaFile c = (AyaFile)obj; return mID == c.mID; } /// /// /// /// public override int GetHashCode() { return ("AyaFile" + mID).GetHashCode(); } #endregion #region Static methods /// /// Create new AyaFile /// /// Empty AyaFile object public static AyaFile NewItem() { AyaFile c; if (AyaBizUtils.Right("Object.AyaFile") > (int)SecurityLevelTypes.ReadOnly) { c = new AyaFile(); return c; } else throw new System.Security.SecurityException( string.Format( LocalizedTextTable.GetLocalizedTextDirect("Error.Security.NotAuthorizedToCreate"), LocalizedTextTable.GetLocalizedTextDirect("O.AyaFile"))); } /// /// Fetch existing AyaFile /// /// AyaFile /// AyaFile Guid public static AyaFile GetItem(Guid ID) { return (AyaFile)DataPortal.Fetch(new Criteria(ID,Guid.Empty)); } /// /// Delete AyaFile /// /// AyaFile applicationID public static void DeleteItem(Guid ID) { DataPortal.Delete(new Criteria(ID, Guid.Empty)); } /// /// Delete all files that are embedded images in wiki page /// /// WikiPage ID public static void DeleteAllEmbeddedImagesInWikiPage(Guid WikiPageID) { DataPortal.Delete(new Criteria(Guid.Empty, WikiPageID)); } /// /// Same as WriteFileToDisk but fetches AyaFile first /// returns the full path and name it was actually saved to /// /// /// /// public static string GetAndWriteFileToDisk(Guid ID, string sPath, string sName) { AyaFile af = AyaFile.GetItem(ID); return af.WriteToDisk(sPath, sName); } /// /// User can read AyaFiles? /// public static bool CanRead { get { return (AyaBizUtils.Right("Object.AyaFile") > (int)SecurityLevelTypes.NoAccess); } } /// /// User can write AyaFiles? /// (anything above read only is considered full rights to edit, upload and delete files) /// public static bool CanWrite { get { return (AyaBizUtils.Right("Object.AyaFile") > (int)SecurityLevelTypes.ReadOnly); } } /// /// Retrieve internal ID from name. /// /// /// Text value /// Guid ID value or Guid.Empty if no match public static Guid GetIDFromName(string Name) { return GuidFetcher.GetItem("AFILE", "ANAME", Name); } #endregion #region DAL DATA ACCESS #region Fetch /// /// protected override void DataPortal_Fetch(object Criteria) { Criteria crit = (Criteria)Criteria; SafeDataReader dr = null; try { dr = DBUtil.GetReaderFromSQLString("SELECT * FROM aFile WHERE aID=@ID;", crit.ID); if (!dr.Read()) DBUtil.ThrowFetchError("AyaFile ID: " + crit.ID.ToString()); //Standard fields mID = dr.GetGuid("aID"); mCreated = DBUtil.ToLocal(dr.GetSmartDate("aCreated")); mModified = DBUtil.ToLocal(dr.GetSmartDate("aModified")); mCreator = dr.GetGuid("aCreator"); mModifier = dr.GetGuid("aModifier"); //AyaFile fields Name = dr.GetString("aName"); mFileSize = dr.GetInt32("AFileSize"); RootObjectID = dr.GetGuid("aRootObjectID"); RootObjectType = (RootObjectTypes)dr.GetInt16("aRootObjectType"); FileType = (AyaFileType)dr.GetInt16("aFileType"); //Get the FObject //Get the data's size mFileSizeStored = dr.GetInt32("aFObjectSize"); //allocate a place to store it mContent = new Byte[mFileSizeStored]; //retrieve the (compressed) bytes dr.GetBytes("aFObject", 0, mContent, 0, mContent.Length); if(dr!=null) dr.Close(); } finally { if (dr != null) dr.Close(); } MarkOld(); //Get access rights level bReadOnly = AyaBizUtils.Right("Object.AyaFile") < (int)SecurityLevelTypes.ReadWrite; } #endregion fetch #region Update /// /// Called by DataPortal to delete/add/update data into the database /// protected override void DataPortal_Update() { // If not a new record, check if record was modified //by another user since original retrieval: if (!IsNew) DBUtil.CheckSafeToUpdate(this.mModified.Date, this.mID, "aFile"); #region Delete if (IsDeleted) { throw new System.NotSupportedException("AyaFile->Update->Delete: not supported for this object, call the static/shared AyaFile.DeleteItem() method instead"); } #endregion #region Add / Update //get modification time temporarily, if update succeeds then //set to this time System.DateTime dtModified = DBUtil.CurrentWorkingDateTime; DBCommandWrapper cm = null; if (IsNew)//Add or update? cm = DBUtil.GetCommandFromSQL( "INSERT INTO aFile (aID, aCreated,aModified,aCreator,aModifier, " + "aRootObjectID, aRootObjectType, aFileType, aName, aFileSize, aFObject,aFObjectSize) " + "VALUES (@ID,@Created,@Modified,@CurrentUserID,@CurrentUserID, " + "@RootObjectID,@RootObjectType, @FileType, @Name,@FileSize,@FObject,@FObjectSize)" ); else cm = DBUtil.GetCommandFromSQL( "UPDATE aFile SET aID=@ID, " + "aModified=@Modified,aModifier=@CurrentUserID, " + "aRootObjectID=@RootObjectID,aRootObjectType=@RootObjectType, aFileType=@FileType, aName=@Name, " + "aFileSize=@FileSize, aFObject=@FObject,aFObjectSize=@FObjectSize " + "WHERE aID=@ID" ); //AyaFile fields cm.AddInParameter("@ID", DbType.Guid, mID); cm.AddInParameter("@Name", DbType.String, mName); cm.AddInParameter("@RootObjectID", DbType.Guid, mRootObjectID); cm.AddInParameter("@RootObjectType", DbType.Int16, (int)mRootObjectType); cm.AddInParameter("@Filetype", DbType.Int16, (int)mFileType); cm.AddInParameter("@FileSize", DbType.Int32, mFileSize); //Standard fields cm.AddInParameter("@CurrentUserID", DbType.Guid, CurrentUserID); cm.AddInParameter("@Created", DbType.DateTime, DBUtil.ToUTC(mCreated).DBValue); cm.AddInParameter("@Modified", DbType.DateTime, DBUtil.ToUTC(dtModified)); cm.AddInParameter("@FObject", DbType.Object, mContent); cm.AddInParameter("@FObjectSize", DbType.Int32, mContent.GetLength(0)); using (IDbConnection connection = DBUtil.DB.GetConnection()) { connection.Open(); IDbTransaction transaction = connection.BeginTransaction(); try { DBUtil.DB.ExecuteNonQuery(cm, transaction); MarkOld();//db is now synched with object // Commit the transaction transaction.Commit(); } catch { // Rollback transaction transaction.Rollback(); throw; } finally { connection.Close(); } //Successful update so //change modification time to match this.mModified.Date = dtModified; } #endregion } #endregion update #region Delete /// /// Remove a AyaFile record /// /// protected override void DataPortal_Delete(object Criteria) { Criteria crit = (Criteria)Criteria; DBCommandWrapper cmDelete = null; //Delete *all* embedded images from a wiki page? if (crit.WikiPageID != Guid.Empty) { cmDelete = DBUtil.GetCommandFromSQL("DELETE FROM aFile WHERE aRootObjectID = @ID AND aFileType=1"); cmDelete.AddInParameter("@ID", DbType.Guid, crit.WikiPageID); } else { //Delete object cmDelete = DBUtil.GetCommandFromSQL("DELETE FROM aFile WHERE aID = @ID;"); cmDelete.AddInParameter("@ID", DbType.Guid, crit.ID); } using (IDbConnection connection = DBUtil.DB.GetConnection()) { connection.Open(); IDbTransaction transaction = connection.BeginTransaction(); try { DBUtil.DB.ExecuteNonQuery(cmDelete, transaction); // Commit the transaction transaction.Commit(); } catch { // Rollback transaction transaction.Rollback(); throw; } finally { connection.Close(); } } } #endregion delete #endregion #region Override IsValid / IsDirty /// /// /// public override bool IsValid { get { return base.IsValid ; } } /// /// /// public override bool IsDirty { get { return base.IsDirty ; } } #endregion #region criteria /// /// Criteria for identifying existing object /// [Serializable] private class Criteria { public Guid ID; public Guid WikiPageID; public Criteria(Guid _ID, Guid _WikiPageID) { ID = _ID; WikiPageID = _WikiPageID; } } #endregion }//end AyaFile }//end namespace GZTW.AyaNova.BLL