You are not logged in.
Hi ab,
i have the following problem. I have the source code:
// interface I
IPhoMessartQuery = interface(ICQRSQuery)
['{CCFB5FAA-FFA2-437A-849C-E7F5A57763D6}']
function SelectByName(const AName: String): TCQRSResult;
function Get(out AAggregate: TPhoMessart): TCQRSResult;
function GetAll(out AAggregates: TPhoMessartObjArray): TCQRSResult;
function GetNext(out AAggregate: TPhoMessart): TCQRSResult;
function GetCount: Integer;
end;
// interface II
IPhoMessartCommand = interface(IPhoMessartQuery)
['{EF721955-203E-4A3E-9FD2-B83A2939669E}']
function Add(const AAggregate: TPhoMessart): TCQRSResult;
function Update(const AUpdatedAggregate: TPhoMessart): TCQRSResult;
function Delete: TCQRSResult;
function DeleteAll: TCQRSResult;
function Commit: TCQRSResult;
function Rollback: TCQRSResult;
end;
initialization
TInterfaceFactory.RegisterInterfaces([TypeInfo(IPhoMessartQuery), TypeInfo(IPhoMessartCommand)]);
Unfortunately I can't compile it and I get an internal error L2111.
I have tried to close the project and restart the IDE in order to clear the unit cache. It didn't help.
But if I change my code in a way that the interface II inherits from ICQRSQuery, i.e.
// interface II
IPhoMessartCommand = interface(ICQRSQuery)
['{EF721955-203E-4A3E-9FD2-B83A2939669E}']
...
end;
I can compile the code without problems.
Do you have an idea how I can get rid of the internal error without the changing the code?
Thank you!
Offline
I never saw this internal error L2111.
Which version of the compiler are you using?
Are you able to compile the regression tests, and the dddInfraRepoUser unit?
This is pretty weird...
I sadly do not know how to circumvent it.
Perhaps putting a "type" between the two definitions.
Or trying to define IPhoMessartCommand in a second unit...
Or changing the GUID...
Offline
I never saw this internal error L2111.
Which version of the compiler are you using?Are you able to compile the regression tests, and the dddInfraRepoUser unit?
This is pretty weird...
I sadly do not know how to circumvent it.
Perhaps putting a "type" between the two definitions.
Or trying to define IPhoMessartCommand in a second unit...
Or changing the GUID...
I am using Delphi XE3.
I have never bone before with the regression tests. Which project in the library is that?
I have tested your assumptions but without success. If I remove the code (see below), I can compile the project. Otherwise, not :-(
initialization
TInterfaceFactory.RegisterInterfaces([TypeInfo(IPhoMessartQuery), TypeInfo(IPhoMessartCommand)]);
Last edited by cypriotcalm (2015-05-19 09:01:55)
Offline
A generally question, I would like a domain model with interface based services. Is this a right way how I am doing that... I have followed one of your DDD examples:
I define the interfaces (see the code above in the previous posts), then I register them in the initialization section.
After that I use them like follows:
Model := TSQLModel.Create([], ROOT_NAME);
ServerDB := TSQLRestServerDB.Create(Model,ChangeFileExt(ExeVersion.ProgramFileName,'.db3'), False);
ServerDB.CreateMissingTables;
ServerDB.ServiceDefine(TPhoMessartCommand, [IPhoMessartCommand], sicShared);
ServerHTTP := TSQLHttpServer.Create('7979', [ServerDB], '+', useHttpApiRegisteringURI);
ServerHTTP.AccessControlAllowOrigin := '*'; // allow cross-site AJAX queries
...
...
var
cmd: IPhoMessartCommand;
begin
ServerDB.Services.Resolve(IPhoMessartCommand, cmd);
if Assigned(cmd) then
begin
// do something
end;
end;
Offline
If I remove the code (see below), I can compile the project. Otherwise, not :-(
TInterfaceFactory.RegisterInterfaces([TypeInfo(IPhoMessartQuery), TypeInfo(IPhoMessartCommand)]);
You may safely put this line outside the initialization section of this unit, elsewhere in your code.
Offline
A generally question, I would like a domain model with interface based services. Is this a right way how I am doing that...
Yes, it sounds right.
But it will also publish the services over REST/HTTP. Is it what you meant?
You can also do IoC and DI without HTTP publication.
See http://synopse.info/files/html/Synopse% … l#TITL_157
and in practice http://synopse.info/files/html/Synopse% … l#TITL_161
Offline
Yes, it sounds right.
But it will also publish the services over REST/HTTP. Is it what you meant?
I am not sure yet, I hope so, I am new to all that stuff with SOA/Rest/OR-Mapping, I am learning by doing and reading ;-) What I want is to map my existing database by mORMot (data persistance), then I woud like to offer some services, e.g. "create/delete/modify a measurement art" (Domain model) and then publish these services (application). I try to follow http://synopse.info/files/html/Synopse% … #TITLE_552
Stop! :-) Maybe you are right and that's not what I want ;-)
I want actually to publish the services over REST/HTTP in a way they can be consumed by different clients (delphi, php, java etc.) So, I can't publish the delphi types, I have to do it with some kind of JSON parameters, don't I? Coud you give me a quick reference in the doc where I can read about it? It would be great!
Last edited by cypriotcalm (2015-05-19 11:31:53)
Offline
If you use ServerDB.ServiceDefine(), then the services would be published over REST/HTTP, and data transmitted as JSON.
See http://synopse.info/files/html/Synopse% … ml#TITL_63
and http://synopse.info/files/html/Synopse% … #TITLE_404
Offline
Thx for your links! The second one I havn't seen before!
I am still trying to find out the internal error. Now I have commented all the methods and step by step tested with wich methods the project can be compiled. And I have found out that that the method GetAll with my own custom data type causes the internal error.
type
TPhoMessartObjArray = type of TObjectDynArray;
...
...
IPhoMessartQuery = interface(ICQRSQuery)
['{CCFB5FAA-FFA2-437A-849C-E7F5A57763D6}']
function SelectByName(const AName: String): TCQRSResult;
function Get(out AAggregate: TPhoMessart): TCQRSResult;
// function GetAll(out AAggregates: TPhoMessartObjArray): TCQRSResult;
function GetNext(out AAggregate: TPhoMessart): TCQRSResult;
function GetCount: Integer;
end;
Do you have an idea why the internal error happens?
How do I define aggregates in a right way?
Last edited by cypriotcalm (2015-05-19 13:03:45)
Offline
Did you register TPhoMessartObjArray via TJSONSerializer.RegisterObjArrayForJSON() ?
No!
But now I did it as follows and I don't get this annoying internal error anymore! Yeahaaa! :-) My code looks like:
type
TPhoMessartObjArray = type TObjectDynArray;
IPhoMessartQuery = interface(ICQRSQuery)
['{87BDB69C-485D-429C-A6AE-8E2751FFCFFB}']
function SelectByName(const AName: String): TCQRSResult;
function Get(out AAggregate: TPhoMessart): TCQRSResult;
function GetAll(out AAggregates: TPhoMessartObjArray): TCQRSResult;
function GetNext(out AAggregate: TPhoMessart): TCQRSResult;
function GetCount: Integer;
end;
IPhoMessartCommand = interface(IPhoMessartQuery)
['{23BE1585-5ED9-4596-A507-A264A2FEE8F6}']
function Add(const AAggregate: TPhoMessart): TCQRSResult;
function Update(const AUpdatedAggregate: TPhoMessart): TCQRSResult;
function Delete: TCQRSResult;
function DeleteAll: TCQRSResult;
function Commit: TCQRSResult;
function Rollback: TCQRSResult;
end;
implementation
initialization
TJSONSerializer.RegisterObjArrayForJSON(TypeInfo(TPhoMessartObjArray), TPhoMessart);
TInterfaceFactory.RegisterInterfaces([TypeInfo(IPhoMessartQuery), TypeInfo(IPhoMessartCommand)]);
Thx for your help! :-)
Last edited by cypriotcalm (2015-05-19 14:27:03)
Offline
For you code to be really usable, you should better define:
type
TPhoMessartObjArray = array of TPhoMessart;
otherwise, your AAggregates[] array would be compiled as an array of TObject...
not very handy...
Offline
Yep! Changed! Thx!
Offline
Dear AB!
I have another problem! :-) I setup my service server as follows
var
PhoMessartCommandFactory: TPhoMessartCommandFactory;
begin
FPhoRepository := TPhoDBRepository.Create;
Model := TSQLModel.Create([TPhoMessartSQLRecord], ROOT_NAME);
VirtualTableExternalRegister(Model, TPhoMessartSQLRecord, FPhoRepository.MDBConnectionProperties, 'messart');
Model.Props[TPhoMessartSQLRecord].ExternalDB.MapField('ID', 'IdMessart');
ServerDB := TSQLRestServerDB.Create(Model, ChangeFileExt(ExeVersion.ProgramFileName, '.db3'), False);
ServerDB.CreateMissingTables;
ServerDB.ServiceDefine(TPhoMessartCommand, [IPhoMessartCommand], sicShared);
ServerHTTP := TSQLHttpServer.Create('7979', [ServerDB], '+', useHttpApiRegisteringURI);
ServerHTTP.AccessControlAllowOrigin := '*'; // allow cross-site AJAX queries
end;
Everything is ok and I can start the server. The I execute the following code
var
cmd: IPhoMessartCommand;
MA: TPhoMessart;
macaption: RawUTF8;
I: Integer;
begin
ServerDB.Services.Resolve(IPhoMessartCommand, cmd);
if Assigned(cmd) then
begin
MA := TPhoMessart.Create;
try
for I := 1 to 10 do begin
UInt32ToUtf8(I, macaption);
MA.Name := 'messart #'+macaption;
if (cmd.Add(MA) <> cqrsSuccess) then
raise Exception.CreateFmt('Invalid data: %s',[cmd.GetLastErrorInfo]);
end;
//here nothing is actually written to the database
if (cmd.Commit <> cqrsSuccess) then
raise Exception.CreateFmt('Commit error: %s',[cmd.GetLastErrorInfo]);
// here everything has been written to the database
finally
MA.Free;
end;
end;
end;
And I get an access violation when accessing the statement "cmd.Add(MA)". This violation happens in the unit mORMotDDD in the statement "if fPropsMapping.MappingVersion<>fPropsMappingVersion then" in the following code:
// unit mORMotDDD
procedure TDDDRepositoryRestFactory.AggregateToTable(aAggregate: TObject; aID: TID; aDest: TSQLRecord);
var i: integer;
begin
if fPropsMapping.MappingVersion<>fPropsMappingVersion then
ComputeMapping;
if aDest=nil then
raise EDDDRepository.CreateUTF8(self,'%.AggregateToTable(%,%,%=nil)',
[self,aAggregate,aID,fTable]);
aDest.ClearProperties;
aDest.IDValue := aID;
if aAggregate<>nil then
for i := 0 to high(fAggregateProp) do
AggregatePropToTable(aAggregate,fAggregateProp[i],aDest,fAggregateToTable[i]);
end;
Do you have an idea what is happening here? :-)
Offline
How did you initialize the factory?
Did you override the constructor as expected?
Yes, the overriden constructor of the factory looks like
constructor TPhoMessartCommandFactory.Create(ARest: TSQLRest; AOwner: TDDDRepositoryRestManager = nil);
begin
inherited Create(IPhoMessartCommand, TPhoMessartCommand, TPhoMessart, aRest, TPhoMessartSQLRecord, aOwner);
AddFilterOrValidate(['*'], TSynFilterTrim.Create);
end;
But, honestly speaking, I don't know where and whether I use to use right. Can you advise me a bit?
Should I create the constructor in the following initialization code? If I create the factory, I get the access violation error anyway :-(
var
PhoMessartCommandFactory: TPhoMessartCommandFactory;
begin
FPhoRepository := TPhoDBRepository.Create;
Model := TSQLModel.Create([TPhoMessartSQLRecord], ROOT_NAME);
VirtualTableExternalRegister(Model, TPhoMessartSQLRecord, FPhoRepository.MDBConnectionProperties, 'messart');
Model.Props[TPhoMessartSQLRecord].ExternalDB.MapField('ID', 'IdMessart');
ServerDB := TSQLRestServerDB.Create(Model, ChangeFileExt(ExeVersion.ProgramFileName, '.db3'), False);
ServerDB.CreateMissingTables;
ServerDB.ServiceDefine(TPhoMessartCommand, [IPhoMessartCommand], sicShared);
// FACTORY
PhoMessartCommandFactory := TPhoMessartCommandFactory.Create(ServerDB);
ServerHTTP := TSQLHttpServer.Create('7979', [ServerDB], '+', useHttpApiRegisteringURI);
ServerHTTP.AccessControlAllowOrigin := '*'; // allow cross-site AJAX queries
end;
Offline
Is fPropsMapping nil ?
fPropsMapping should have been set by TPhoMessartCommandFactory.Create
Why is it not the case?
No, fPropsMapping is not nil, the variable Factory is nil in the following code:
function TDDDRepositoryRestCommand.ORMAdd(aAggregate: TObject): TCQRSResult;
begin
if CqrsBeginMethod(qaCommandDirect,result) then begin
// Factory is nil
Factory.AggregateToTable(aAggregate,0,fCurrentORMInstance);
ORMPrepareForCommit(soInsert,aAggregate);
end;
end;
Offline
ab wrote:Is fPropsMapping nil ?
fPropsMapping should have been set by TPhoMessartCommandFactory.Create
Why is it not the case?No, fPropsMapping is not nil, the variable Factory is nil in the following code:
function TDDDRepositoryRestCommand.ORMAdd(aAggregate: TObject): TCQRSResult; begin if CqrsBeginMethod(qaCommandDirect,result) then begin // Factory is nil Factory.AggregateToTable(aAggregate,0,fCurrentORMInstance); ORMPrepareForCommit(soInsert,aAggregate); end; end;
Ab, how do I right set this factory?
Offline
Dear AB, could you help me with this factory? Because I am stuck with that! :-(
Offline
A factory is indeed mandatory to use the CQRS interfaces, so the following is NOT correct:
ServerDB.ServiceDefine(TPhoMessartCommand, [IPhoMessartCommand], sicShared);
You have to register the factory itself, as an IoC/DI resolver, e.g. via:
ServerDB.ServiceContainer.InjectResolver([TPhoMessartCommandFactory.Create(ServerDB)],true);
Here ...],true) would let the factory be owned by ServerDB.Services so you would not need to manually release its instance.
But you may also write:
PhoMessartCommandFactory := TPhoMessartCommandFactory.Create(ServerDB);
ServerDB.ServiceContainer.InjectResolver([PhoMessartCommandFactory ]);
...
PhoMessartCommandFactory .Free;
Note that you need the latest version of the framework source code to have access to the new TSQLRest.ServiceContainer function.
See http://synopse.info/fossil/info/cd20687e28
Otherwise, you need to have other services defined, and use TSQLRest.Services property.
I've also updated the documentation to make this point clearer:
You would probably want to use those CQRS interfaces, via usual IoC, at TSQLRest level, just like any Client-Server services via interfaces:
var cmd: IDomUserCommand;
...
aServer.Services.Resolve(IDomUserCommand,cmd);
or, for a Query:
var qry: IDomUserQuery;
...
aServer.Services.Resolve(IDomUserQuery,qry);
In order to be able to get a IDomUserCommand or IDomUserQuery instance from aServer.Services.Resolve(), you would need to register the TInfraRepoUserFactory first:
aServer.ServiceContainer.InjectResolver([TInfraRepoUserFactory.Create(aServer)],true);
or if you want to maintain the factory instance life-time (e.g. to share it with other interface resolvers):
var factory: TInfraRepoUserFactory;
...
factory := TInfraRepoUserFactory.Create(aServer);
try
aServer.ServiceContainer.InjectResolver([factory]);
...
finally
factory.Free;
end;
This single TInfraRepoUserFactory would allow to implement both IDomUserCommand and IDomUserQuery contracts.
Of course, having the ability to let aServer own the factory, via the InjectResolver([...],true) parameter, sounds easier to work with.
Offline
Hi Arnaud,
thank you for your detailed answer!
I have tried it out, but I am getting an error. It's an access violation in the class TDDDRepositoryRestQuery. The field value Factory is nil. See the code below.
function TDDDRepositoryRestQuery.ORMSelectOne(ORMWhereClauseFmt: PUTF8Char; const Bounds: array of const; ForcedBadRequest: boolean): TCQRSResult;
begin
CqrsBeginMethod(qaSelect,result);
if ForcedBadRequest then
CqrsSetResult(cqrsBadRequest) else
CqrsSetResultSuccessIf(Factory.Rest.Retrieve(ORMWhereClauseFmt,[],Bounds,
fCurrentORMInstance),cqrsNotFound);
end;
My server is initialized as follows:
procedure InitHTTPService;
begin
Model := CreateModel;
uPhoDataLayer.InitializePhoModel(Model, DatabaseConnectionProperties);
ServerDB := TSQLRestServerDB.Create(Model, ChangeFileExt(ExeVersion.ProgramFileName, '.db3'), False);
ServerDB.CreateMissingTables;
ServerDB.ServiceRegister(TPhoMessartCommand, [TypeInfo(IPhoMessartCommand)], sicShared);
ServerDB.ServiceContainer.InjectResolver([TPhoMessartCommandFactory.Create(ServerDB)], True);
ServerHTTP := TSQLHttpServer.Create('7979', [ServerDB], '+', useHttpApiRegisteringURI);
ServerHTTP.AccessControlAllowOrigin := '*'; // allow cross-site AJAX queries
end;
// constructor TPhoMessartCommandFactory looks like
constructor TPhoMessartCommandFactory.Create(ARest: TSQLRest; AOwner: TDDDRepositoryRestManager = nil);
begin
inherited Create(IPhoMessartCommand, TPhoMessartCommand, TMessart, aRest, TPhoMessart, aOwner);
AddFilterOrValidate(['*'], TSynFilterTrim.Create);
AddFilterOrValidate(['Name'], TSynValidateNonVoidText.Create);
end;
On the client side I have the following code:
var
Model: TSQLModel;
Client: TSQLHttpClient;
MessartCommand: IPhoMessartCommand;
begin
Model := TSQLModel.Create([]);
Client := TSQLHttpClient.Create('localhost','7979', lModel);
Client.ServiceDefine([IPhoMessartCommand], sicShared);
Client.Services.Resolve(IPhoMessartCommand, MessartCommand);
if not Assigned(MessartCommand) then
raise Exception.Create('Can''t get PhoMessartCommand')
else
MessartCommand.SelectByName('somemessart');
end;
and if I call MessartCommand.SelectByName('somemessart'), I am getting this access violation because of Factory is nil.
Is it a bug or am I doing something wrong?
Thx for your help!
Offline
Please use the debugger on the server side to find out why the factory is not resolved in your case.
You have to explicitly resolve the Factory reference, before using it.
You did not show this in your code.
Offline
Please use the debugger on the server side to find out why the factory is not resolved in your case.
I have debugged this code:
ServerDB.Services.Resolve(IPhoMessartCommand, MessartCommand);
Everything seems to be ok. And I am getting true if the code below is executed:
function TInterfaceResolverInjected.Resolve(aInterface: PTypeInfo; out Obj): boolean;
begin
if self=nil then
result := false else
result := TryResolve(aInterface,Obj);
end;
function TInterfaceResolverInjected.Resolve(const aGUID: TGUID; out Obj): boolean;
var known: TInterfaceFactory;
begin
if self=nil then
result := false else begin
known := TInterfaceFactory.Get(aGUID);
if known<>nil then
result := Resolve(known.fInterfaceTypeInfo,Obj) else
result := false;
end;
end;
But I don't understand where the instance of the class TDDDRepositoryRestQuery is created.
The debugger doesn't call this code:
constructor TDDDRepositoryRestQuery.Create(aFactory: TDDDRepositoryRestFactory);
begin
fFactory := aFactory;
fCurrentORMInstance := fFactory.Table.Create;
end;
Do you have any idea?
Offline
Please use the debugger on the server side to find out why the factory is not resolved in your case.
You have to explicitly resolve the Factory reference, before using it.
You did not show this in your code.
Also on the client side?
How and where can I do this? Code you give me a short code example of it?
Last edited by cypriotcalm (2015-06-17 07:38:03)
Offline
The client side has nothing to do with the factory.
The factory is an implementation detail of TDDDRepositoryRestQuery, which stay on the server side.
I'll look further and come back.
Offline
The client side has nothing to do with the factory.
The factory is an implementation detail of TDDDRepositoryRestQuery, which stay on the server side.
I have thought the same
Is the reference to the factory not done with the followiing code?
ServerDB.ServiceContainer.InjectResolver([TPhoMessartCommandFactory.Create(ServerDB)], True);
I'll look further and come back.
Ok, thx, I'll wait
Offline
This automatic injection is tested by TInfraRepoUserFactory.RegressionTests, as defined in dddInfraRepoUser.
Up to now, only direct call from the server side was made.
I've enhanced the tests to use a client/server connection (as you do).
There was indeed an issue, which has been fixed by http://synopse.info/fossil/info/ff269c01b1
But IMHO your code has still a problem: you define your IPhoMessartCommand service as sicShared.
A CQRS service is NOT meant to be defined as sicShared.
A CQRS service should have the client life-time, so should be defined as sicClientDriven.
Offline
This automatic injection is tested by TInfraRepoUserFactory.RegressionTests, as defined in dddInfraRepoUser.
Up to now, only direct call from the server side was made.
I've enhanced the tests to use a client/server connection (as you do).
There was indeed an issue, which has been fixed by http://synopse.info/fossil/info/ff269c01b1
I have just downloaded the nightly build and tried it out. I am still getting the same error. The "factory" field is nil when the framework is calling the function TDDDRepositoryRestQuery.ORMSelectOne.
By debugging I have noticed that the instance of PhoMessartCommand is resolved but the "factory" field is not set. See the code below:
function TDDDRepositoryRestQuery.ORMSelectOne(ORMWhereClauseFmt: PUTF8Char;
const Bounds: array of const; ForcedBadRequest: boolean): TCQRSResult;
begin
CqrsBeginMethod(qaSelect,result);
if ForcedBadRequest then
CqrsSetResult(cqrsBadRequest) else
CqrsSetResultSuccessIf(Factory.Rest.Retrieve(ORMWhereClauseFmt,[],Bounds,
fCurrentORMInstance),cqrsNotFound);
end;
But IMHO your code has still a problem: you define your IPhoMessartCommand service as sicShared.
A CQRS service is NOT meant to be defined as sicShared.
A CQRS service should have the client life-time, so should be defined as sicClientDriven.
Thx for the advice! I have changed it.
Last edited by cypriotcalm (2015-06-18 06:22:59)
Offline
There should still be something wrong in your code.
How is your TPhoMessartCommand class defined?
Please check the TInfraRepoUserFactory.RegressionTests, as defined in dddInfraRepoUser.
TInfraRepoUser = class(TDDDRepositoryRestCommand,IDomUserCommand,IDomUserQuery)
...
RestServer := TSQLRestServerFullMemory.CreateWithOwnModel([TSQLRecordUser]);
try // then try from a client-server process
RestServer.ServiceContainer.InjectResolver([TInfraRepoUserFactory.Create(RestServer)],true);
RestServer.ServiceDefine(TInfraRepoUser,[IDomUserCommand,IDomUserQuery],sicClientDriven);
test.Check(RestServer.ExportServer);
RestClient := TSQLRestClientURIDll.Create(TSQLModel.Create(RestServer.Model),@URIRequest);
try
RestClient.Model.Owner := RestClient;
RestClient.ServiceDefine([IDomUserCommand],sicClientDriven);
TestOne(RestServer);
RestServer.DropDatabase;
USEFASTMM4ALLOC := true; // for slightly faster process
TestOne(RestClient);
finally
RestClient.Free;
end;
finally
RestServer.Free;
end;
Using the debugger, you would be able to find out how it works, even on client/server.
Offline
There should still be something wrong in your code.
How is your TPhoMessartCommand class defined?
Arnaud, you are right! This time it's my mistake
I have defined this class in the same way your documentation requires it.
The point is that I have to inject the resolver before defining the service when initializing the server instance. Otherwise, the "factory" field is not set and I get an violation error.
But on the client side I have to do it vice versa, i.e. I have to define first the service and then to resolve the interface.
// server side
(...)
ServerDB.ServiceContainer.InjectResolver([TPhoMessartCommandFactory.Create(ServerDB)], True);
ServerDB.ServiceDefine(TPhoMessartCommand, [IPhoMessartCommand], sicClientDriven);
(...)
// client side
(...)
Model := TSQLModel.Create([]);
Client := TSQLHttpClient.Create('localhost','7979', FModel);
Client.ServiceDefine([IPhoMessartCommand], sicClientDriven);
Client.Services.Resolve(IPhoMessartCommand, MessartCommand);
(...)
Using the debugger, you would be able to find out how it works, even on client/server.
I am using the debugger all the time! ;-)
But now it works and I continue to discover the amazing framework! A big thank you for your help and fast fixing the issues!
Last edited by cypriotcalm (2015-06-18 06:52:37)
Offline