Fix give inventory tests to use different users rather than (accidentally) the same user. Extend TestGiveInventoryItem() to test giving back the same item.
parent
fe890554fb
commit
12b1cbf8bf
|
@ -207,6 +207,17 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver
|
|||
return null;
|
||||
}
|
||||
|
||||
public static List<InventoryItemBase> FindItemsByPath(
|
||||
IInventoryService inventoryService, UUID userId, string path)
|
||||
{
|
||||
InventoryFolderBase rootFolder = inventoryService.GetRootFolder(userId);
|
||||
|
||||
if (null == rootFolder)
|
||||
return new List<InventoryItemBase>();
|
||||
|
||||
return FindItemsByPath(inventoryService, rootFolder, path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find items that match a given PATH_DELIMITOR delimited path starting from this folder.
|
||||
/// </summary>
|
||||
|
|
|
@ -425,192 +425,198 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
InventoryItemBase item = new InventoryItemBase(itemId, senderId);
|
||||
item = InventoryService.GetItem(item);
|
||||
|
||||
if ((item != null) && (item.Owner == senderId))
|
||||
if (item == null)
|
||||
{
|
||||
IUserManagement uman = RequestModuleInterface<IUserManagement>();
|
||||
if (uman != null)
|
||||
uman.AddUser(item.CreatorIdAsUuid, item.CreatorData);
|
||||
|
||||
if (!Permissions.BypassPermissions())
|
||||
{
|
||||
if ((item.CurrentPermissions & (uint)PermissionMask.Transfer) == 0)
|
||||
return null;
|
||||
}
|
||||
|
||||
// Insert a copy of the item into the recipient
|
||||
InventoryItemBase itemCopy = new InventoryItemBase();
|
||||
itemCopy.Owner = recipient;
|
||||
itemCopy.CreatorId = item.CreatorId;
|
||||
itemCopy.CreatorData = item.CreatorData;
|
||||
itemCopy.ID = UUID.Random();
|
||||
itemCopy.AssetID = item.AssetID;
|
||||
itemCopy.Description = item.Description;
|
||||
itemCopy.Name = item.Name;
|
||||
itemCopy.AssetType = item.AssetType;
|
||||
itemCopy.InvType = item.InvType;
|
||||
itemCopy.Folder = recipientFolderId;
|
||||
|
||||
if (Permissions.PropagatePermissions() && recipient != senderId)
|
||||
{
|
||||
// Trying to do this right this time. This is evil. If
|
||||
// you believe in Good, go elsewhere. Vampires and other
|
||||
// evil creatores only beyond this point. You have been
|
||||
// warned.
|
||||
|
||||
// We're going to mask a lot of things by the next perms
|
||||
// Tweak the next perms to be nicer to our data
|
||||
//
|
||||
// In this mask, all the bits we do NOT want to mess
|
||||
// with are set. These are:
|
||||
//
|
||||
// Transfer
|
||||
// Copy
|
||||
// Modufy
|
||||
uint permsMask = ~ ((uint)PermissionMask.Copy |
|
||||
(uint)PermissionMask.Transfer |
|
||||
(uint)PermissionMask.Modify);
|
||||
|
||||
// Now, reduce the next perms to the mask bits
|
||||
// relevant to the operation
|
||||
uint nextPerms = permsMask | (item.NextPermissions &
|
||||
((uint)PermissionMask.Copy |
|
||||
(uint)PermissionMask.Transfer |
|
||||
(uint)PermissionMask.Modify));
|
||||
|
||||
// nextPerms now has all bits set, except for the actual
|
||||
// next permission bits.
|
||||
|
||||
// This checks for no mod, no copy, no trans.
|
||||
// This indicates an error or messed up item. Do it like
|
||||
// SL and assume trans
|
||||
if (nextPerms == permsMask)
|
||||
nextPerms |= (uint)PermissionMask.Transfer;
|
||||
|
||||
// Inventory owner perms are the logical AND of the
|
||||
// folded perms and the root prim perms, however, if
|
||||
// the root prim is mod, the inventory perms will be
|
||||
// mod. This happens on "take" and is of little concern
|
||||
// here, save for preventing escalation
|
||||
|
||||
// This hack ensures that items previously permalocked
|
||||
// get unlocked when they're passed or rezzed
|
||||
uint basePerms = item.BasePermissions |
|
||||
(uint)PermissionMask.Move;
|
||||
uint ownerPerms = item.CurrentPermissions;
|
||||
|
||||
// If this is an object, root prim perms may be more
|
||||
// permissive than folded perms. Use folded perms as
|
||||
// a mask
|
||||
if (item.InvType == (int)InventoryType.Object)
|
||||
{
|
||||
// Create a safe mask for the current perms
|
||||
uint foldedPerms = (item.CurrentPermissions & 7) << 13;
|
||||
foldedPerms |= permsMask;
|
||||
|
||||
bool isRootMod = (item.CurrentPermissions &
|
||||
(uint)PermissionMask.Modify) != 0 ?
|
||||
true : false;
|
||||
|
||||
// Mask the owner perms to the folded perms
|
||||
ownerPerms &= foldedPerms;
|
||||
basePerms &= foldedPerms;
|
||||
|
||||
// If the root was mod, let the mask reflect that
|
||||
// We also need to adjust the base here, because
|
||||
// we should be able to edit in-inventory perms
|
||||
// for the root prim, if it's mod.
|
||||
if (isRootMod)
|
||||
{
|
||||
ownerPerms |= (uint)PermissionMask.Modify;
|
||||
basePerms |= (uint)PermissionMask.Modify;
|
||||
}
|
||||
}
|
||||
|
||||
// These will be applied to the root prim at next rez.
|
||||
// The slam bit (bit 3) and folded permission (bits 0-2)
|
||||
// are preserved due to the above mangling
|
||||
ownerPerms &= nextPerms;
|
||||
|
||||
// Mask the base permissions. This is a conservative
|
||||
// approach altering only the three main perms
|
||||
basePerms &= nextPerms;
|
||||
|
||||
// Assign to the actual item. Make sure the slam bit is
|
||||
// set, if it wasn't set before.
|
||||
itemCopy.BasePermissions = basePerms;
|
||||
itemCopy.CurrentPermissions = ownerPerms;
|
||||
itemCopy.Flags |= (uint)InventoryItemFlags.ObjectSlamPerm;
|
||||
|
||||
itemCopy.NextPermissions = item.NextPermissions;
|
||||
|
||||
// This preserves "everyone can move"
|
||||
itemCopy.EveryOnePermissions = item.EveryOnePermissions &
|
||||
nextPerms;
|
||||
|
||||
// Intentionally killing "share with group" here, as
|
||||
// the recipient will not have the group this is
|
||||
// set to
|
||||
itemCopy.GroupPermissions = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemCopy.CurrentPermissions = item.CurrentPermissions;
|
||||
itemCopy.NextPermissions = item.NextPermissions;
|
||||
itemCopy.EveryOnePermissions = item.EveryOnePermissions & item.NextPermissions;
|
||||
itemCopy.GroupPermissions = item.GroupPermissions & item.NextPermissions;
|
||||
itemCopy.BasePermissions = item.BasePermissions;
|
||||
}
|
||||
|
||||
if (itemCopy.Folder == UUID.Zero)
|
||||
{
|
||||
InventoryFolderBase folder = InventoryService.GetFolderForType(recipient, (AssetType)itemCopy.AssetType);
|
||||
|
||||
if (folder != null)
|
||||
{
|
||||
itemCopy.Folder = folder.ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
InventoryFolderBase root = InventoryService.GetRootFolder(recipient);
|
||||
|
||||
if (root != null)
|
||||
itemCopy.Folder = root.ID;
|
||||
else
|
||||
return null; // No destination
|
||||
}
|
||||
}
|
||||
|
||||
itemCopy.GroupID = UUID.Zero;
|
||||
itemCopy.GroupOwned = false;
|
||||
itemCopy.Flags = item.Flags;
|
||||
itemCopy.SalePrice = item.SalePrice;
|
||||
itemCopy.SaleType = item.SaleType;
|
||||
|
||||
if (AddInventoryItem(itemCopy))
|
||||
{
|
||||
IInventoryAccessModule invAccess = RequestModuleInterface<IInventoryAccessModule>();
|
||||
if (invAccess != null)
|
||||
invAccess.TransferInventoryAssets(itemCopy, senderId, recipient);
|
||||
}
|
||||
|
||||
if (!Permissions.BypassPermissions())
|
||||
{
|
||||
if ((item.CurrentPermissions & (uint)PermissionMask.Copy) == 0)
|
||||
{
|
||||
List<UUID> items = new List<UUID>();
|
||||
items.Add(itemId);
|
||||
InventoryService.DeleteItems(senderId, items);
|
||||
}
|
||||
}
|
||||
|
||||
return itemCopy;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_log.WarnFormat("[AGENT INVENTORY]: Failed to find item {0} or item does not belong to giver ", itemId);
|
||||
m_log.WarnFormat(
|
||||
"[AGENT INVENTORY]: Failed to find item {0} sent by {1} to {2}", itemId, senderId, recipient);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (item.Owner != senderId)
|
||||
{
|
||||
m_log.WarnFormat(
|
||||
"[AGENT INVENTORY]: Attempt to send item {0} {1} to {2} failed because sender {3} did not match item owner {4}",
|
||||
item.Name, item.ID, recipient, senderId, item.Owner);
|
||||
return null;
|
||||
}
|
||||
|
||||
IUserManagement uman = RequestModuleInterface<IUserManagement>();
|
||||
if (uman != null)
|
||||
uman.AddUser(item.CreatorIdAsUuid, item.CreatorData);
|
||||
|
||||
if (!Permissions.BypassPermissions())
|
||||
{
|
||||
if ((item.CurrentPermissions & (uint)PermissionMask.Transfer) == 0)
|
||||
return null;
|
||||
}
|
||||
|
||||
// Insert a copy of the item into the recipient
|
||||
InventoryItemBase itemCopy = new InventoryItemBase();
|
||||
itemCopy.Owner = recipient;
|
||||
itemCopy.CreatorId = item.CreatorId;
|
||||
itemCopy.CreatorData = item.CreatorData;
|
||||
itemCopy.ID = UUID.Random();
|
||||
itemCopy.AssetID = item.AssetID;
|
||||
itemCopy.Description = item.Description;
|
||||
itemCopy.Name = item.Name;
|
||||
itemCopy.AssetType = item.AssetType;
|
||||
itemCopy.InvType = item.InvType;
|
||||
itemCopy.Folder = recipientFolderId;
|
||||
|
||||
if (Permissions.PropagatePermissions() && recipient != senderId)
|
||||
{
|
||||
// Trying to do this right this time. This is evil. If
|
||||
// you believe in Good, go elsewhere. Vampires and other
|
||||
// evil creatores only beyond this point. You have been
|
||||
// warned.
|
||||
|
||||
// We're going to mask a lot of things by the next perms
|
||||
// Tweak the next perms to be nicer to our data
|
||||
//
|
||||
// In this mask, all the bits we do NOT want to mess
|
||||
// with are set. These are:
|
||||
//
|
||||
// Transfer
|
||||
// Copy
|
||||
// Modufy
|
||||
uint permsMask = ~ ((uint)PermissionMask.Copy |
|
||||
(uint)PermissionMask.Transfer |
|
||||
(uint)PermissionMask.Modify);
|
||||
|
||||
// Now, reduce the next perms to the mask bits
|
||||
// relevant to the operation
|
||||
uint nextPerms = permsMask | (item.NextPermissions &
|
||||
((uint)PermissionMask.Copy |
|
||||
(uint)PermissionMask.Transfer |
|
||||
(uint)PermissionMask.Modify));
|
||||
|
||||
// nextPerms now has all bits set, except for the actual
|
||||
// next permission bits.
|
||||
|
||||
// This checks for no mod, no copy, no trans.
|
||||
// This indicates an error or messed up item. Do it like
|
||||
// SL and assume trans
|
||||
if (nextPerms == permsMask)
|
||||
nextPerms |= (uint)PermissionMask.Transfer;
|
||||
|
||||
// Inventory owner perms are the logical AND of the
|
||||
// folded perms and the root prim perms, however, if
|
||||
// the root prim is mod, the inventory perms will be
|
||||
// mod. This happens on "take" and is of little concern
|
||||
// here, save for preventing escalation
|
||||
|
||||
// This hack ensures that items previously permalocked
|
||||
// get unlocked when they're passed or rezzed
|
||||
uint basePerms = item.BasePermissions |
|
||||
(uint)PermissionMask.Move;
|
||||
uint ownerPerms = item.CurrentPermissions;
|
||||
|
||||
// If this is an object, root prim perms may be more
|
||||
// permissive than folded perms. Use folded perms as
|
||||
// a mask
|
||||
if (item.InvType == (int)InventoryType.Object)
|
||||
{
|
||||
// Create a safe mask for the current perms
|
||||
uint foldedPerms = (item.CurrentPermissions & 7) << 13;
|
||||
foldedPerms |= permsMask;
|
||||
|
||||
bool isRootMod = (item.CurrentPermissions &
|
||||
(uint)PermissionMask.Modify) != 0 ?
|
||||
true : false;
|
||||
|
||||
// Mask the owner perms to the folded perms
|
||||
ownerPerms &= foldedPerms;
|
||||
basePerms &= foldedPerms;
|
||||
|
||||
// If the root was mod, let the mask reflect that
|
||||
// We also need to adjust the base here, because
|
||||
// we should be able to edit in-inventory perms
|
||||
// for the root prim, if it's mod.
|
||||
if (isRootMod)
|
||||
{
|
||||
ownerPerms |= (uint)PermissionMask.Modify;
|
||||
basePerms |= (uint)PermissionMask.Modify;
|
||||
}
|
||||
}
|
||||
|
||||
// These will be applied to the root prim at next rez.
|
||||
// The slam bit (bit 3) and folded permission (bits 0-2)
|
||||
// are preserved due to the above mangling
|
||||
ownerPerms &= nextPerms;
|
||||
|
||||
// Mask the base permissions. This is a conservative
|
||||
// approach altering only the three main perms
|
||||
basePerms &= nextPerms;
|
||||
|
||||
// Assign to the actual item. Make sure the slam bit is
|
||||
// set, if it wasn't set before.
|
||||
itemCopy.BasePermissions = basePerms;
|
||||
itemCopy.CurrentPermissions = ownerPerms;
|
||||
itemCopy.Flags |= (uint)InventoryItemFlags.ObjectSlamPerm;
|
||||
|
||||
itemCopy.NextPermissions = item.NextPermissions;
|
||||
|
||||
// This preserves "everyone can move"
|
||||
itemCopy.EveryOnePermissions = item.EveryOnePermissions &
|
||||
nextPerms;
|
||||
|
||||
// Intentionally killing "share with group" here, as
|
||||
// the recipient will not have the group this is
|
||||
// set to
|
||||
itemCopy.GroupPermissions = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemCopy.CurrentPermissions = item.CurrentPermissions;
|
||||
itemCopy.NextPermissions = item.NextPermissions;
|
||||
itemCopy.EveryOnePermissions = item.EveryOnePermissions & item.NextPermissions;
|
||||
itemCopy.GroupPermissions = item.GroupPermissions & item.NextPermissions;
|
||||
itemCopy.BasePermissions = item.BasePermissions;
|
||||
}
|
||||
|
||||
if (itemCopy.Folder == UUID.Zero)
|
||||
{
|
||||
InventoryFolderBase folder = InventoryService.GetFolderForType(recipient, (AssetType)itemCopy.AssetType);
|
||||
|
||||
if (folder != null)
|
||||
{
|
||||
itemCopy.Folder = folder.ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
InventoryFolderBase root = InventoryService.GetRootFolder(recipient);
|
||||
|
||||
if (root != null)
|
||||
itemCopy.Folder = root.ID;
|
||||
else
|
||||
return null; // No destination
|
||||
}
|
||||
}
|
||||
|
||||
itemCopy.GroupID = UUID.Zero;
|
||||
itemCopy.GroupOwned = false;
|
||||
itemCopy.Flags = item.Flags;
|
||||
itemCopy.SalePrice = item.SalePrice;
|
||||
itemCopy.SaleType = item.SaleType;
|
||||
|
||||
if (AddInventoryItem(itemCopy))
|
||||
{
|
||||
IInventoryAccessModule invAccess = RequestModuleInterface<IInventoryAccessModule>();
|
||||
if (invAccess != null)
|
||||
invAccess.TransferInventoryAssets(itemCopy, senderId, recipient);
|
||||
}
|
||||
|
||||
if (!Permissions.BypassPermissions())
|
||||
{
|
||||
if ((item.CurrentPermissions & (uint)PermissionMask.Copy) == 0)
|
||||
{
|
||||
List<UUID> items = new List<UUID>();
|
||||
items.Add(itemId);
|
||||
InventoryService.DeleteItems(senderId, items);
|
||||
}
|
||||
}
|
||||
|
||||
return itemCopy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -59,19 +59,24 @@ namespace OpenSim.Region.Framework.Tests
|
|||
// log4net.Config.XmlConfigurator.Configure();
|
||||
|
||||
Scene scene = SceneSetupHelpers.SetupScene();
|
||||
UserAccount user1 = UserAccountHelpers.CreateUserWithInventory(scene);
|
||||
UserAccount user2 = UserAccountHelpers.CreateUserWithInventory(scene);
|
||||
UserAccount user1 = UserAccountHelpers.CreateUserWithInventory(scene, 1001);
|
||||
UserAccount user2 = UserAccountHelpers.CreateUserWithInventory(scene, 1002);
|
||||
InventoryItemBase item1 = UserInventoryHelpers.CreateInventoryItem(scene, "item1", user1.PrincipalID);
|
||||
|
||||
scene.GiveInventoryItem(user2.PrincipalID, user1.PrincipalID, item1.ID);
|
||||
|
||||
InventoryItemBase retrievedItem1
|
||||
= UserInventoryHelpers.GetInventoryItem(scene.InventoryService, user2.PrincipalID, "Objects/item1");
|
||||
= UserInventoryHelpers.GetInventoryItem(scene.InventoryService, user2.PrincipalID, "Notecards/item1");
|
||||
|
||||
Assert.That(retrievedItem1, Is.Not.Null);
|
||||
|
||||
// Try giving back the freshly received item
|
||||
//scene.GiveInventoryItem(user1.PrincipalID, user2.PrincipalID, retrievedItem1.ID);
|
||||
scene.GiveInventoryItem(user1.PrincipalID, user2.PrincipalID, retrievedItem1.ID);
|
||||
|
||||
List<InventoryItemBase> reretrievedItems
|
||||
= UserInventoryHelpers.GetInventoryItems(scene.InventoryService, user1.PrincipalID, "Notecards/item1");
|
||||
|
||||
Assert.That(reretrievedItems.Count, Is.EqualTo(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -81,8 +86,8 @@ namespace OpenSim.Region.Framework.Tests
|
|||
// log4net.Config.XmlConfigurator.Configure();
|
||||
|
||||
Scene scene = SceneSetupHelpers.SetupScene();
|
||||
UserAccount user1 = UserAccountHelpers.CreateUserWithInventory(scene);
|
||||
UserAccount user2 = UserAccountHelpers.CreateUserWithInventory(scene);
|
||||
UserAccount user1 = UserAccountHelpers.CreateUserWithInventory(scene, 1001);
|
||||
UserAccount user2 = UserAccountHelpers.CreateUserWithInventory(scene, 1002);
|
||||
InventoryFolderBase folder1
|
||||
= UserInventoryHelpers.CreateInventoryFolder(scene.InventoryService, user1.PrincipalID, "folder1");
|
||||
|
||||
|
|
|
@ -341,6 +341,9 @@ namespace OpenSim.Services.InventoryService
|
|||
|
||||
itemsList.AddRange(m_Database.getInventoryInFolder(folderID));
|
||||
|
||||
// m_log.DebugFormat(
|
||||
// "[INVENTORY SERVICE]: Found {0} items in folder {1} for {2}", itemsList.Count, folderID, userID);
|
||||
|
||||
return itemsList;
|
||||
}
|
||||
|
||||
|
@ -385,8 +388,9 @@ namespace OpenSim.Services.InventoryService
|
|||
// See IInventoryServices
|
||||
public virtual bool AddItem(InventoryItemBase item)
|
||||
{
|
||||
m_log.DebugFormat(
|
||||
"[INVENTORY SERVICE]: Adding item {0} {1} to folder {2}", item.Name, item.ID, item.Folder);
|
||||
// m_log.DebugFormat(
|
||||
// "[INVENTORY SERVICE]: Adding item {0} {1} to folder {2} for {3}",
|
||||
// item.Name, item.ID, item.Folder, item.Owner);
|
||||
|
||||
m_Database.addInventoryItem(item);
|
||||
|
||||
|
|
|
@ -117,17 +117,20 @@ namespace OpenSim.Tests.Common
|
|||
// }
|
||||
|
||||
public static UserAccount CreateUserWithInventory(Scene scene)
|
||||
{
|
||||
return CreateUserWithInventory(scene, 99);
|
||||
}
|
||||
|
||||
public static UserAccount CreateUserWithInventory(Scene scene, int uuidTail)
|
||||
{
|
||||
return CreateUserWithInventory(
|
||||
scene, "Bill", "Bailey", UUID.Parse("00000000-0000-0000-0000-000000000099"), "troll");
|
||||
scene, "Bill", "Bailey", new UUID(string.Format("00000000-0000-0000-0000-{0:X12}", uuidTail)), "troll");
|
||||
}
|
||||
|
||||
public static UserAccount CreateUserWithInventory(
|
||||
Scene scene, string firstName, string lastName, UUID userId, string pw)
|
||||
{
|
||||
UserAccount ua
|
||||
= new UserAccount(userId)
|
||||
{ FirstName = firstName, LastName = lastName };
|
||||
UserAccount ua = new UserAccount(userId) { FirstName = firstName, LastName = lastName };
|
||||
CreateUserWithInventory(scene, ua, pw);
|
||||
return ua;
|
||||
}
|
||||
|
|
|
@ -65,15 +65,18 @@ namespace OpenSim.Tests.Common
|
|||
/// <returns></returns>
|
||||
public static InventoryItemBase CreateInventoryItem(Scene scene, string itemName, UUID itemId, UUID userId)
|
||||
{
|
||||
AssetBase asset = AssetHelpers.CreateAsset(scene, userId);
|
||||
InventoryItemBase item = new InventoryItemBase();
|
||||
item.Name = itemName;
|
||||
item.AssetID = AssetHelpers.CreateAsset(scene, userId).FullID;
|
||||
item.AssetID = asset.FullID;
|
||||
item.ID = itemId;
|
||||
item.Owner = userId;
|
||||
item.AssetType = asset.Type;
|
||||
item.InvType = (int)InventoryType.Notecard;
|
||||
|
||||
// Really quite bad since the objs folder could be moved in the future and confuse the tests
|
||||
InventoryFolderBase objsFolder = scene.InventoryService.GetFolderForType(userId, AssetType.Object);
|
||||
InventoryFolderBase folder = scene.InventoryService.GetFolderForType(userId, AssetType.Notecard);
|
||||
|
||||
item.Folder = objsFolder.ID;
|
||||
item.Folder = folder.ID;
|
||||
scene.AddInventoryItem(item);
|
||||
|
||||
return item;
|
||||
|
@ -165,5 +168,17 @@ namespace OpenSim.Tests.Common
|
|||
{
|
||||
return InventoryArchiveUtils.FindItemByPath(inventoryService, userId, path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the inventory items that match the path name.
|
||||
/// </summary>
|
||||
/// <param name="inventoryService"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="path"></param>
|
||||
/// <returns>An empty list if no matching items were found.</returns>
|
||||
public static List<InventoryItemBase> GetInventoryItems(IInventoryService inventoryService, UUID userId, string path)
|
||||
{
|
||||
return InventoryArchiveUtils.FindItemsByPath(inventoryService, userId, path);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue