Update ContentServices.cs

Update ContentServices.cs

--- Services/Isle.BizServices/ContentServices.cs
+++ Services/Isle.BizServices/ContentServices.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
 using System.Collections.Generic;
 using System.Data;
 using System.Linq;
@@ -31,6 +31,7 @@
     {
         static string thisClassName = "ContentServices";
         MyManager myMgr = new MyManager();
+		CSMgr csMgr = new CSMgr();

         EFDAL.IsleContentContext ctx = new EFDAL.IsleContentContext();
         EFManager myEfManager = new EFManager();
@@ -47,7 +48,7 @@
 		/// <param name="pId"></param>
 		/// <param name="statusMessage"></param>
 		/// <returns></returns>
-        public bool Delete( int pId, ref string statusMessage )
+        public bool Delete( int pId, int userId, ref string statusMessage )
 		{
             bool isValid = false;
             //get item in order to handle related actions
@@ -58,13 +59,14 @@
                 //also, related doc version is deleted.
                 //isValid = myEfManager.Content_Delete( pId, ref statusMessage );
                 //15-01-28 MP - moved up delete to here to enable use of other services at this level
-                isValid = Content_Delete( pId, ref statusMessage );
+                isValid = Content_Delete( pId, userId, ref statusMessage );
                 //assumes if failed, the status message will have details
+				new ActivityBizServices().AddActivity( "Audit", "Content", "Delete", "Content item deleted by user", userId, 0, pId, 0 );

                 //if resource exists, need to set inactive
                 if ( entity.HasResourceId() )
                 {
-                    new ResourceBizService().Resource_SetInactive( entity.ResourceIntId, ref statusMessage );
+                    new ResourceBizService().Resource_SetInactive( entity.ResourceIntId, userId, ref statusMessage );
                 }
             }
             return isValid;
@@ -77,7 +79,7 @@
 		/// <param name="id"></param>
 		/// <param name="statusMessage"></param>
 		/// <returns></returns>
-        private bool Content_Delete( int id, ref string statusMessage )
+        private bool Content_Delete( int id, int userId, ref string statusMessage )
         {
             bool isSuccessful = false;
             try
@@ -92,13 +94,21 @@
                         //will need to delete resource
                         //14-12-04 MP - added code at the services level to check this!
                         //15-01-28 MP - note in a cascade delete, the resources will need to be handled!!
+						//15-11-23 MP - need to do content standards separately, as no RI on related standards
+						Content_DeleteAllStandards( id, userId );
+
                         int resourceId = item.ResourceIntId == null ? 0 : ( int ) item.ResourceIntId;

                         context.Contents.Remove( item );
                         context.SaveChanges();
                         isSuccessful = true;

-                        //TODO - need to check for and delete a related resource
+						//if resource exists, need to set inactive
+						if ( resourceId > 0 )
+						{
+							new ResourceBizService().Resource_SetInactive( resourceId, userId, ref statusMessage );
+						}
+
                         //delete doc version
                         if ( item.DocumentRowId != null
                             && item.DocumentRowId.ToString().Length == 36
@@ -118,12 +128,13 @@
                         {
                             foreach ( EFDAL.Content efom in eflist )
                             {
-                                Content_Delete( efom.Id, ref statusMessage );
+                                Content_Delete( efom.Id, userId, ref statusMessage );
                                 //if resource exists, need to set inactive
-                                if ( efom.ResourceIntId != null && efom.ResourceIntId > 0 )
-                                {
-                                    new ResourceBizService().Resource_SetInactive( ( int ) efom.ResourceIntId, ref statusMessage );
-                                }
+								//already done in Content_Delete
+								//if ( efom.ResourceIntId != null && efom.ResourceIntId > 0 )
+								//{
+								//	new ResourceBizService().Resource_SetInactive( ( int ) efom.ResourceIntId, ref statusMessage );
+								//}
                             }
                             statusMessage = string.Format( "Also removed all child items ({0})", eflist.Count );
                         }
@@ -135,9 +146,50 @@
             {
                 isSuccessful = false;
                 statusMessage = ex.Message;
+				LogError( ex, string.Format("Content_Delete. id: {0}", id) );
             }
             return isSuccessful;
         }
+
+		///
+		/// Delete all content standards (and related standards) for a content item
+		///
+		/// <param name="contentId"></param>
+		/// <param name="userId"></param>
+		public void Content_DeleteAllStandards( int contentId, int userId )
+		{
+			string statusMessage = "";
+			ContentItem content = GetBasic( contentId );
+
+			List<Content_StandardSummary> list = CSMgr.Fill_ContentStandards( contentId );
+			foreach ( Content_StandardSummary item in list )
+			{
+				ContentStandard_Delete( content.ParentId, userId, item.Id, ref statusMessage );
+			}
+		}
+
+		///
+		/// Delete a Content.standard
+		///
+		/// <param name="parentId"></param>
+		/// <param name="userId"></param>
+		/// <param name="contentStandardId"></param>
+		/// <param name="statusMessage"></param>
+		/// <returns></returns>
+		public bool ContentStandard_Delete( int parentId, int userId, int contentStandardId, ref string statusMessage )
+		{
+
+			//need to delete related first, as no RI cascade was possible
+			ContentRelatedStandard_Delete( contentStandardId );
+			//now delete standard
+			bool ok = csMgr.ContentStandard_Delete( contentStandardId, ref statusMessage );
+
+			//also need to check for delete of a resource standard? -
+			//do delayed
+			if ( parentId > 0 )
+				new ResourceV2Services().CheckForDelayedPublishing( parentId, userId );
+			return ok;
+		}

 		///
@@ -154,23 +206,35 @@
             //until a better process is designed, always add creator of a learning list as an admin partner
 			//actually may want to do other top levels, including content (10)
 			//not sure about documents - typically within a list, but are suppliments?
-            if (contentId > 0 )
-            {
-				if ( entity.TypeId == ContentItem.CURRICULUM_CONTENT_ID
-					|| ( entity.TypeId >= ContentItem.GENERAL_CONTENT_ID && entity.TypeId < ContentItem.DOCUMENT_CONTENT_ID ) )
-					Content_AddCreatorPartner(contentId, entity.CreatedById, ref statusMessage);
-            }
+			//if (contentId > 0 )
+			//{
+			//	if ( entity.TypeId == ContentItem.CURRICULUM_CONTENT_ID
+			//		|| ( entity.TypeId >= ContentItem.GENERAL_CONTENT_ID && entity.TypeId < ContentItem.DOCUMENT_CONTENT_ID ) )
+			//		Content_AddCreatorPartner(contentId, entity.CreatedById, ref statusMessage);
+			//}
             return contentId;
 		}
         public int Create_ef( ContentItem entity, ref string statusMessage )
         {
+			//org check
+			if ( entity.OrgId == 0 && entity.ParentId > 0 )
+			{
+				ContentItem topNode = GetTopNodeForHierarchy( entity );
+				entity.OrgId = topNode.OrgId;
+			}
             int contentId = myEfManager.ContentAdd(entity, ref statusMessage);

             //until a better process is designed, always add creator of a learning list as an admin partner
-            if (contentId > 0 && entity.TypeId == ContentItem.CURRICULUM_CONTENT_ID)
-            {
-                Content_AddCreatorPartner(contentId, entity.CreatedById, ref statusMessage);
-            }
+			if ( contentId > 0 )
+			{
+				string keywordsStatus = HandleContentKeyword( entity );
+
+				if ( entity.TypeId == ContentItem.CURRICULUM_CONTENT_ID
+					|| entity.TypeId == ContentItem.LEARNING_SET_CONTENT_ID
+					|| ( entity.TypeId >= ContentItem.GENERAL_CONTENT_ID && entity.TypeId < ContentItem.DOCUMENT_CONTENT_ID ) )
+					Content_AddCreatorPartner( contentId, entity.CreatedById, ref statusMessage );
+			}
+
             return contentId;
         }
 		///
@@ -186,9 +250,18 @@
 		public string Update( ContentItem entity, bool updateResourceVersion )
 		{
             string result = "";
+
             if ( myEfManager.ContentUpdate( entity ) )
                     result = "successful";
-
+
+			string statusMessage = HandleContentKeyword( entity );
+			//if ( entity.ContentKeywords != null && entity.ContentKeywords.Count > 0 )
+			//{
+			//	foreach (ContentKeyword item in entity.ContentKeywords) {
+			//		myMgr.ContentKeyword_Create( entity.Id, item.Keyword, item.CreatedById, ref statusMessage );
+			//	}
+
+			//}
             if ( entity.HasResourceId() && updateResourceVersion )
             {
                 //may want to check on sync of RV content!
@@ -199,18 +272,33 @@

 		}

-        ///
-        /// Update ContentItem with related ResourceVersionId
-        ///
-        /// <param name="id"></param>
-        /// <param name="resourceVersionId"></param>
-        /// <returns></returns>
-		//[Obsolete]
-		//private string UpdateResourceVersionId( int id, int resourceVersionId )
-		//{
-		//	//return myMgr.UpdateResourceVersionId( id, resourceVersionId );
-		//	return "obsolete";
-		//}//
+		///
+		/// DRAFT - Handle content keywords
+		/// I progress. Not sure if we will only get new keywords here, or all, and have to do a balance line compare, or delete all and just add the list.
+		///
+		/// <param name="entity"></param>
+		/// <returns></returns>
+		public string HandleContentKeyword( ContentItem entity )
+		{
+			string statusMessage = "";
+			string result = "";
+			int kId = 0;
+			if ( entity.ContentKeywords != null && entity.ContentKeywords.Count > 0 )
+			{
+				foreach ( ContentKeyword item in entity.ContentKeywords )
+				{
+					kId = myMgr.ContentKeyword_Create( entity.Id, item.Keyword, item.CreatedById, ref statusMessage );
+					if ( kId == 0 )
+					{
+						result = result + statusMessage + ";
";
+					}
+				}
+
+			}
+
+			return result;
+
+		}

         ///
         /// Do a quick update to ensure image is generated correctly
@@ -234,7 +322,7 @@
         public string UpdateAfterQuickPub( ContentItem entity )
         {
             //check on usage rights
-            if ( entity.UseRightsUrl != null && entity.UseRightsUrl.Length > 5 && entity.ConditionsOfUseId == 0 )
+			if ( entity.UsageRightsUrl != null && entity.UsageRightsUrl.Length > 5 && entity.ConditionsOfUseId == 0 )
             {
                 //call method to look up url
                 //if found set 1, else use 4. Read the Fine Print
@@ -267,7 +355,6 @@
         {
             try
             {
-                //ContentItem entity = EFManager.Content_GetByResourceVersionId( rv.Id );
                 ContentItem entity = EFManager.Content_GetByResourceId( rv.ResourceIntId );
                 if ( entity != null && entity.Id > 0 )
                 {
@@ -289,6 +376,85 @@
             }

         }//
+		#endregion
+		#region Content.RelatedStandards
+
+		public int ContentRelatedStandard_Add( int curriculumID, int contentId, int contentStandardId )
+		{
+			int newId = 0;
+			CSMgr csMgr = new CSMgr();
+			ResourceV2Services resMgr = new ResourceV2Services();
+			int cntr = 0;
+			//delayed pub is handled by caller
+			bool doingDelayedPubHere = false;
+			try
+			{
+				//get content and add to parent
+				ContentItem item = GetBasic( contentId );
+
+				//while each parent has a parent, add the related standard to the node
+				while ( item.ParentId > 0 )
+				{
+					//add
+					csMgr.ContentRelatedStandard_Add( curriculumID, item.ParentId, contentStandardId );
+
+					//if has resourceId, do something
+					//==> actually would have been necessary after adding teh content.standard. Should use the delayed publishing!
+					if ( item.ResourceIntId > 0 && item.StatusId == 5 && doingDelayedPubHere )
+					{
+						//call proc to do copy - probably need top node Id
+						//add delayed record and submit later
+						resMgr.ResourceDelayedPublish_AddElasticRequest( item.Id, item.ResourceIntId );
+						cntr++;
+					}
+					//get next
+					item = GetBasic( item.ParentId );
+
+				} //while
+
+				if ( cntr > 0 )
+				{
+					//initiate elastic update
+					//done elsewhere
+				}
+			}
+			catch ( Exception ex )
+			{
+				LoggingHelper.LogError( ex, thisClassName + ".ContentRelatedStandard_Add()" );
+			}
+
+
+			return newId;
+		}
+
+		public void ContentRelatedStandard_Delete( int contentStandardId )
+		{
+			int cntr = 0;
+			//delayed pub is handled by caller
+			bool doingDelayedPubHere = false;
+			ResourceV2Services resMgr = new ResourceV2Services();
+			List<Content_RelatedStandardsSummary> list = CSMgr.ContentRelatedStandard_Summary( contentStandardId );
+			foreach ( Content_RelatedStandardsSummary item in list )
+			{
+				//delete related
+				csMgr.ContentRelatedStandard_Delete( item.ContentStandardId, item.ContentId );
+
+				//ContentItem ci = GetBasic( item.ContentId );
+				if ( item.ResourceIntId > 0 && doingDelayedPubHere )
+				{
+					//add delayed record and submit later
+					resMgr.ResourceDelayedPublish_AddElasticRequest( item.ContentId, item.ResourceIntId );
+					cntr++;
+				}
+			}
+
+			if ( cntr > 0 )
+			{
+				//initiate elastic update
+				//elsewhere
+			}
+		}//
+
 		#endregion
 		#region === content approval ===
 		///
@@ -315,9 +481,8 @@
 				return false;
 			}

-			//entity.ResourceVersionId = resource.Version.Id;
 			entity.ResourceIntId = resource.Id;
-			entity.UseRightsUrl = resource.Version.Rights;
+			entity.UsageRightsUrl = resource.Version.Rights;

 			if (entity.IsOrgContent() == false)
 			{
@@ -336,7 +501,8 @@
 			entity.StatusId = ContentItem.SUBMITTED_STATUS;
 			Update(entity);
 			//set resource to inactive
-			new ResourceBizService().Resource_SetInactive(resource.Id, ref statusMessage);
+			//??should this be not doing a delete from LR? - should not have been published if required approval
+			new ResourceBizService().Resource_SetInactive(resource.Id, author, ref statusMessage);

 			//add record to audit table
 			string msg = string.Format("Request by {0} for approval of resource id: {1}", author.FullName(), entity.Id);
@@ -377,8 +543,10 @@
 				entity.StatusId = ContentItem.SUBMITTED_STATUS;
 				Update(entity);
 				//set resource to inactive
-				new ResourceBizService().Resource_SetInactive(entity.ResourceIntId, ref statusMessage);
-
+				if (entity.ResourceIntId > 0)
+				{
+					new ResourceBizService().Resource_SetInactive(entity.ResourceIntId, author, ref statusMessage);
+				}
 				//add record to audit table
 				string msg = string.Format("Request by {0} for approval of resource id: {1}", author.FullName(), entity.Id);

@@ -428,7 +596,7 @@
 				string url = UtilityManager.FormatAbsoluteUrl(string.Format("/Content/{0}/{1}", entity.Id.ToString(), friendlyTitle), true);
 				string urlTitle = string.Format("{1}", url, entity.Title);
 				if (System.DateTime.Now < new System.DateTime(2013, 7, 1))
-					bccEmail = UtilityManager.GetAppKeyValue("appAdminEmail", "info@illinoisworknet.com");
+					bccEmail = UtilityManager.GetAppKeyValue( "appAdminEmail", "info@ilsharedlearning.org" );

 				string subject = string.Format("Isle request to approve an education resource from: {0}", author.FullName());
 				string body = string.Format("
{0} from {1} is requesting approval on an education resource.
", author.FullName(), org.Name);
@@ -481,7 +649,7 @@
 			Update(entity);

 			//set resource to inactive
-			new ResourceBizService().Resource_SetInactive(entity.ResourceIntId, ref statusMessage);
+			new ResourceBizService().Resource_SetInactive( entity.ResourceIntId, approver, ref statusMessage );

 			//add record to audit table
 			string msg = string.Format("Resource: {0}, author: {1}, was declined by {2}", entity.Title, entity.Author, approver.FullName());
@@ -615,6 +783,78 @@

         }//

+		///
+		/// Get detail - old format that only handles one values, soon to be replaced by GetForResourceDetail
+		///
+		/// <param name="resourceId"></param>
+		/// <param name="user"></param>
+		/// <param name="canViewItem"></param>
+		/// <returns></returns>
+		public ContentItem GetForResourceDetail( int resourceId, Patron user, ref bool canViewItem )
+		{
+
+			ContentItem entity = new ContentItem();
+			string statusMessage = "";
+			string extraMessage = "";
+			string formattedUrl = "";
+			canViewItem = true;
+
+			if ( resourceId > 0 )
+			{
+				//15-10-10 mp - there can now be multiple records returned
+				// entity = EFManager.Content_GetByResourceId(resourceId);
+
+				List<ContentItem> list = EFManager.Content_ListByResourceId( resourceId );
+				if ( list.Count > 0 )
+					entity = list[ 0 ];
+				//if found, what else would we want
+				if ( entity != null && entity.Id > 0 )
+				{
+					if ( entity.TypeId == ContentItem.DOCUMENT_CONTENT_ID )
+					{
+						canViewItem = CanViewDocument( entity, user, ref statusMessage, ref formattedUrl );
+						if ( canViewItem == false )
+						{
+							//for consistancy, just use formattedUrl always - except can't assume the H level.
+							//MP- in this case, let caller do the formatting. It will have access to ResourceFriendlyUrl and DocumentUrl
+							//in this case, the caller (detail page), needs to handle
+							extraMessage = "
" + statusMessage + "
";
+						}
+						//method first checks if part of a curriculum, then formats message with link to item under the curriculum
+						//for docs, should link to parent node!
+						if ( FormatCurriculumMessage( entity, extraMessage ) == false )
+						{
+							//HACK WARNING - using standAloneContent to indicate not part of curriculum, so detail page can show actual file url
+							string message = "
This resource has a related page:

{1}

";
+							//
+							string content = string.Format( message, FormatContentFriendlyUrl( entity ), entity.Title );
+							content += extraMessage;
+
+							entity.Message = string.Format( "

Resource Note
{0}
", content );
+						}
+
+					}
+
+					else if ( entity.TypeId == ContentItem.CURRICULUM_CONTENT_ID )
+					{
+						//could get all standards, but would take a lot of time - unless cached
+						//string content = "
Note: Only standards directly aligned to this resource are displayed here. The related resource page will display all standards that have been aligned to any of the components.
";
+						//entity.Message = string.Format( "

Resource Set Information
{0}
", content );
+					}
+					else if ( entity.IsHierarchyType
+						|| entity.TypeId == ContentItem.LEARNING_SET_CONTENT_ID
+						|| entity.TypeId == ContentItem.EXTERNAL_URL_CONTENT_ID )
+					{
+						FormatCurriculumMessage( entity, extraMessage );
+					}
+
+				}
+			}
+
+			return entity;
+
+		}//
+
         ///
         /// Get Content record using resourceId
         /// The implied context is for use on the detail page or to support the resource
@@ -624,14 +864,15 @@
         /// <param name="user"></param>
         /// <param name="canViewItem">Should default to true, if a related to a document, a privilege check will be done</param>
         /// <returns></returns>
-        public ContentItem GetForResourceDetail(int resourceId, Patron user, ref bool canViewItem )
-        {
-
-            ContentItem entity = new ContentItem();
+        public ContentItem GetForResourceDetailNew(int resourceId, Patron user, ref bool canViewItem )
+        {
+
+            ContentItem mainEntity = new ContentItem();
             string statusMessage = "";
             string extraMessage = "";
             string formattedUrl = "";
             canViewItem = true;
+			int cntr = 0;

             if (resourceId > 0)
             {
@@ -640,51 +881,72 @@

 				List<ContentItem> list = EFManager.Content_ListByResourceId( resourceId );
 				if ( list.Count > 0 )
-					entity = list[ 0 ];
-                //if found, what else would we want
-                if (entity != null && entity.Id > 0)
-                {
-                    if (entity.TypeId == ContentItem.DOCUMENT_CONTENT_ID)
-                    {
-                        canViewItem = CanViewDocument( entity, user, ref statusMessage, ref formattedUrl );
-                        if ( canViewItem == false )
-                        {
-                            //for consistancy, just use formattedUrl always - except can't assume the H level.
-                            //MP- in this case, let caller do the formatting. It will have access to ResourceFriendlyUrl and DocumentUrl
-                            //in this case, the caller (detail page), needs to handle
-                            extraMessage = "
" + statusMessage + "
";
-                        }
-                        //method first checks if part of a curriculum, then formats message with link to item under the curriculum
-                        //for docs, should link to parent node!
-                        if ( FormatCurriculumMessage( entity, extraMessage ) == false )
-                        {
-							//HACK WARNING - using standAloneContent to indicate not part of curriculum, so detail page can show actual file url
-                            string message = "
This resource has a related page:

{1}

";
-                            //
-                            string content = string.Format( message, FormatContentFriendlyUrl( entity ), entity.Title );
-                            content += extraMessage;
-
-                            entity.Message = string.Format( "

Resource Note
{0}
", content );
-                        }
-
+				{
+					if ( cntr == 0 )
+					{
+						mainEntity = list[ 0 ];
+						mainEntity.Message = "";
 					}
-
-                    else if (entity.TypeId == ContentItem.CURRICULUM_CONTENT_ID)
-                    {
-                        //could get all standards, but would take a lot of time - unless cached
-						//string content = "
Note: Only standards directly aligned to this resource are displayed here. The related resource page will display all standards that have been aligned to any of the components.
";
-						//entity.Message = string.Format( "

Resource Set Information
{0}
", content );
-                    }
-					else if (entity.IsHierarchyType
-						|| entity.TypeId == ContentItem.EXTERNAL_URL_CONTENT_ID)
-                    {
-                        FormatCurriculumMessage( entity, extraMessage );
-                    }
-
-                }
-            }
-
-            return entity;
+					cntr++;
+					foreach ( ContentItem entity in list )
+					{
+						//if found, what else would we want
+						if ( entity != null && entity.Id > 0 )
+						{
+							if ( entity.TypeId == ContentItem.DOCUMENT_CONTENT_ID )
+							{
+								canViewItem = CanViewDocument( entity, user, ref statusMessage, ref formattedUrl );
+								if ( canViewItem == false )
+								{
+									//for consistancy, just use formattedUrl always - except can't assume the H level.
+									//MP- in this case, let caller do the formatting. It will have access to ResourceFriendlyUrl and DocumentUrl
+									//in this case, the caller (detail page), needs to handle
+									extraMessage = "
" + statusMessage + "
";
+								}
+								//method first checks if part of a curriculum, then formats message with link to item under the curriculum
+								//for docs, should link to parent node!
+								if ( FormatCurriculumMessage( entity, extraMessage ) == false )
+								{
+									//???????????????
+									if ( entity.Message.Length > 0 )
+									{
+										mainEntity.Message += entity.Message + "
";
+									}
+
+									//HACK WARNING - using standAloneContent to indicate not part of curriculum, so detail page can show actual file url
+									string message = "
This resource has a related page:

{1}

";
+									//
+									string content = string.Format( message, FormatContentFriendlyUrl( entity ), entity.Title );
+									content += extraMessage;
+
+									mainEntity.Message += string.Format( "

Resource Note
{0}

", content );
+								}
+
+							}
+
+							else if ( entity.TypeId == ContentItem.CURRICULUM_CONTENT_ID )
+							{
+								//could get all standards, but would take a lot of time - unless cached
+								//string content = "
Note: Only standards directly aligned to this resource are displayed here. The related resource page will display all standards that have been aligned to any of the components.
";
+								//entity.Message = string.Format( "

Resource Set Information
{0}
", content );
+							}
+							else if ( entity.IsHierarchyType
+								|| entity.TypeId == ContentItem.LEARNING_SET_CONTENT_ID
+								|| entity.TypeId == ContentItem.EXTERNAL_URL_CONTENT_ID )
+							{
+								FormatCurriculumMessage( entity, extraMessage );
+								if ( entity.Message.Length > 0 )
+								{
+									mainEntity.Message += entity.Message + "
";
+								}
+							}
+
+						}
+					}//foreach
+				}
+            }
+
+			return mainEntity;

         }//
 		private void HandleContentItems( ContentItem entity, Patron user, ref bool canViewItem )
@@ -714,7 +976,7 @@
 						//
 						string content = string.Format( message, FormatContentFriendlyUrl( entity ), entity.Title );
 						content += extraMessage;
-
+						//ToDo - shouldn't have to worry about multi
 						entity.Message = string.Format( "

Resource Note
{0}
", content );
 					}

@@ -727,6 +989,7 @@
 					//entity.Message = string.Format( "

Resource Set Information
{0}
", content );
 				}
 				else if ( entity.IsHierarchyType
+					|| entity.TypeId == ContentItem.LEARNING_SET_CONTENT_ID
 					|| entity.TypeId == ContentItem.EXTERNAL_URL_CONTENT_ID )
 				{
 					FormatCurriculumMessage( entity, extraMessage );
@@ -758,7 +1021,8 @@
             string noteBody = "";

             //if top code is a curriculum
-            if (topNode.TypeId == ContentItem.CURRICULUM_CONTENT_ID)
+            if (topNode.TypeId == ContentItem.CURRICULUM_CONTENT_ID
+				|| topNode.TypeId == ContentItem.LEARNING_SET_CONTENT_ID )
             {
                 string content = string.Format( message, FormatCurriculumFriendlyUrl( topNode, entity ), topNode.Title, noteBody );
                 content += extraMessage;
@@ -808,8 +1072,6 @@
 		/// <param name="pMaximumRows"></param>
 		/// <param name="pTotalRows"></param>
 		/// <returns></returns>
-
-
 		public DataSet SearchOLD( string pFilter, string pOrderBy, int pStartPageIndex, int pMaximumRows, ref int pTotalRows )
 		{
 			//TODO - create a List<> version
@@ -908,6 +1170,7 @@

         ///
         /// Check if the passed node is part of a hierarchy
+		/// Or realistically, is the presence of a parentId enough?
         ///
         /// <param name="node"></param>
         /// <returns></returns>
@@ -923,48 +1186,23 @@
         /// <returns></returns>
         public int GetTopIdForHierarchy( ContentItem node )
         {
+			if ( node != null && node.ParentId == 0 )
+				return node.Id;
+
             ContentItem entity = EFManager.GetTopNode(node);

             return entity.Id;
         }
         public ContentItem GetTopNodeForHierarchy(ContentItem node)
         {
+			if ( node != null && node.ParentId == 0 )
+				return node;
+
             ContentItem entity = EFManager.GetTopNode(node);

             return entity;
         }

-        ///
-        /// Get node detail - assume guest user
-        ///
-        /// <param name="pNodeId"></param>
-        /// <returns></returns>
-        //public ContentItem GetCurriculumNode( int pNodeId )
-        //{
-        //    ThisUser user = new ThisUser();
-
-        //    return GetCurriculumNode( pNodeId, user, true, true );
-        //}//
-
-        ///
-        /// Get node detail
-        /// - retrieve user info
-        /// - handle user privileges
-        ///
-        /// <param name="pNodeId"></param>
-        /// <param name="userId"></param>
-        /// <returns></returns>
-        //public ContentItem GetCurriculumNode( int pNodeId, int userId )
-        //{
-
-        //    ThisUser user = new ThisUser();
-        //    if ( userId > 0 )
-        //    {
-        //        user = AccountServices.GetUser( userId );
-        //    }
-
-        //    return GetCurriculumNode( pNodeId, user, true, true );
-        //}//

         ///
         /// Get node detail
@@ -980,7 +1218,7 @@
             ContentItem entity = new ContentItem();
             string formattedUrl = "";
             string statusMessage = "";
-            string noViewDocLinkTemplate = "{0}
{1}";
+          //  string noViewDocLinkTemplate = "{0}
{1}";
             //string docLinkTemplate = "{1}";
             try
             {
@@ -990,6 +1228,11 @@
                     FormatResourceContent( entity );
                     if ( entity == null )
                         return entity;
+
+					if ( request.IncludeKeywords )
+					{
+						entity.ContentKeywords = MyManager.ContentKeyword_Select( entity.Id );
+					}

                     if ( entity.HasChildItems )
                     {
@@ -1053,119 +1296,127 @@
             return EFManager.Content_RenumberHierarchy( pNodeId );
         }//

-        public bool CloneNodeTags( ThisUser user, int fromNodeId, ContentItem toNode, ref string statusMessage )
-        {
-            ContentItem fromNode = Get(fromNodeId);
-
-            return CloneNodeTags( user, fromNode, toNode, ref statusMessage );
-        }//
-
-        public bool CloneNodeTags( ThisUser user, ContentItem fromNode, ContentItem toNode, ref string statusMessage )
-        {
-            bool isValid = false;
-
-            if ( fromNode.ResourceIntId == 0 )
-            {
-                statusMessage = "Error: the base node doesn't have a resourceId - so will not have tags";
-                return false;
-            }
-
-            if ( toNode.ResourceIntId > 0 )
-            {
-                //not attempting a merge yet, just new
-                statusMessage = "Error: the target node already has a resourceId - not allowing a merge or appending of tags";
-                return false;
-            }
-            string sortTitle = "";
-            int versionID = 0;
-            int intID= 0;
-
-            Resource res = ResourceBizService.Resource_FillSummary( fromNode.ResourceIntId );
-            if ( res != null && res.Id > 0 )
-            {
-                ResourceReplace( res, user, toNode );
-                //re: publish for org, should be same as source. But don't have explicit question
-                PublishingServices.PublishToDatabase( res
-                    ,user.OrgId
-                        , ref isValid
-                        , ref statusMessage
-                        , ref versionID
-                        , ref intID
-                        , ref sortTitle );
-
-                if ( intID > 0 )
-                {
-                    toNode.ResourceIntId = intID;
-                    //toNode.ResourceVersionId = versionID;
-                    Update( toNode );
-                }
-            }
-            return isValid;
-
-        }//
-
-        public void ResourceReplace( Resource resource, Patron user, ContentItem toNode )
-        {
-            resource.Id = 0;
-            resource.IsActive = true;
-            resource.CreatedById = user.Id;
-            resource.Created = DateTime.Now;
-            resource.LastUpdated = DateTime.Now;
-
-            //URL
-            resource.ResourceUrl = FormatContentFriendlyUrl(toNode);
-            resource.Version.ResourceUrl = resource.ResourceUrl;
-
-            //Version Data
-            resource.Version.Id = 0;
-            resource.Version.ResourceIntId = 0;
-            resource.Version.Title = toNode.Title;
-            resource.Version.Description = toNode.Summary;
-            resource.Version.LRDocId = "";
-            resource.Version.IsActive = true;
-
-            resource.Version.Submitter = user.FullName();
-            resource.Version.Creator = user.FullName();
-            resource.Version.Created = DateTime.Now;
-            resource.Version.CreatedById = user.Id;
-            resource.Version.Imported = DateTime.Now;
-            resource.Version.Modified = DateTime.Now;
-            resource.Version.SortTitle = ResourceBizService.FormatFriendlyTitle( toNode.Title );
-            //Keywords
-            foreach ( ResourceChildItem k in resource.Keyword )
-            {
-                k.ResourceIntId = 0;
-                k.CreatedById = user.Id;
-            }
-
-            foreach ( ResourceChildItem s in resource.SubjectMap )
-            {
-                s.ResourceIntId = 0;
-                s.CreatedById = user.Id;
-            }
-
-            foreach ( ResourceChildItem g in resource.Gradelevel )
-            {
-                g.ResourceIntId = 0;
-                g.CreatedById = user.Id;
-            }
-            foreach ( ResourceChildItem c in resource.ClusterMap )
-            {
-                c.ResourceIntId = 0;
-                c.CreatedById = user.Id;
-            }
-            foreach ( ResourceChildItem l in resource.Language )
-            {
-                l.ResourceIntId = 0;
-                l.CreatedById = user.Id;
-            }
+		//public bool CloneNodeTags( ThisUser user, int fromNodeId, ContentItem toNode, ref string statusMessage )
+		//{
+		//	ContentItem fromNode = Get(fromNodeId);
+
+		//	return CloneNodeTags( user, fromNode, toNode, ref statusMessage );
+		//}//
+
+		/////
+		///// INCOMPLETE
+		///// - NEED TO CHANGE TO USE THE LATEST DATABASE PUBLISH
+		/////
+		///// <param name="user"></param>
+		///// <param name="fromNode"></param>
+		///// <param name="toNode"></param>
+		///// <param name="statusMessage"></param>
+		///// <returns></returns>
+		//public bool CloneNodeTags( ThisUser user, ContentItem fromNode, ContentItem toNode, ref string statusMessage )
+		//{
+		//	bool isValid = false;
+
+		//	if ( fromNode.ResourceIntId == 0 )
+		//	{
+		//		statusMessage = "Error: the base node doesn't have a resourceId - so will not have tags";
+		//		return false;
+		//	}
+
+		//	if ( toNode.ResourceIntId > 0 )
+		//	{
+		//		//not attempting a merge yet, just new
+		//		statusMessage = "Error: the target node already has a resourceId - not allowing a merge or appending of tags";
+		//		return false;
+		//	}
+		//	string sortTitle = "";
+		//	int versionID = 0;
+		//	int intID= 0;
+
+		//	Resource res = ResourceBizService.Resource_FillSummary( fromNode.ResourceIntId );
+		//	if ( res != null && res.Id > 0 )
+		//	{
+		//		ResourceReplace( res, user, toNode );
+		//		//re: publish for org, should be same as source. But don't have explicit question
+		//		PublishingServices.PublishToDatabase( res
+		//			,user.OrgId
+		//				, ref isValid
+		//				, ref statusMessage
+		//				, ref versionID
+		//				, ref intID
+		//				, ref sortTitle );
+
+		//		if ( intID > 0 )
+		//		{
+		//			toNode.ResourceIntId = intID;
+		//			Update( toNode );
+		//		}
+		//	}
+		//	return isValid;
+
+		//}//
+
+		//private void ResourceReplace( Resource resource, Patron user, ContentItem toNode )
+		//{
+		//	resource.Id = 0;
+		//	resource.IsActive = true;
+		//	resource.CreatedById = user.Id;
+		//	resource.Created = DateTime.Now;
+		//	resource.LastUpdated = DateTime.Now;
+
+		//	//URL
+		//	resource.ResourceUrl = FormatContentFriendlyUrl(toNode);
+		//	resource.Version.ResourceUrl = resource.ResourceUrl;
+
+		//	//Version Data
+		//	resource.Version.Id = 0;
+		//	resource.Version.ResourceIntId = 0;
+		//	resource.Version.Title = toNode.Title;
+		//	resource.Version.Description = toNode.Summary;
+		//	resource.Version.LRDocId = "";
+		//	resource.Version.IsActive = true;
+
+		//	resource.Version.Submitter = user.FullName();
+		//	resource.Version.Creator = user.FullName();
+		//	resource.Version.Created = DateTime.Now;
+		//	resource.Version.CreatedById = user.Id;
+		//	resource.Version.Imported = DateTime.Now;
+		//	resource.Version.Modified = DateTime.Now;
+		//	resource.Version.SortTitle = ResourceBizService.FormatFriendlyTitle( toNode.Title );
+		//	//Keywords
+		//	foreach ( ResourceChildItem k in resource.Keyword )
+		//	{
+		//		k.ResourceIntId = 0;
+		//		k.CreatedById = user.Id;
+		//	}
+
+		//	foreach ( ResourceChildItem s in resource.SubjectMap )
+		//	{
+		//		s.ResourceIntId = 0;
+		//		s.CreatedById = user.Id;
+		//	}
+
+		//	foreach ( ResourceChildItem g in resource.Gradelevel )
+		//	{
+		//		g.ResourceIntId = 0;
+		//		g.CreatedById = user.Id;
+		//	}
+		//	foreach ( ResourceChildItem c in resource.ClusterMap )
+		//	{
+		//		c.ResourceIntId = 0;
+		//		c.CreatedById = user.Id;
+		//	}
+		//	foreach ( ResourceChildItem l in resource.Language )
+		//	{
+		//		l.ResourceIntId = 0;
+		//		l.CreatedById = user.Id;
+		//	}

-        } //
-
-        public Resource GetNodeTags( int resourceId )
-        {
-            return ResourceBizService.Resource_FillSummary( resourceId );
-        }//
+		//} //
+
+		//public Resource GetNodeTags( int resourceId )
+		//{
+		//	return ResourceBizService.Resource_FillSummary( resourceId );
+		//}//

         ///
         /// Get node detail for download
@@ -1302,7 +1553,30 @@
                 }
             }
         }//
-
+
+		///
+		/// Return the highest sort order value for child items for the provided content Id.
+		/// Returns zero if there are no child items
+		///
+		/// <param name="contentId"></param>
+		/// <param name="contentTypeId"></param>
+		/// <returns></returns>
+		public static int  Content_GetLastChildSortOrder( int contentId, int contentTypeId )
+		{
+			ContentItem item = EFManager.Content_GetLastChild(contentId, contentTypeId);
+			if ( item == null || item.Id == 0 )
+			{
+				//return zero to allow caller to decide to just add 10 to the returned value
+				return 0;	//ContentItem.CONTENT_DEFAULT_SORT_ORDER;
+			}
+			else
+			{
+
+				return item.SortOrder;
+			}
+
+
+		}//
         #endregion
         #region --- helper methods
         ///
@@ -1317,10 +1591,16 @@
             string friendlyTitle = ResourceBizService.FormatFriendlyTitle( entity.Title );
             var siteRoot = UtilityManager.GetAppKeyValue( "siteRoot", "http://ioer.ilsharedlearning.org" );

-            if (entity.TypeId >= ContentItem.CURRICULUM_CONTENT_ID)
-                return UtilityManager.FormatAbsoluteUrl( siteRoot + string.Format( "/learningList/{0}/{1}", entity.Id.ToString(), friendlyTitle ), false );
-            else
-                return UtilityManager.FormatAbsoluteUrl( siteRoot + string.Format( "/Content/{0}/{1}", entity.Id.ToString(), friendlyTitle ), false );
+			if ( entity.TypeId == ContentItem.LEARNING_SET_CONTENT_ID)
+				return UtilityManager.FormatAbsoluteUrl( siteRoot + string.Format( "/learningSet/{0}/{1}", entity.Id.ToString(), friendlyTitle ), false );
+
+			else if (entity.TypeId >= ContentItem.CURRICULUM_CONTENT_ID)
+				return UtilityManager.FormatAbsoluteUrl(siteRoot + string.Format("/learningList/{0}/{1}", entity.Id.ToString(), friendlyTitle), false);
+			else
+			{
+				//TODO - should be elliminating these types
+				return UtilityManager.FormatAbsoluteUrl(siteRoot + string.Format("/Content/{0}/{1}", entity.Id.ToString(), friendlyTitle), false);
+			}
         }
         ///
         /// Format friendly url for a curriculum item
@@ -1348,7 +1628,7 @@
             }
             //chg
             string template1 = "/curriculum/{0}/{1}";
-            string template2 = "/curriculum/{0}/{1}/{2}";
+           // string template2 = "/curriculum/{0}/{1}/{2}";

             return UtilityManager.FormatAbsoluteUrl( siteRoot + string.Format( template1, childId, friendlyTitle ), false );
             //return UtilityManager.FormatAbsoluteUrl( siteRoot + string.Format( "/curriculum/{0}/{1}/{2}", topEntity.Id, childId, friendlyTitle ), false );
@@ -1366,8 +1646,7 @@
             entity.ResourceFriendlyTitle = ResourceBizService.FormatFriendlyTitle( entity.Title );
             if ( entity.ResourceIntId > 0 )
                 entity.ResourceFriendlyUrl = ResourceBizService.FormatFriendlyResourceUrlByResId( entity.Title, entity.ResourceIntId );
-            //else
-            //    entity.ResourceFriendlyUrl = ResourceBizService.FormatFriendlyResourceUrlByRvId( entity.Title, entity.ResourceVersionId );
+
             entity.ResourceThumbnailImageUrl = ResourceBizService.GetResourceThumbnailImageUrl( entity.ResourceFriendlyUrl, entity.ResourceIntId );
         }

@@ -1826,19 +2105,25 @@
         /// <param name="pCreatedById"></param>
         /// <param name="statusMessage"></param>
         /// <returns></returns>
-        public int Content_AddComment( int pContentId, string comment, int pCreatedById, ref string statusMessage )
-        {
+        public int Content_AddComment( int pContentId, string comment, Patron user, ref string statusMessage )
+        {
+			int id = 0;
             //get content, if has resourceId, check for like
             ContentItem entity = Get( pContentId );
             if ( entity != null && entity.ResourceIntId > 0 )
             {
-                return new ResourceManager().Resource_AddComment( entity.ResourceIntId, comment, pCreatedById, ref statusMessage );
+                id = new ResourceManager().Resource_AddComment( entity.ResourceIntId, comment, user.Id, ref statusMessage );
+				if ( id > 0 )
+				{
+					ActivityBizServices.SiteActivityAdd( "Learning List/Set", "Comment", string.Format( "{0} commented on Content ID: {1}", user.FullName(), pContentId ), user.Id, 0, pContentId );
+				}
             }
             else
             {
                 statusMessage = "Error: no related resource was found";
                 return 0;
             }
+			return id;
         }

         ///
@@ -1952,6 +2237,22 @@
         {
            return myMgr.ContentPrivilegeCodes_Select();
         }
+				public List<CodeItem> ContentPrivilegeCodes_SelectList()
+				{
+					var data = ContentPrivilegeCodes_Select();
+					var list = new List<CodeItem>();
+					foreach ( DataRow dr in data.Tables[ 0 ].Rows )
+					{
+						list.Add( new CodeItem()
+						{
+							Id = int.Parse( GetRowColumn( dr, "Id" ) ),
+							Title = GetRowColumn( dr, "Title" ),
+							Description = GetRowColumn( dr, "Description" )
+						} );
+					}
+
+					return list;
+				}
         #endregion

         ///


change proxy's max reading idle time to 5s

--- SharpBroadcast.BroadcastProxy/DataProcess.cs
+++ SharpBroadcast.BroadcastProxy/DataProcess.cs
@@ -926,7 +926,7 @@
             {
                 if (m_Server != null)
                 {
-                    m_Server.SetIdleTime(Session.IO_RECEIVE, 30); // set reading timeout to 30s
+                    m_Server.SetIdleTime(Session.IO_RECEIVE, 5); // set reading timeout to 5s
                     m_Server.Start(m_Port);
                     isServerOK = true;
                 }


override session on change password

override session on change password

--- Signum.Engine.Extensions/ServerExtensions.cs
+++ Signum.Engine.Extensions/ServerExtensions.cs
@@ -81,10 +81,12 @@

         public virtual void ChagePassword(string username, string passwordHash, string newPasswordHash)
         {
-
             try
             {
-                AuthLogic.ChangePassword(username, passwordHash, newPasswordHash);
+                using (ScopeSessionFactory.OverrideSession(session))
+                {
+                    AuthLogic.ChangePassword(username, passwordHash, newPasswordHash);
+                }
             }
             catch (Exception e)
             {


[ci skip] Updating ServerBuildInfo_Dynamic.cs

1.32.4097.20211031202836-master-1d436d8

--- Source/ACE.Server/ServerBuildInfo_Dynamic.cs
+++ Source/ACE.Server/ServerBuildInfo_Dynamic.cs
@@ -4,17 +4,17 @@
     public static partial class ServerBuildInfo
     {
         public static string Branch = "master";
-        public static string Commit = "d713d42ff764b6235057231ebb4b7c47dd173b2c";
+        public static string Commit = "1d436d8a40237989f9e90cc487e4d40e363fffea";

         public static string Version = "1.32";
-        public static string Build   = "4096";
+        public static string Build   = "4097";

         public static int BuildYear   = 2021;
         public static int BuildMonth  = 10;
-        public static int BuildDay    = 28;
-        public static int BuildHour   = 03;
-        public static int BuildMinute = 47;
-        public static int BuildSecond = 55;
+        public static int BuildDay    = 31;
+        public static int BuildHour   = 20;
+        public static int BuildMinute = 28;
+        public static int BuildSecond = 36;
     }
 }


[ci skip] Updating ServerBuildInfo_Dynamic.cs

1.12.3582.20200904081034-master-3e88ff7

--- Source/ACE.Server/ServerBuildInfo_Dynamic.cs
+++ Source/ACE.Server/ServerBuildInfo_Dynamic.cs
@@ -4,17 +4,17 @@
     public static partial class ServerBuildInfo
     {
         public static string Branch = "master";
-        public static string Commit = "32aecfe0cbd9e7d7c832b3543057a0de7689c310";
+        public static string Commit = "3e88ff7df17a7b183c8a614a71b45387cdd1489e";

         public static string Version = "1.12";
-        public static string Build   = "3581";
+        public static string Build   = "3582";

         public static int BuildYear   = 2020;
         public static int BuildMonth  = 09;
         public static int BuildDay    = 04;
-        public static int BuildHour   = 06;
-        public static int BuildMinute = 06;
-        public static int BuildSecond = 49;
+        public static int BuildHour   = 08;
+        public static int BuildMinute = 10;
+        public static int BuildSecond = 34;
     }
 }


[ci skip] Updating ServerBuildInfo_Dynamic.cs

1.15.3747.20201122075020-master-a5f1889

--- Source/ACE.Server/ServerBuildInfo_Dynamic.cs
+++ Source/ACE.Server/ServerBuildInfo_Dynamic.cs
@@ -4,17 +4,17 @@
     public static partial class ServerBuildInfo
     {
         public static string Branch = "master";
-        public static string Commit = "838643e8677934feed2c79d556138ae5d82017ec";
+        public static string Commit = "a5f188966dfa7d3a907cc9318c09f17c983335e3";

         public static string Version = "1.15";
-        public static string Build   = "3746";
+        public static string Build   = "3747";

         public static int BuildYear   = 2020;
         public static int BuildMonth  = 11;
         public static int BuildDay    = 22;
-        public static int BuildHour   = 01;
-        public static int BuildMinute = 28;
-        public static int BuildSecond = 18;
+        public static int BuildHour   = 07;
+        public static int BuildMinute = 50;
+        public static int BuildSecond = 20;
     }
 }


[ci skip] Updating ServerBuildInfo_Dynamic.cs

1.10.3532.20200822205128-master-1c02b1c

--- Source/ACE.Server/ServerBuildInfo_Dynamic.cs
+++ Source/ACE.Server/ServerBuildInfo_Dynamic.cs
@@ -4,17 +4,17 @@
     public static partial class ServerBuildInfo
     {
         public static string Branch = "master";
-        public static string Commit = "d7bf518d7a14e4e65ee254cfe64729b0c02b3a01";
+        public static string Commit = "1c02b1c1bbc5a2b842d0440ad2ae33c61793bed7";

         public static string Version = "1.10";
-        public static string Build   = "3530";
+        public static string Build   = "3532";

         public static int BuildYear   = 2020;
         public static int BuildMonth  = 08;
         public static int BuildDay    = 22;
-        public static int BuildHour   = 05;
-        public static int BuildMinute = 32;
-        public static int BuildSecond = 52;
+        public static int BuildHour   = 20;
+        public static int BuildMinute = 51;
+        public static int BuildSecond = 28;
     }
 }


[ci skip] Updating ServerBuildInfo_Dynamic.cs

1.36.4162.20220120060317-master-0a30505

--- Source/ACE.Server/ServerBuildInfo_Dynamic.cs
+++ Source/ACE.Server/ServerBuildInfo_Dynamic.cs
@@ -4,17 +4,17 @@
     public static partial class ServerBuildInfo
     {
         public static string Branch = "master";
-        public static string Commit = "2956246d80be9a45a2ddec9a44999231c2e5aaab";
+        public static string Commit = "0a305050a4c2ca4c54cf04765edd3a81898dc517";

         public static string Version = "1.36";
-        public static string Build   = "4161";
+        public static string Build   = "4162";

         public static int BuildYear   = 2022;
         public static int BuildMonth  = 01;
         public static int BuildDay    = 20;
-        public static int BuildHour   = 05;
-        public static int BuildMinute = 45;
-        public static int BuildSecond = 09;
+        public static int BuildHour   = 06;
+        public static int BuildMinute = 03;
+        public static int BuildSecond = 17;
     }
 }


(GH-833) Update JetBrains.ReSharper.CommandLineTools reference from 2020.3.3 to 2020.3.4

--- Source/Cake.Recipe/Content/toolsettings.cake
+++ Source/Cake.Recipe/Content/toolsettings.cake
@@ -47,7 +47,7 @@
         string gitReleaseManagerTool = "#tool nuget:?package=GitReleaseManager&version=0.11.0",
         // This is specifically pinned to 5.0.1 as later versions break compatibility with Unix.
         string gitVersionTool = "#tool nuget:?package=GitVersion.CommandLine&version=5.0.1",
-        string reSharperTools = "#tool nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2020.3.3",
+        string reSharperTools = "#tool nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2020.3.4",
         string kuduSyncTool = "#tool nuget:?package=KuduSync.NET&version=1.5.3",
         string wyamTool = "#tool nuget:?package=Wyam&version=2.2.9",
         string xunitTool = "#tool nuget:?package=xunit.runner.console&version=2.4.1",


Added formatting to message string with PrimaryProperty.FriendlyName. This will allow:  BusinessRules.AddRule<Root>(Num1Property, o => o.Num1 > 3, "{0} must be larger than 3"); bugid: 936

--- Source/Csla/Rules/BusinessRulesExtensions.cs
+++ Source/Csla/Rules/BusinessRulesExtensions.cs
@@ -21,16 +21,16 @@
       where T: Csla.Core.BusinessBase
     {
       var rule = new CommonRules.Lambda(primaryProperty, (o) =>
-                                                           {
-                                                             var target = (T) o.Target;
-                                                             using (target.BypassPropertyChecks)
-                                                             {
-                                                               if (!ruleHandler.Invoke(target))
-                                                               {
-                                                                 o.Results.Add(new RuleResult(o.Rule.RuleName, primaryProperty, message) {Severity = severity});
-                                                               }
-                                                             }
-                                                           });
+      {
+        var target = (T) o.Target;
+        using (target.BypassPropertyChecks)
+        {
+          if (!ruleHandler.Invoke(target))
+          {
+            o.Results.Add(new RuleResult(o.Rule.RuleName, primaryProperty, string.Format(message, o.Rule.PrimaryProperty.FriendlyName)) {Severity = severity});
+          }
+        }
+      });
       businessRules.AddRule(rule);
     }

@@ -67,7 +67,7 @@
         {
           if (!ruleHandler.Invoke(target))
           {
-            o.Results.Add(new RuleResult(o.Rule.RuleName, primaryProperty, message) { Severity = severity });
+            o.Results.Add(new RuleResult(o.Rule.RuleName, primaryProperty, string.Format(message, o.Rule.PrimaryProperty.FriendlyName)) { Severity = severity });
           }
         }
       });
@@ -101,16 +101,16 @@
       where T: Csla.Core.BusinessBase
     {
       var rule = new CommonRules.Lambda(primaryProperty, (o) =>
-                                                           {
-                                                             var target = (T) o.Target;
-                                                             using (target.BypassPropertyChecks)
-                                                             {
-                                                               if (!ruleHandler.Invoke(target))
-                                                               {
-                                                                 o.Results.Add(new RuleResult(o.Rule.RuleName, primaryProperty, messageDelegate.Invoke()) { Severity = severity });
-                                                               }
-                                                             }
-                                                           });
+      {
+        var target = (T) o.Target;
+        using (target.BypassPropertyChecks)
+        {
+          if (!ruleHandler.Invoke(target))
+          {
+            o.Results.Add(new RuleResult(o.Rule.RuleName, primaryProperty, messageDelegate.Invoke()) { Severity = severity });
+          }
+        }
+      });
       businessRules.AddRule(rule);
     }


DataMigrationUtility: Added code for automation of schema serialization.

--- Source/Tools/DataMigrationUtility/Program.cs
+++ Source/Tools/DataMigrationUtility/Program.cs
@@ -24,7 +24,11 @@
 //******************************************************************************************************

 using System;
+using System.IO;
+using System.Linq;
 using System.Windows.Forms;
+using GSF;
+using GSF.IO;

 namespace DataMigrationUtility
 {
@@ -34,11 +38,29 @@
         /// The main entry point for the application.
         /// </summary>
         [STAThread]
-        static void Main()
+        static void Main(string[] args)
         {
+            if (args.Any() && args.All(arg => arg != "-install"))
+            {
+                // Code added for automation of schema serialization
+                SerializeSchema(args[0]);
+                return;
+            }
+
             Application.EnableVisualStyles();
             Application.SetCompatibleTextRenderingDefault(false);
             Application.Run(new DataMigrationUtilityScreen());
         }
+
+        static void SerializeSchema(string connectionString)
+        {
+            Schema schema = new Schema(connectionString, TableType.Table);
+
+            using (FileStream stream = new FileStream(FilePath.GetAbsolutePath("SerializedSchema.bin"), FileMode.Create, FileAccess.Write, FileShare.None))
+            {
+                byte[] schemaData = Serialization.Serialize(schema, SerializationFormat.Binary);
+                stream.Write(schemaData, 0, schemaData.Length);
+            }
+        }
     }
 }


Handling for parameters that are binary data instead of arrays.

--- Source/Tools/Generator/MarshallingService.cs
+++ Source/Tools/Generator/MarshallingService.cs
@@ -77,11 +77,18 @@

 		public MarshalBehavior ResolveBehavior(ParameterModel model)
 		{
-			if (model.IndirectionLevel > 0)
+			if (model.SizeParameter != null)
+			{
+				if (model.Flags.HasFlag(ParameterModelFlags.HasElementCount))
+					return MarshalBehavior.Array;
+				else
+					return MarshalBehavior.Indirect;
+			}
+
+			var fallback = ResolveBehavior(model.Type);
+			if (fallback == MarshalBehavior.Direct && model.IndirectionLevel > 0)
 				return MarshalBehavior.Indirect;
-			if (model.SizeParameter != null)
-				return MarshalBehavior.Array;
-			return ResolveBehavior(model.Type);
+			return fallback;
 		}

 		public MarshalBehavior ResolveBehavior(StructureMemberModel model)


Fixed test;

Fixed test;

--- Sources/Taijutsu.Test/LogicContextFixture.cs
+++ Sources/Taijutsu.Test/LogicContextFixture.cs
@@ -26,8 +26,8 @@
     public class LogicContextFixture
     {

-        [SetUp]
-        protected void OnSetUp()
+        [TearDown]
+        protected void OnTearDown()
         {
             LogicContext.Reset();
         }


crypto fix for des-2498

crypto fix for des-2498

--- minuet/symphony/Symphony/Symphony.Crypto/CryptoPlugin.cs
+++ minuet/symphony/Symphony/Symphony.Crypto/CryptoPlugin.cs
@@ -476,20 +476,8 @@
                 Marshal.Copy(OutPtr, Out, 0, System.Convert.ToInt32(OutLen));
                 Marshal.FreeHGlobal(InputPtr);

-                string OutStr;
-
-                // Encode in Base64 if we just encrypted.
-                if (name == "RSAEncrypt")
-                {
-                    OutStr = System.Convert.ToBase64String(Out, 0, System.Convert.ToInt32(OutLen));
-                }
-                else
-                {
-                    OutStr = System.Text.Encoding.UTF8.GetString(Out);
-                }
-
                 // Return the answer.
-                return OutStr;
+                return System.Convert.ToBase64String(Out, 0, System.Convert.ToInt32(OutLen));
             }

             private static IntPtr getRSAKeyFromPEM(string PEMKey)


Update tests/Fractions.Tests/FractionSpecs/Sqrt/Method_Sqrt.cs

Co-authored-by: Daniel Mueller <64b2b6d12bfe4baae7dad3d018f8cbf6b0e7a044@danm.de>

--- tests/Fractions.Tests/FractionSpecs/Sqrt/Method_Sqrt.cs
+++ tests/Fractions.Tests/FractionSpecs/Sqrt/Method_Sqrt.cs
@@ -22,29 +22,23 @@
                         .WithMessage("Cannot calculate square root from a negative number");
         }

-
-        private bool SqrtCompareToDouble(double testValue) {
-
-            var fractionSqrt = Fraction.FromDouble(testValue).Sqrt();
-            var doubleSqrt = Math.Sqrt(testValue);
-
-            return fractionSqrt.ToDouble() == doubleSqrt;
+        private static IEnumerable<TestCaseData> TestCases {
+            get {
+                yield return new TestCaseData(0.001);
+                yield return new TestCaseData(5);
+                yield return new TestCaseData(4.54854751);
+                yield return new TestCaseData(9999999855487);
+                yield return new TestCaseData(99999998554865557);
+                yield return new TestCaseData(Math.PI);
+            }
         }

-        [Test]
-        public void Sqrt_Of_values() {
-
-            SqrtCompareToDouble(0.001).Should().BeTrue();
-            SqrtCompareToDouble(5).Should().BeTrue();
-            SqrtCompareToDouble(4.54854751).Should().BeTrue();
-            SqrtCompareToDouble(9999999855487).Should().BeTrue();
-            SqrtCompareToDouble(99999998554865557).Should().BeTrue();
-            SqrtCompareToDouble(Math.PI).Should().BeTrue();
-
-        }
-
-
-
+        [Test,TestCaseSource(nameof(TestCases))]
+        public void Should_the_result_be_equal_to_Math_Sqrt(double value) {
+            var expected = Math.Sqrt(value);
+            var actual = Fraction.FromDouble(value).Sqrt();
+            actual.ToDouble().Should().Be(expected);
+        }```

     }


Removed unused variable (cherry picked from commit 3bc86484b6d69a1b5768b9e7722d96efa6b11733)

--- DotCMIS/binding/http.cs
+++ DotCMIS/binding/http.cs
@@ -611,7 +611,6 @@
             {
                 return value;
             }
-            string mimeCharset = value.Substring(0, q1);
             int q2 = value.IndexOf('\'', q1 + 1);
             if (q2 == -1)
             {


Trie replace value

Trie replace value

--- Src/BlockchainSharp.Tests/Tries/TrieTests.cs
+++ Src/BlockchainSharp.Tests/Tries/TrieTests.cs
@@ -48,6 +48,24 @@
             Assert.AreEqual("foo", trie3.Get("012"));
             Assert.AreEqual("bar", trie3.Get("abc"));
         }
+
+        [TestMethod]
+        public void ReplaceValue()
+        {
+            Trie<string> trie = new Trie<string>();
+
+            var trie2 = trie.Put("012", "foo");
+            var trie3 = trie2.Put("012", "bar");
+
+            Assert.IsNotNull(trie2);
+            Assert.AreNotSame(trie2, trie);
+            Assert.IsNull(trie.Get("012"));
+            Assert.AreEqual("foo", trie2.Get("012"));
+
+            Assert.IsNotNull(trie3);
+            Assert.AreNotSame(trie3, trie2);
+            Assert.AreEqual("bar", trie3.Get("012"));
+        }
     }
 }


Prevent null exception in MapFile

Prevent null exception in MapFile

--- EOLib.IO/Map/MapFile.cs
+++ EOLib.IO/Map/MapFile.cs
@@ -333,7 +333,7 @@
                 var nextEntityRow = new MapEntityRow<T> {Y = r};

                 var row = m.GetRow(r);
-                if (row.All(x => x.Equals(fillValue)))
+                if (row.All(x => x == null || x.Equals(fillValue)))
                     continue;

                 var mapEntityItems = row.Select((val, x) => new MapEntityItem<T> {X = x, Value = val})


change order and naming [#136060481]

change order and naming [#136060481]

--- revashare-svc-webapi/revashare-svc-webapi.Client/Controllers/DriverController.cs
+++ revashare-svc-webapi/revashare-svc-webapi.Client/Controllers/DriverController.cs
@@ -16,9 +16,20 @@
       this.repo = repo;
     }

+    [HttpPut]
+    [Route("updatevehicle")]
+    public HttpResponseMessage UpdateVehicleInfo([FromBody]VehicleDTO vehicle) {
+      try {
+        return Request.CreateResponse(HttpStatusCode.OK, this.repo.UpdateVehicleInfo(vehicle));
+      }
+      catch (Exception) {
+        return Request.CreateResponse(HttpStatusCode.BadRequest);
+      }
+    }
+
     [HttpPost]
-    [Route("report")]
-    public HttpResponseMessage Post([FromBody]FlagDTO flag) {
+    [Route("reportrider")]
+    public HttpResponseMessage ReportRider([FromBody]FlagDTO flag) {
       try {
         return Request.CreateResponse(HttpStatusCode.OK, this.repo.ReportRider(flag));
       }
@@ -27,11 +38,22 @@
       }
     }

+    [HttpPost]
+    [Route("setavailability")]
+    public HttpResponseMessage SetAvailability([FromBody]RideDTO ride) {
+      try {
+        return Request.CreateResponse(HttpStatusCode.OK, this.repo.SetAvailability(ride));
+      }
+      catch (Exception) {
+        return Request.CreateResponse(HttpStatusCode.BadRequest);
+      }
+    }
+
     [HttpPut]
-    [Route("update/vehicle")]
-    public HttpResponseMessage Put([FromBody]VehicleDTO vehicle) {
+    [Route("updatedriverprofile")]
+    public HttpResponseMessage UpdateDriverProfile([FromBody] UserDTO driver) {
       try {
-        return Request.CreateResponse(HttpStatusCode.OK, this.repo.UpdateVehicleInfo(vehicle));
+        return Request.CreateResponse(HttpStatusCode.OK, this.repo.UpdateDriverProfile(driver));
       }
       catch (Exception) {
         return Request.CreateResponse(HttpStatusCode.BadRequest);
@@ -59,27 +81,5 @@
         return Request.CreateResponse(HttpStatusCode.BadRequest);
       }
     }
-
-    [HttpPost]
-    [Route("addcar")]
-    public HttpResponseMessage AddCar([FromBody]VehicleDTO vehicle) {
-      try {
-        return Request.CreateResponse(HttpStatusCode.OK, this.repo.AddVehicle(vehicle));
-      }
-      catch (Exception) {
-        return Request.CreateResponse(HttpStatusCode.BadRequest);
-      }
-    }
-    // DELETE: api/Driver/5
-    public void Delete(int id) {
-    }
-
-        [HttpPut]
-        [Route("update-driver-profile")]
-        public HttpResponseMessage UpdateDriverProfile([FromBody] UserDTO driver)
-        {
-            return Request.CreateResponse(HttpStatusCode.OK, this.repo.ModifyDriverProfile(driver));
-        }
-
-    }
+  }
 }


(PB3): added JBP decoder.

(PB3): added JBP decoder.

--- ArcFormats/Cmvs/ImagePB3.cs
+++ ArcFormats/Cmvs/ImagePB3.cs
@@ -2,7 +2,7 @@
 //! \date       Wed Dec 02 13:55:45 2015
 //! \brief      Cmvs engine image format.
 //
-// Copyright (C) 2015 by morkt
+// Copyright (C) 2015-2016 by morkt
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to
@@ -27,7 +27,9 @@
 using System;
 using System.ComponentModel.Composition;
 using System.IO;
+using System.Linq;
 using System.Windows.Media;
+using System.Collections.Generic;

 namespace GameRes.Formats.Purple
 {
@@ -99,7 +101,10 @@
             if (info.Type == 1 && info.SubType != 0x10)
                 throw new NotSupportedException();
             m_info = info;
-            m_input = new byte[m_info.InputSize];
+            if (3 == m_info.Type || 2 == m_info.Type)
+                m_input = new byte[input.Length];
+            else
+                m_input = new byte[m_info.InputSize];
             if (m_input.Length != input.Read (m_input, 0, m_input.Length))
                 throw new EndOfStreamException();
             m_channels = m_info.BPP / 8;
@@ -119,11 +124,9 @@
             case 8:
             case 6: UnpackV6(); break;
             case 2:
-            case 3:
+            case 3: UnpackV3(); break;
             case 4:
             case 7: throw new NotSupportedException(string.Format ("PB3 v{0} images not supported", m_info.Type));
-            // V3 is pain in the ass to implement, machine code is full of unrolled loops resulting in a
-            // thousands lines of spaghetti code.
             }
         }

@@ -222,6 +225,52 @@
             }
         }

+        void UnpackV3 ()
+        {
+            var jbp = new JbpReader (m_input, 0x34);
+            m_output = jbp.Unpack();
+            if (m_stride != jbp.Stride)
+            {
+                int src_stride = jbp.Stride;
+                int src = src_stride;
+                int dst = m_stride;
+                for (uint y = 1; y < m_info.Height; ++y)
+                {
+                    Buffer.BlockCopy (m_output, src, m_output, dst, m_stride);
+                    src += src_stride;
+                    dst += m_stride;
+                }
+            }
+            int alpha_pos = LittleEndian.ToInt32 (m_input, 0x2C);
+            if (32 == m_info.BPP && alpha_pos > 0)
+            {
+                int dst = 3;
+                int output_end = m_stride * (int)m_info.Height;
+                while (dst < output_end)
+                {
+                    byte alpha = m_input[alpha_pos++];
+                    if (0 != alpha && 0xFF != alpha)
+                    {
+                        m_output[dst] = alpha;
+                        dst += 4;
+                    }
+                    else
+                    {
+                        int count = m_input[alpha_pos++];
+                        while (count --> 0)
+                        {
+                            m_output[dst] = alpha;
+                            dst += 4;
+                        }
+                    }
+                }
+            }
+            else
+            {
+                Format = PixelFormats.Bgr32;
+            }
+        }
+
         void UnpackV5 ()
         {
             m_output = new byte[m_stride * (int)m_info.Height];
@@ -404,4 +453,440 @@
             }
         }
     }
+
+    internal sealed class JbpReader
+    {
+        byte[]  m_input;
+        byte[]  m_output;
+        int     m_data_pos;
+        uint    m_format;
+
+        int     m_aligned_width;
+        int     m_aligned_height;
+        int     m_stride;
+        int     m_blocks_x;
+        int     m_blocks_y;
+        int     m_dc_bits;
+        int     m_ac_bits;
+
+        public int  Width { get { return m_aligned_width; } }
+        public int Height { get { return m_aligned_height; } }
+        public int Stride { get { return m_stride; } }
+
+        public JbpReader (byte[] input, int offset)
+        {
+            m_input     = input;
+            m_data_pos  = LittleEndian.ToInt32 (input, offset+4) + offset;
+            m_format    = LittleEndian.ToUInt32 (input, offset+8);
+            int width   = LittleEndian.ToUInt16 (input, offset+0x10);
+            int height  = LittleEndian.ToUInt16 (input, offset+0x12);
+            m_dc_bits = LittleEndian.ToInt32 (input, offset+0x1C);
+            m_ac_bits = LittleEndian.ToInt32 (input, offset+0x20);
+
+            switch ((m_format >> 28) & 3)
+            {
+            case 0:
+                m_aligned_width  = (width  + 7) & ~7;
+                m_aligned_height = (height + 7) & ~7;
+                break;
+            case 1:
+                m_aligned_width  = (width  + 0xF) & ~0xF;
+                m_aligned_height = (height + 0xF) & ~0xF;
+                break;
+            case 2:
+                m_aligned_width  = (width  + 0x1F) & ~0x1F;
+                m_aligned_height = (height + 0x0F) & ~0x0F;
+                break;
+            default:
+                throw new InvalidFormatException();
+            }
+            m_blocks_x = m_aligned_width  >> 4;
+            m_blocks_y = m_aligned_height >> 4;
+
+            m_stride = 4 * m_aligned_width;
+            m_output = new byte[m_stride * m_aligned_height];
+        }
+
+        short[] quant_y = new short[0x40];
+        short[] quant_c = new short[0x40];
+
+        JBitStream  bits_dc;
+        JBitStream  bits_ac;
+
+        HuffmanTree tree_dc;
+        HuffmanTree tree_ac;
+
+        public byte[] Unpack ()
+        {
+            var tree_data = new byte[0x10];
+            int tree_pos = m_data_pos+0x80;
+            for (int i = 0; i < 0x10; ++i)
+                tree_data[i] = (byte)(m_input[tree_pos+i] + 1);
+
+            var freq = new uint[0x20];
+            Buffer.BlockCopy (m_input, m_data_pos, freq, 0, 0x40);
+            tree_dc = new HuffmanTree (tree_data, freq);
+            Buffer.BlockCopy (m_input, m_data_pos+0x40, freq, 0, 0x40);
+            tree_ac = new HuffmanTree (tree_data, freq);
+
+            int quant_pos = tree_pos+0x10;
+            if (0 != (m_format & 0x8000000))
+            {
+                for (int i = 0; i < 0x40; ++i)
+                {
+                    quant_y[i] = m_input[quant_pos+i];
+                    quant_c[i] = m_input[quant_pos+i+0x40];
+                }
+            }
+            int bits_offset = quant_pos + 0x80;
+            bits_dc = new JBitStream (m_input, bits_offset,             m_dc_bits);
+            bits_ac = new JBitStream (m_input, bits_offset + m_dc_bits, m_ac_bits);
+
+            Decode();
+            return m_output;
+        }
+
+        static byte[] ZigzagOrder = new byte[64]
+        {
+             1,  8,  16, 9,  2,  3, 10, 17,
+            24, 32, 25, 18, 11,  4,  5, 12,
+            19, 26, 33, 40, 48, 41, 34, 27,
+            20, 13,  6,  7, 14, 21, 28, 35,
+            42, 49, 56, 57, 50, 43, 36, 29,
+            22, 15, 23, 30, 37, 44, 51, 58,
+            59, 52, 45, 38, 31, 39, 46, 53,
+            60, 61, 54, 47, 55, 62, 63,  0
+        };
+
+        void Decode ()
+        {
+            int total_blocks = m_blocks_x * m_blocks_y;
+            var blocks = new short[total_blocks, 6];
+            uint prev_v = 0;
+            for (int i = 0; i < total_blocks; ++i)
+            for (int j = 0; j < 6; ++j)
+            {
+                int bit_count = tree_dc.Read (bits_dc);
+                uint v = (uint)bits_dc.GetBits (bit_count);
+                if (v < (1u << (bit_count - 1)))
+                    v -= (1u << bit_count) - 1;
+
+                prev_v += v;
+                blocks[i,j] = (short)prev_v;
+            }
+
+            var dct_table = new short[6][];
+            for (int i = 0; i < 6; ++i)
+                dct_table[i] = new short[64];
+
+            for (int y = 0; y < m_blocks_y; ++y)
+            {
+                int dst1 = y * m_stride * 16;
+                int dst2 = dst1 + m_stride * 9;
+
+                for (int x = 0; x < m_blocks_x; ++x)
+                {
+                    for (int j = 0; j < 6; ++j)
+                    for (int k = 0; k < 64; ++k)
+                        dct_table[j][k] = 0;
+
+                    for (int n = 0; n < 6; ++n)
+                    {
+                        dct_table[n][0] = blocks[y * m_blocks_x + x, n];
+
+                        for (int i = 0; i < 63;)
+                        {
+                            int bit_count = tree_ac.Read (bits_ac);
+
+                            if (15 == bit_count)
+                                break;
+
+                            if (0 == bit_count)
+                            {
+                                int node_idx = 0;
+                                while (0 != bits_ac.GetNextBit())
+                                    node_idx++;
+                                i += tree_ac.Base[node_idx];
+                            }
+                            else
+                            {
+                                uint v = (uint)bits_ac.GetBits (bit_count);
+                                if (v < (1u << (bit_count - 1)))
+                                    v -= (1u << bit_count) - 1;
+                                dct_table[n][ZigzagOrder[i]] = (short)v;
+                                ++i;
+                            }
+                        }
+                    }
+
+                    Dct (dct_table[0], quant_y);
+                    Dct (dct_table[1], quant_y);
+                    Dct (dct_table[2], quant_y);
+                    Dct (dct_table[3], quant_y);
+                    Dct (dct_table[4], quant_c);
+                    Dct (dct_table[5], quant_c);
+
+                    Ycc2Rgb (dst1,             dst1+m_stride,    dct_table[0], dct_table[4], dct_table[5], 0);
+                    Ycc2Rgb (dst1+32,          dst1+m_stride+32, dct_table[1], dct_table[4], dct_table[5], 4);
+                    Ycc2Rgb (dst2-m_stride,    dst2,             dct_table[2], dct_table[4], dct_table[5], 32);
+                    Ycc2Rgb (dst2-m_stride+32, dst2+32,          dct_table[3], dct_table[4], dct_table[5], 36);
+
+                    dst1 += 64;
+                    dst2 += 64;
+                }
+            }
+        }
+
+        void Dct (short[] dct_table, short[] quant)
+        {
+            int a, b, c, d;
+            int w, x, y, z;
+            int s, t, u, v, n;
+
+            int p = 0;
+            int q = 0;
+
+            for (int i = 0; i < 8; ++i)
+            {
+                if (dct_table[p+0x08] == 0 && dct_table[p+0x10] == 0 &&
+                    dct_table[p+0x18] == 0 && dct_table[p+0x20] == 0 &&
+                    dct_table[p+0x28] == 0 && dct_table[p+0x30] == 0 &&
+                    dct_table[p+0x38] == 0)
+                {
+                    dct_table[p]      = dct_table[p+0x08] =
+                    dct_table[p+0x10] = dct_table[p+0x18] =
+                    dct_table[p+0x20] = dct_table[p+0x28] =
+                    dct_table[p+0x30] = dct_table[p+0x38] = (short)(dct_table[p] * quant[q]);
+                }
+                else
+                {
+                    c = quant[q+0x10] * dct_table[p+0x10];
+                    d = quant[q+0x30] * dct_table[p+0x30];
+                    x = ((c + d) * 35467) >> 16;
+                    c = ((c * 50159) >> 16) + x;
+                    d = ((d * -121094) >> 16) + x;
+                    a = dct_table[p+0x00] * quant[q+0x00];
+                    b = dct_table[p+0x20] * quant[q+0x20];
+                    w = a + b + c;
+                    x = a + b - c;
+                    y = a - b + d;
+                    z = a - b - d;
+
+                    c = dct_table[p+0x38] * quant[q+0x38];
+                    d = dct_table[p+0x28] * quant[q+0x28];
+                    a = dct_table[p+0x18] * quant[q+0x18];
+                    b = dct_table[p+0x08] * quant[q+0x08];
+                    n = ((a + b + c + d) * 77062) >> 16;
+
+                    u = n + ((c * 19571) >> 16)  + (((c + a) * -128553) >> 16) + (((c + b) * -58980) >> 16);
+                    v = n + ((d * 134553) >> 16) + (((d + b) * -25570) >> 16)  + (((d + a) * -167963) >> 16);
+                    t = n + ((b * 98390) >> 16)  + (((d + b) * -25570) >> 16)  + (((c + b) * -58980) >> 16);
+                    s = n + ((a * 201373) >> 16) + (((c + a) * -128553) >> 16) + (((d + a) * -167963) >> 16);
+
+                    dct_table[p]      = (short)(w + t);
+                    dct_table[p+0x38] = (short)(w - t);
+                    dct_table[p+0x08] = (short)(y + s);
+                    dct_table[p+0x30] = (short)(y - s);
+                    dct_table[p+0x10] = (short)(z + v);
+                    dct_table[p+0x28] = (short)(z - v);
+                    dct_table[p+0x18] = (short)(x + u);
+                    dct_table[p+0x20] = (short)(x - u);
+                }
+                p++;
+                q++;
+            }
+
+            p = 0;
+            for (int i = 0; i < 8; ++i)
+            {
+                a = dct_table[p];
+                c = dct_table[p+2];
+                b = dct_table[p+4];
+                d = dct_table[p+6];
+                x = ((c + d) * 35467) >> 16;
+                c = ((c * 50159) >> 16) + x;
+                d = ((d * -121094) >> 16) + x;
+                w = a + b + c;
+                x = a + b - c;
+                y = a - b + d;
+                z = a - b - d;
+
+                d = dct_table[p+5];
+                b = dct_table[p+1];
+                c = dct_table[p+7];
+                a = dct_table[p+3];
+                n = ((a + b + c + d) * 77062) >> 16;
+
+                s = n + ((a * 201373) >> 16) + (((a + c) * -128553) >> 16) + (((a + d) * -167963) >> 16);
+                t = n + ((b * 98390) >> 16)  + (((b + c) * -58980) >> 16)  + (((b + d) * -25570) >> 16);
+                u = n + ((c * 19571) >> 16)  + (((b + c) * -58980) >> 16)  + (((a + c) * -128553) >> 16);
+                v = n + ((d * 134553) >> 16) + (((b + d) * -25570) >> 16)  + (((a + d) * -167963) >> 16);
+
+                dct_table[p  ] = (short)((w + t) >> 3);
+                dct_table[p+7] = (short)((w - t) >> 3);
+                dct_table[p+1] = (short)((y + s) >> 3);
+                dct_table[p+6] = (short)((y - s) >> 3);
+                dct_table[p+2] = (short)((z + v) >> 3);
+                dct_table[p+5] = (short)((z - v) >> 3);
+                dct_table[p+3] = (short)((x + u) >> 3);
+                dct_table[p+4] = (short)((x - u) >> 3);
+
+                p += 8;
+            }
+        }
+
+        void Ycc2Rgb (int dc, int ac, short[] dct_y, short[] dct_cb, short[] dct_cr, int cbcr_src)
+        {
+            int y_src = 0;
+            for (int y = 0; y < 4; ++y)
+            {
+                for (int x = 0; x < 4; ++x)
+                {
+                    var cb = dct_cb[cbcr_src];
+                    var cr = dct_cr[cbcr_src];
+                    var r = ((cr * 0x166F0) >> 16);
+                    var g = ((cb * 0x5810) >> 16) + ((cr * 0xB6C0) >> 16);
+                    var b = ((cb * 0x1C590) >> 16);
+                    var c0 = dct_y[y_src  ] + 0x180;
+                    var c1 = dct_y[y_src+1] + 0x180;
+                    var c8 = dct_y[y_src+8] + 0x180;
+                    var c9 = dct_y[y_src+9] + 0x180;
+
+                    m_output[dc]            = Clamp (c0 + b);
+                    m_output[ac+1-m_stride] = Clamp (c0 - g);
+                    m_output[ac+2-m_stride] = Clamp (c0 + r);
+                    m_output[ac+4-m_stride] = Clamp (c1 + b);
+                    m_output[ac+5-m_stride] = Clamp (c1 - g);
+                    m_output[ac+6-m_stride] = Clamp (c1 + r);
+                    m_output[ac]            = Clamp (c8 + b);
+                    m_output[ac+1]          = Clamp (c8 - g);
+                    m_output[ac+2]          = Clamp (c8 + r);
+                    m_output[ac+4]          = Clamp (c9 + b);
+                    m_output[ac+5]          = Clamp (c9 - g);
+                    m_output[ac+6]          = Clamp (c9 + r);
+                    y_src += 2;
+                    dc += 8;
+                    ac += 8;
+                    cbcr_src++;
+                }
+
+                dc += m_stride * 2 - 32;
+                ac += m_stride * 2 - 32;
+
+                y_src += 8;
+                cbcr_src += 4;
+            }
+        }
+
+        static byte[] YccClampTable = Enumerable.Repeat<byte> (0, 0x100)
+            .Concat (Enumerable.Range (0, 0x100).Select (x => (byte)x))
+            .Concat (Enumerable.Repeat<byte> (0xFF, 0x100)).ToArray();
+
+        static byte Clamp (int c)
+        {
+            return YccClampTable[c];
+        }
+
+        internal class HuffmanTree
+        {
+            public  byte[]  Base;
+            private int[]   Nodes = new int[0x400];
+            private int     Root;
+            public  int     LeafCount { get { return Base.Length; } }
+
+            const uint      MaxFreq = 2100000000u;
+
+            public HuffmanTree (byte[] input, uint[] freq)
+            {
+                Base = input;
+                int depth = Base.Length;
+                for (;;)
+                {
+                    int l = -1;
+                    uint min = MaxFreq - 1;
+                    for (int i = 0; i < depth; ++i)
+                    {
+                        if (freq[i] < min)
+                        {
+                            min = freq[i];
+                            l = i;
+                        }
+                    }
+
+                    int r = -1;
+                    min = MaxFreq - 1;
+                    for (int i = 0; i < depth; ++i)
+                    {
+                        if ((i != l) && (freq[i] < min))
+                        {
+                            min = freq[i];
+                            r = i;
+                        }
+                    }
+
+                    if (l < 0 || r < 0)
+                        break;
+
+                    Nodes[depth] = l;
+                    Nodes[depth+0x200] = r;
+
+                    freq[depth++] = freq[l] + freq[r];
+                    freq[l] = MaxFreq;
+                    freq[r] = MaxFreq;
+                }
+                Root = depth - 1;
+            }
+
+            public int Read (JBitStream bits)
+            {
+                int v = Root;
+                while (v >= LeafCount)
+                    v = Nodes[v + (bits.GetNextBit() << 9)];
+                return v;
+            }
+        }
+
+        internal class JBitStream
+        {
+            byte[]      m_input;
+            int         m_pos;
+            int         m_end;
+
+            public JBitStream (byte[] input, int offset, int length)
+            {
+                m_input = input;
+                m_pos   = offset;
+                m_end   = offset + length;
+            }
+
+            int         m_bits = 0;
+            int         m_cached_bits = 0;
+
+            public int GetBits (int count)
+            {
+                while (m_cached_bits < count)
+                {
+                    if (m_pos >= m_end)
+                        throw new EndOfStreamException();
+                    m_bits = (m_bits << 8) | ReverseByteBits (m_input[m_pos++]);
+                    m_cached_bits += 8;
+                }
+                int mask = (1 << count) - 1;
+                m_cached_bits -= count;
+                return (m_bits >> m_cached_bits) & mask;
+            }
+
+            public int GetNextBit ()
+            {
+                return GetBits (1);
+            }
+
+            int ReverseByteBits (int x)
+            {
+                x = (x & 0xAA) >> 1 | (x & 0x55) << 1;
+                x = (x & 0xCC) >> 2 | (x & 0x33) << 2;
+                return (x >> 4 | x << 4) & 0xFF;
+            }
+        }
+    }
 }


cleaning

cleaning


(Rip007): implemented RGBA decoding.

(Rip007): implemented RGBA decoding.

--- ArcFormats/rUGP/ImageRIP.cs
+++ ArcFormats/rUGP/ImageRIP.cs
@@ -43,6 +43,8 @@
         public override string Description { get { return "rUGP compressed image format"; } }
         public override uint     Signature { get { return 0; } }

+        // signature set to 0 because all serialized rUGP objects have same signature.
+
         public override ImageMetaData ReadMetaData (IBinaryStream file)
         {
             if (file.Signature != CRioArchive.ObjectSignature)
@@ -84,31 +86,34 @@
         int      Version;
         int      m_width;
         int      m_height;
+        int      m_x;
+        int      m_y;
+        int      m_w;
+        int      m_h;
         byte[]   CompressInfo;
-        byte[]   field_30;
-        int      field_38;
-        int      field_3C;
+        int      m_flags;
         CObject  field_4C;
         byte[]   m_pixels;

         public PixelFormat Format { get; private set; }
         public byte[]      Pixels { get { return m_pixels; } }
-        public bool      HasAlpha { get { return ((field_38 & 0xFF) - 2) == 1; } }
+        public bool      HasAlpha { get { return ((m_flags & 0xFF) - 2) == 1; } }

         public override void Deserialize (CRioArchive arc)
         {
             Version = arc.ReadInt32();
             m_width = arc.ReadUInt16();
             m_height = arc.ReadUInt16();
-            field_30 = arc.ReadBytes (4);
-            arc.ReadUInt16();
-            arc.ReadUInt16();
-            field_38 = arc.ReadInt32();
+            m_x = arc.ReadUInt16();
+            m_y = arc.ReadUInt16();
+            m_w = arc.ReadUInt16();
+            m_h = arc.ReadUInt16();
+            m_flags = arc.ReadInt32();
             CompressInfo = arc.ReadBytes (7);
             if (arc.GetObjectSchema() >= 2)
                 field_4C = arc.ReadRioReference ("CSbm");
             int size = arc.ReadInt32();
-            field_3C = arc.ReadInt32();
+            arc.ReadInt32(); // field_3C
             var data = arc.ReadBytes (size);
             m_pixels = Uncompress (data);
             Format = HasAlpha ? PixelFormats.Bgra32 : PixelFormats.Bgr32;
@@ -148,50 +153,31 @@
                     int x = 0;
                     while (x < m_width)
                     {
-                        int count = 1;
-                        while (input.GetNextBit() > 0)
-                        {
-                            count <<= 1;
-                            count |= input.GetNextBit();
-                        }
+                        int count = GetInt (input);
                         x += count;
                         do
                         {
                             if (input.GetNextBit() > 0)
                             {
                                 rgb = LittleEndian.ToInt32 (output, dst - stride);
+                                if (rgb != 0)
+                                {
+                                    rgb -= baseline;
+                                }
                                 rgb_ = rgb;
-                                if (rgb != 0)
-                                {
-                                    rgb -= baseline;
-                                    rgb_ = rgb;
-                                }
                             }
                             else
                             {
                                 int r = 0, g = 0, b = 0;
                                 if (input.GetNextBit() > 0)
                                 {
-                                    g = 1;
+                                    g = GetSigned (input);
+                                }
+                                int b_inc = 0;
+                                if (input.GetNextBit() > 0)
+                                {
                                     bool sign = input.GetNextBit() > 0;
-                                    while (input.GetNextBit() > 0)
-                                    {
-                                        g <<= 1;
-                                        g |= input.GetNextBit();
-                                    }
-                                    if (sign)
-                                        g = -g;
-                                }
-                                int b_inc = 0;
-                                if (input.GetNextBit() > 0)
-                                {
-                                    int v = 1;
-                                    bool sign = input.GetNextBit() > 0;
-                                    while (input.GetNextBit() > 0)
-                                    {
-                                        v <<= 1;
-                                        v |= input.GetNextBit();
-                                    }
+                                    int v = GetInt (input);
                                     b_inc = tblQuantTransfer[q, v];
                                     if (sign)
                                         b_inc = -b_inc;
@@ -199,13 +185,8 @@
                                 int r_inc = 0;
                                 if (input.GetNextBit() > 0)
                                 {
-                                    int v = 1;
                                     bool sign = input.GetNextBit() > 0;
-                                    while (input.GetNextBit() > 0)
-                                    {
-                                        v <<= 1;
-                                        v |= input.GetNextBit();
-                                    }
+                                    int v = GetInt (input);
                                     r_inc = tblQuantTransfer[q, v];
                                     if (sign)
                                         r_inc = -r_inc;
@@ -215,21 +196,21 @@
                                     gg >>= 1;
                                 if (CompressInfo[3] != 0)
                                 {
-                                    int c1 = (0xFF >> b_shift) & (rgb_ >> b_shift);
-                                    int c2 = (0xFF >> b_shift) - c1;
+                                    int c1 = (0xFF >> b_shift) & (int)((uint)rgb_ >> b_shift);
                                     c1 = -c1;
                                     if (gg >= c1)
                                     {
+                                        int c2 = (0xFF >> b_shift) + c1;
                                         c1 = c2;
                                         if (gg <= c2)
                                             c1 = gg;
                                     }
                                     b = c1 + b_inc;
-                                    c1 = (0xFF0000 >> r_shift) & (rgb_ >> r_shift);
-                                    c2 = (0xFF0000 >> r_shift) - c1;
+                                    c1 = (0xFF0000 >> r_shift) & (int)((uint)rgb_ >> r_shift);
                                     c1 = -c1;
                                     if (gg >= c1)
                                     {
+                                        int c2 = (0xFF0000 >> r_shift) + c1;
                                         c1 = c2;
                                         if (gg <= c2)
                                             c1 = gg;
@@ -254,12 +235,7 @@
                         if (x >= m_width)
                             break;

-                        count = 1;
-                        while (input.GetNextBit() > 0)
-                        {
-                            count <<= 1;
-                            count |= input.GetNextBit();
-                        }
+                        count = GetInt (input);
                         x += count;
                         while (count --> 0)
                         {
@@ -273,7 +249,140 @@

         void UncompressRgba (IBitStream input, byte[] output)
         {
-            throw new NotImplementedException();
+            int stride = 4 * m_width;
+            int q = CompressInfo[0];
+            int b_bits = CompressInfo[4];
+            int g_bits = CompressInfo[5];
+            int r_bits = CompressInfo[6];
+            int b_shift = 8 - b_bits;
+            int g_shift = 16 - g_bits;
+            int r_shift = 24 - r_bits;
+            int dst_pos = m_y * stride + m_x * 4;
+            int baseline = 0xFF >> b_bits | (0xFF >> g_bits | (0xFF >> r_bits << 8)) << 8;
+            var line_buf = new int[m_w];
+            for (int y = 0; y < m_h; ++y)
+            {
+                int alpha = 0;
+                int rgb = 0;
+                int repeat_count = 0;
+                bool repeat = true;
+                int dst = dst_pos + y * stride;
+                int x = 0;
+                int chunk_size = 0;
+                while (x < m_w)
+                {
+                    if (0 == chunk_size)
+                    {
+                        int alpha_inc = 0;
+                        if (input.GetNextBit() > 0)
+                        {
+                            alpha_inc = GetSigned (input);
+                        }
+                        alpha += alpha_inc;
+                        if (0 == alpha || 31 == alpha)
+                        {
+                            chunk_size = GetInt (input);
+                        }
+                    }
+                    if (alpha != 0)
+                    {
+                        if (31 == alpha)
+                            --chunk_size;
+                        if (0 == repeat_count)
+                        {
+                            repeat_count = GetInt (input);
+                            repeat = !repeat;
+                        }
+                        --repeat_count;
+                        if (!repeat)
+                        {
+                            if (input.GetNextBit() > 0)
+                            {
+                                rgb = line_buf[x];
+                            }
+                            else
+                            {
+                                int g = 0;
+                                if (input.GetNextBit() > 0)
+                                {
+                                    g = GetSigned (input);
+                                }
+                                int b_inc = 0;
+                                if (input.GetNextBit() > 0)
+                                {
+                                    bool sign = input.GetNextBit() > 0;
+                                    int v = GetInt (input);
+                                    b_inc = tblQuantTransfer[q, v];
+                                    if (sign)
+                                        b_inc = -b_inc;
+                                }
+                                int r_inc = 0;
+                                if (input.GetNextBit() > 0)
+                                {
+                                    bool sign = input.GetNextBit() > 0;
+                                    int v = GetInt (input);
+                                    r_inc = tblQuantTransfer[q, v];
+                                    if (sign)
+                                        r_inc = -r_inc;
+                                }
+                                int c1 = (0xFF >> b_shift) & (int)((uint)rgb >> b_shift);
+                                c1 = -c1;
+                                if (g >= c1)
+                                {
+                                    int c2 = (0xFF >> b_shift) + c1;
+                                    c1 = g;
+                                    if (g > c2)
+                                        c1 = c2;
+                                }
+                                int b = c1 + b_inc;
+                                c1 = (0xFF0000 >> r_shift) & (int)((uint)rgb >> r_shift);
+                                c1 = -c1;
+                                if (g >= c1)
+                                {
+                                    int c2 = (0xFF0000 >> r_shift) + c1;
+                                    c1 = g;
+                                    if (g > c2)
+                                        c1 = c2;
+                                }
+                                int r = c1 + r_inc;
+                                rgb += (b << b_shift) + (r << r_shift) + (g << g_shift);
+                            }
+                        }
+                        uint pixel = (uint)(baseline + rgb);
+                        if (31 == alpha)
+                            pixel |= 0xFF000000u;
+                        else
+                            pixel |= (uint)(alpha << 27);
+                        LittleEndian.Pack (pixel, output, dst);
+                        dst += 4;
+                        line_buf[x++] = rgb;
+                    }
+                    else
+                    {
+                        dst += 4 * chunk_size;
+                        x += chunk_size;
+                        chunk_size = 0;
+                    }
+                }
+            }
+        }
+
+        static int GetInt (IBitStream input)
+        {
+            int n = 1;
+            while (input.GetNextBit() > 0)
+            {
+                n <<= 1;
+                n |= input.GetNextBit();
+            }
+            return n;
+        }
+
+        static int GetSigned (IBitStream input)
+        {
+            bool sign = input.GetNextBit() > 0;
+            int n = GetInt (input);
+            return sign ? -n : n;
         }

         static readonly byte[,] tblQuantTransfer = {