Discussion:
[nhusers] Add auditing through Http
Pratip Bagchi
2018-11-12 17:43:18 UTC
Permalink
I am in dire need of your help. I have migrated some legacy .Net web API
applications to .net core and get it all working except the Envers piece of
it. The major problem I am facing is to pass user name to the
RevisionListener. As RevisionListener does not support DI I am not able to
inject HttpContextAccessor to get the context. Alternatively if I use
static HttpContextAccessor I am running with same context from two
different request.


Even if I use "IHttpContextAccessor " due t static nature of the class
Revision table is holding the same context between two requests from 2
different users.


Any example will be appreciated!



Here is the code I am using to configure nHibernate


public static class NHibernateExtension

{

private static readonly ILog log =
LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);


public static void AddNhibernate(this IServiceCollection services,
string connectionString,string schemaName )

{



services.AddSingleton((provider) =>

{

var cfg = new NHibernate.Cfg.Configuration();

cfg.Configure("hibernate.cfg.xml");

cfg.SetProperty("connection.connection_string",
connectionString);

cfg.AddProperties(new Dictionary<string, string>

{

{ NHibernate.Cfg.Environment.DefaultSchema, schemaName }

});

cfg.AddMapping(NHibernateConfig.GetMappings());


var enversConfiguration = GetEnversConfiguration();

cfg.SetEnversProperty(ConfigurationKey.DefaultSchema,
"Audit");

cfg.IntegrateWithEnvers(enversConfiguration);



return cfg;

});


services.AddSingleton((provider) =>
provider.GetService<NHibernate.Cfg.Configuration>().BuildSessionFactory());


services.AddScoped((provider) =>
provider.GetService<ISessionFactory>().OpenSession());

}



private static FluentConfiguration GetEnversConfiguration()

{

var enversConf = new FluentConfiguration();



var userId = HttpContext.Current?.Request?.Headers["user_name"];

var userName = HttpContext.Current?.Request?.Headers["name"];


RevisionListener rn = new RevisionListener(userName, userId);

enversConf.SetRevisionEntity<RevisionDetails>(x => x.Id, x =>
x.RevisionTimestamp, rn);

enversConf.Audit<PrinterMapping>().SetTableInfo(x => x.Value =
typeof(PrinterMapping).Name);

enversConf.Audit<Label>().SetTableInfo(x => x.Value =
typeof(Label).Name);

enversConf.Audit<Language>().SetTableInfo(x => x.Value =
typeof(Language).Name);

enversConf.Audit<Translation>().SetTableInfo(x => x.Value =
typeof(Translation).Name);

return enversConf;

}


}


//this provides HttpContext through IHttpContextAccessor


public static class HttpContext

{

private static IHttpContextAccessor _contextAccessor;


public static Microsoft.AspNetCore.Http.HttpContext Current =>
_contextAccessor.HttpContext;


internal static void Configure(IHttpContextAccessor contextAccessor)

{

_contextAccessor = contextAccessor;

}

}



***/Listener******/

public class RevisionListener : IRevisionListener {

private string _userName = string.Empty;

private string _userId = string.Empty;



public RevisionListener(string userName, string userId)

: base()

{

this._userName = userName;

this._userId = userId;

}



public void NewRevision(object revisionEntity)

{

var casted = revisionEntity as RevisionDetails;



if (casted != null)

{

casted.UserName = this._userName;

casted.UserId = this._userId;

}

}



}
--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhusers+***@googlegroups.com.
To post to this group, send email to ***@googlegroups.com.
Visit this group at https://groups.google.com/group/nhusers.
For more options, visit https://groups.google.com/d/optout.
Oskar Berggren
2018-11-12 20:11:52 UTC
Permalink
Envers isn't my strong side, but why can't your RevisionListener access
HttpContext.Current on the fly?

/Oskar
Post by Pratip Bagchi
I am in dire need of your help. I have migrated some legacy .Net web API
applications to .net core and get it all working except the Envers piece of
it. The major problem I am facing is to pass user name to the
RevisionListener. As RevisionListener does not support DI I am not able to
inject HttpContextAccessor to get the context. Alternatively if I use
static HttpContextAccessor I am running with same context from two
different request.
Even if I use "IHttpContextAccessor " due t static nature of the class
Revision table is holding the same context between two requests from 2
different users.
Any example will be appreciated!
Here is the code I am using to configure nHibernate
public static class NHibernateExtension
{
private static readonly ILog log =
LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public static void AddNhibernate(this IServiceCollection services,
string connectionString,string schemaName )
{
services.AddSingleton((provider) =>
{
var cfg = new NHibernate.Cfg.Configuration();
cfg.Configure("hibernate.cfg.xml");
cfg.SetProperty("connection.connection_string",
connectionString);
cfg.AddProperties(new Dictionary<string, string>
{
{ NHibernate.Cfg.Environment.DefaultSchema, schemaName }
});
cfg.AddMapping(NHibernateConfig.GetMappings());
var enversConfiguration = GetEnversConfiguration();
cfg.SetEnversProperty(ConfigurationKey.DefaultSchema,
"Audit");
cfg.IntegrateWithEnvers(enversConfiguration);
return cfg;
});
services.AddSingleton((provider) =>
provider.GetService<NHibernate.Cfg.Configuration>().BuildSessionFactory());
services.AddScoped((provider) =>
provider.GetService<ISessionFactory>().OpenSession());
}
private static FluentConfiguration GetEnversConfiguration()
{
var enversConf = new FluentConfiguration();
var userId = HttpContext.Current?.Request?.Headers["user_name"];
var userName = HttpContext.Current?.Request?.Headers["name"];
RevisionListener rn = new RevisionListener(userName, userId);
enversConf.SetRevisionEntity<RevisionDetails>(x => x.Id, x =>
x.RevisionTimestamp, rn);
enversConf.Audit<PrinterMapping>().SetTableInfo(x => x.Value =
typeof(PrinterMapping).Name);
enversConf.Audit<Label>().SetTableInfo(x => x.Value =
typeof(Label).Name);
enversConf.Audit<Language>().SetTableInfo(x => x.Value =
typeof(Language).Name);
enversConf.Audit<Translation>().SetTableInfo(x => x.Value =
typeof(Translation).Name);
return enversConf;
}
}
//this provides HttpContext through IHttpContextAccessor
public static class HttpContext
{
private static IHttpContextAccessor _contextAccessor;
public static Microsoft.AspNetCore.Http.HttpContext Current =>
_contextAccessor.HttpContext;
internal static void Configure(IHttpContextAccessor
contextAccessor)
{
_contextAccessor = contextAccessor;
}
}
***/Listener******/
public class RevisionListener : IRevisionListener {
private string _userName = string.Empty;
private string _userId = string.Empty;
public RevisionListener(string userName, string userId)
: base()
{
this._userName = userName;
this._userId = userId;
}
public void NewRevision(object revisionEntity)
{
var casted = revisionEntity as RevisionDetails;
if (casted != null)
{
casted.UserName = this._userName;
casted.UserId = this._userId;
}
}
}
--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at https://groups.google.com/group/nhusers.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhusers+***@googlegroups.com.
To post to this group, send email to ***@googlegroups.com.
Visit this group at https://groups.google.com/group/nhusers.
For more options, visit https://groups.google.com/d/optout.
Roger Kratz
2018-11-13 08:51:02 UTC
Permalink
Yes, IRevisionListener supports DI.

var myListener = new MyRevisionListener(new DepA(), new DepB(new DepC()))

https://nhibernate.jira.com/browse/NHE-17



From: ***@googlegroups.com <***@googlegroups.com> On Behalf Of Pratip Bagchi
Sent: den 12 november 2018 18:43
To: nhusers <***@googlegroups.com>
Subject: [nhusers] Add auditing through Http


I am in dire need of your help. I have migrated some legacy .Net web API applications to .net core and get it all working except the Envers piece of it. The major problem I am facing is to pass user name to the RevisionListener. As RevisionListener does not support DI I am not able to inject HttpContextAccessor to get the context. Alternatively if I use static HttpContextAccessor I am running with same context from two different request.



Even if I use "IHttpContextAccessor " due t static nature of the class Revision table is holding the same context between two requests from 2 different users.



Any example will be appreciated!





Here is the code I am using to configure nHibernate



public static class NHibernateExtension

{

private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);



public static void AddNhibernate(this IServiceCollection services, string connectionString,string schemaName )

{



services.AddSingleton((provider) =>

{

var cfg = new NHibernate.Cfg.Configuration();

cfg.Configure("hibernate.cfg.xml");

cfg.SetProperty("connection.connection_string", connectionString);

cfg.AddProperties(new Dictionary<string, string>

{

{ NHibernate.Cfg.Environment.DefaultSchema, schemaName }

});

cfg.AddMapping(NHibernateConfig.GetMappings());



var enversConfiguration = GetEnversConfiguration();

cfg.SetEnversProperty(ConfigurationKey.DefaultSchema, "Audit");

cfg.IntegrateWithEnvers(enversConfiguration);





return cfg;

});



services.AddSingleton((provider) => provider.GetService<NHibernate.Cfg.Configuration>().BuildSessionFactory());



services.AddScoped((provider) => provider.GetService<ISessionFactory>().OpenSession());

}





private static FluentConfiguration GetEnversConfiguration()

{

var enversConf = new FluentConfiguration();



var userId = HttpContext.Current?.Request?.Headers["user_name"];

var userName = HttpContext.Current?.Request?.Headers["name"];



RevisionListener rn = new RevisionListener(userName, userId);

enversConf.SetRevisionEntity<RevisionDetails>(x => x.Id, x => x.RevisionTimestamp, rn);

enversConf.Audit<PrinterMapping>().SetTableInfo(x => x.Value = typeof(PrinterMapping).Name);

enversConf.Audit<Label>().SetTableInfo(x => x.Value = typeof(Label).Name);

enversConf.Audit<Language>().SetTableInfo(x => x.Value = typeof(Language).Name);

enversConf.Audit<Translation>().SetTableInfo(x => x.Value = typeof(Translation).Name);

return enversConf;

}



}



//this provides HttpContext through IHttpContextAccessor



public static class HttpContext

{

private static IHttpContextAccessor _contextAccessor;



public static Microsoft.AspNetCore.Http.HttpContext Current => _contextAccessor.HttpContext;



internal static void Configure(IHttpContextAccessor contextAccessor)

{

_contextAccessor = contextAccessor;

}

}





*/Listener****/

public class RevisionListener : IRevisionListener {

private string _userName = string.Empty;

private string _userId = string.Empty;



public RevisionListener(string userName, string userId)

: base()

{

this._userName = userName;

this._userId = userId;

}



public void NewRevision(object revisionEntity)

{

var casted = revisionEntity as RevisionDetails;



if (casted != null)

{

casted.UserName = this._userName;

casted.UserId = this._userId;

}

}



}
--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhusers+***@googlegroups.com<mailto:nhusers+***@googlegroups.com>.
To post to this group, send email to ***@googlegroups.com<mailto:***@googlegroups.com>.
Visit this group at https://groups.google.com/group/nhusers<https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgroups.google.com%2Fgroup%2Fnhusers&data=02%7C01%7Croger.kratz%40teleopti.com%7C8834d6caa5a84f3cc00f08d648c69bea%7C449a67e94f234e758bccf32dd77cefe0%7C0%7C1%7C636776415196701993&sdata=NKvRxk34LCvrgiJWKMQZJQHw83%2BoX5c2O0oe8lnB6ps%3D&reserved=0>.
For more options, visit https://groups.google.com/d/optout<https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgroups.google.com%2Fd%2Foptout&data=02%7C01%7Croger.kratz%40teleopti.com%7C8834d6caa5a84f3cc00f08d648c69bea%7C449a67e94f234e758bccf32dd77cefe0%7C0%7C1%7C636776415196711997&sdata=kdksRXz%2BK1ecdILwucNG9tAzpMUnSPf8YVomnul4Wjc%3D&reserved=0>.
--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhusers+***@googlegroups.com.
To post to this group, send email to ***@googlegroups.com.
Visit this group at https://groups.google.com/group/nhusers.
For more options, visit https://groups.google.com/d/optout.
Pratip Bagchi
2018-11-13 18:37:16 UTC
Permalink
Thanks Roger. But do I pass a new IHttpContextAccessor to the
RevisionListener. My "AddNhibernate" service extension called only once in
Startup and as IHttpContextAccessor is singleton in nature once it is
initialized it is holding the same user every time. I am not use how I pass
a new instance of IHttpContextAccessor type to RevisionListener. Can you
please help with some sample if possible
Post by Roger Kratz
Yes, IRevisionListener supports DI.
var myListener = new MyRevisionListener(new DepA(), new DepB(new DepC()))
https://nhibernate.jira.com/browse/NHE-17
*Pratip Bagchi
*Sent:* den 12 november 2018 18:43
*Subject:* [nhusers] Add auditing through Http
I am in dire need of your help. I have migrated some legacy .Net web API
applications to .net core and get it all working except the Envers piece of
it. The major problem I am facing is to pass user name to the
RevisionListener. As RevisionListener does not support DI I am not able to
inject HttpContextAccessor to get the context. Alternatively if I use
static HttpContextAccessor I am running with same context from two
different request.
Even if I use "IHttpContextAccessor " due t static nature of the class
Revision table is holding the same context between two requests from 2
different users.
Any example will be appreciated!
Here is the code I am using to configure nHibernate
public static class NHibernateExtension
{
private static readonly ILog log =
LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public static void AddNhibernate(this IServiceCollection services,
string connectionString,string schemaName )
{
services.AddSingleton((provider) =>
{
var cfg = new NHibernate.Cfg.Configuration();
cfg.Configure("hibernate.cfg.xml");
cfg.SetProperty("connection.connection_string", connectionString);
cfg.AddProperties(new Dictionary<string, string>
{
{ NHibernate.Cfg.Environment.DefaultSchema, schemaName }
});
cfg.AddMapping(NHibernateConfig.GetMappings());
var enversConfiguration = GetEnversConfiguration();
cfg.SetEnversProperty(ConfigurationKey.DefaultSchema, "Audit");
cfg.IntegrateWithEnvers(enversConfiguration);
return cfg;
});
services.AddSingleton((provider) =>
provider.GetService<NHibernate.Cfg.Configuration>().BuildSessionFactory());
services.AddScoped((provider) =>
provider.GetService<ISessionFactory>().OpenSession());
}
private static FluentConfiguration GetEnversConfiguration()
{
var enversConf = new FluentConfiguration();
var userId = HttpContext.Current?.Request?.Headers["user_name"];
var userName = HttpContext.Current?.Request?.Headers["name"];
RevisionListener rn = new RevisionListener(userName, userId);
enversConf.SetRevisionEntity<RevisionDetails>(x => x.Id, x =>
x.RevisionTimestamp, rn);
enversConf.Audit<PrinterMapping>().SetTableInfo(x => x.Value =
typeof(PrinterMapping).Name);
enversConf.Audit<Label>().SetTableInfo(x => x.Value = typeof(Label).Name);
enversConf.Audit<Language>().SetTableInfo(x => x.Value =
typeof(Language).Name);
enversConf.Audit<Translation>().SetTableInfo(x => x.Value =
typeof(Translation).Name);
return enversConf;
}
}
//this provides HttpContext through IHttpContextAccessor
public static class HttpContext
{
private static IHttpContextAccessor _contextAccessor;
public static Microsoft.AspNetCore.Http.HttpContext Current =>
_contextAccessor.HttpContext;
internal static void Configure(IHttpContextAccessor
contextAccessor)
{
_contextAccessor = contextAccessor;
}
}
***/Listener******/
public class RevisionListener : IRevisionListener {
private string _userName = string.Empty;
private string _userId = string.Empty;
public RevisionListener(string userName, string userId)
: base()
{
this._userName = userName;
this._userId = userId;
}
public void NewRevision(object revisionEntity)
{
var casted = revisionEntity as RevisionDetails;
if (casted != null)
{
casted.UserName = this._userName;
casted.UserId = this._userId;
}
}
}
--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at https://groups.google.com/group/nhusers
<https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgroups.google.com%2Fgroup%2Fnhusers&data=02%7C01%7Croger.kratz%40teleopti.com%7C8834d6caa5a84f3cc00f08d648c69bea%7C449a67e94f234e758bccf32dd77cefe0%7C0%7C1%7C636776415196701993&sdata=NKvRxk34LCvrgiJWKMQZJQHw83%2BoX5c2O0oe8lnB6ps%3D&reserved=0>
.
For more options, visit https://groups.google.com/d/optout
<https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgroups.google.com%2Fd%2Foptout&data=02%7C01%7Croger.kratz%40teleopti.com%7C8834d6caa5a84f3cc00f08d648c69bea%7C449a67e94f234e758bccf32dd77cefe0%7C0%7C1%7C636776415196711997&sdata=kdksRXz%2BK1ecdILwucNG9tAzpMUnSPf8YVomnul4Wjc%3D&reserved=0>
.
--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at https://groups.google.com/group/nhusers.
For more options, visit https://groups.google.com/d/optout.
--
*Thanks*
*~ Pratip*
--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhusers+***@googlegroups.com.
To post to this group, send email to ***@googlegroups.com.
Visit this group at https://groups.google.com/group/nhusers.
For more options, visit https://groups.google.com/d/optout.
Oskar Berggren
2018-11-13 20:26:16 UTC
Permalink
Post by Pratip Bagchi
Thanks Roger. But do I pass a new IHttpContextAccessor to the
RevisionListener. My "AddNhibernate" service extension called only once in
Startup and as IHttpContextAccessor is singleton in nature once it is
initialized it is holding the same user every time. I am not use how I pass
a new instance of IHttpContextAccessor type to RevisionListener. Can you
please help with some sample if possible
Very little of this seems to be shown in the code you posted, so I'm not
sure what exactly you mean but...

IHttpContextAccessor is an interface and the term "singleton" does not
apply to it as such. In the definition of the interface
IHttpContextAccessor I can't see anything about holding the user name or
similar. It only has one property, which should be implemented to return
the current HttpContext. So one way of doing it should be that revision
listener does something like

On call to NewRevision():
var user = _httpContextAccessor.HttpContext.User;


I'm sure there are other way to do it too.

/Oskar
Post by Pratip Bagchi
Post by Roger Kratz
Yes, IRevisionListener supports DI.
var myListener = new MyRevisionListener(new DepA(), new DepB(new DepC()))
https://nhibernate.jira.com/browse/NHE-17
Of *Pratip Bagchi
*Sent:* den 12 november 2018 18:43
*Subject:* [nhusers] Add auditing through Http
I am in dire need of your help. I have migrated some legacy .Net web API
applications to .net core and get it all working except the Envers piece of
it. The major problem I am facing is to pass user name to the
RevisionListener. As RevisionListener does not support DI I am not able to
inject HttpContextAccessor to get the context. Alternatively if I use
static HttpContextAccessor I am running with same context from two
different request.
Even if I use "IHttpContextAccessor " due t static nature of the class
Revision table is holding the same context between two requests from 2
different users.
Any example will be appreciated!
Here is the code I am using to configure nHibernate
public static class NHibernateExtension
{
private static readonly ILog log =
LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public static void AddNhibernate(this IServiceCollection
services, string connectionString,string schemaName )
{
services.AddSingleton((provider) =>
{
var cfg = new NHibernate.Cfg.Configuration();
cfg.Configure("hibernate.cfg.xml");
cfg.SetProperty("connection.connection_string", connectionString);
cfg.AddProperties(new Dictionary<string, string>
{
{ NHibernate.Cfg.Environment.DefaultSchema, schemaName }
});
cfg.AddMapping(NHibernateConfig.GetMappings());
var enversConfiguration = GetEnversConfiguration();
cfg.SetEnversProperty(ConfigurationKey.DefaultSchema, "Audit");
cfg.IntegrateWithEnvers(enversConfiguration);
return cfg;
});
services.AddSingleton((provider) =>
provider.GetService<NHibernate.Cfg.Configuration>().BuildSessionFactory());
services.AddScoped((provider) =>
provider.GetService<ISessionFactory>().OpenSession());
}
private static FluentConfiguration GetEnversConfiguration()
{
var enversConf = new FluentConfiguration();
var userId =
HttpContext.Current?.Request?.Headers["user_name"];
var userName = HttpContext.Current?.Request?.Headers["name"];
RevisionListener rn = new RevisionListener(userName, userId);
enversConf.SetRevisionEntity<RevisionDetails>(x => x.Id, x =>
x.RevisionTimestamp, rn);
enversConf.Audit<PrinterMapping>().SetTableInfo(x => x.Value
= typeof(PrinterMapping).Name);
enversConf.Audit<Label>().SetTableInfo(x => x.Value = typeof(Label).Name);
enversConf.Audit<Language>().SetTableInfo(x => x.Value =
typeof(Language).Name);
enversConf.Audit<Translation>().SetTableInfo(x => x.Value =
typeof(Translation).Name);
return enversConf;
}
}
//this provides HttpContext through IHttpContextAccessor
public static class HttpContext
{
private static IHttpContextAccessor _contextAccessor;
public static Microsoft.AspNetCore.Http.HttpContext Current =>
_contextAccessor.HttpContext;
internal static void Configure(IHttpContextAccessor
contextAccessor)
{
_contextAccessor = contextAccessor;
}
}
***/Listener******/
public class RevisionListener : IRevisionListener {
private string _userName = string.Empty;
private string _userId = string.Empty;
public RevisionListener(string userName, string userId)
: base()
{
this._userName = userName;
this._userId = userId;
}
public void NewRevision(object revisionEntity)
{
var casted = revisionEntity as RevisionDetails;
if (casted != null)
{
casted.UserName = this._userName;
casted.UserId = this._userId;
}
}
}
--
You received this message because you are subscribed to the Google Groups
"nhusers" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at https://groups.google.com/group/nhusers
<https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgroups.google.com%2Fgroup%2Fnhusers&data=02%7C01%7Croger.kratz%40teleopti.com%7C8834d6caa5a84f3cc00f08d648c69bea%7C449a67e94f234e758bccf32dd77cefe0%7C0%7C1%7C636776415196701993&sdata=NKvRxk34LCvrgiJWKMQZJQHw83%2BoX5c2O0oe8lnB6ps%3D&reserved=0>
.
For more options, visit https://groups.google.com/d/optout
<https://emea01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgroups.google.com%2Fd%2Foptout&data=02%7C01%7Croger.kratz%40teleopti.com%7C8834d6caa5a84f3cc00f08d648c69bea%7C449a67e94f234e758bccf32dd77cefe0%7C0%7C1%7C636776415196711997&sdata=kdksRXz%2BK1ecdILwucNG9tAzpMUnSPf8YVomnul4Wjc%3D&reserved=0>
.
--
You received this message because you are subscribed to the Google Groups
"nhusers" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at https://groups.google.com/group/nhusers.
For more options, visit https://groups.google.com/d/optout.
--
*Thanks*
*~ Pratip*
--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at https://groups.google.com/group/nhusers.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nhusers+***@googlegroups.com.
To post to this group, send email to ***@googlegroups.com.
Visit this group at https://groups.google.com/group/nhusers.
For more options, visit https://groups.google.com/d/optout.
Continue reading on narkive:
Loading...